一、this的绑定规则
默认绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// 1 独立函数被调用-this指向window
function foo() {
console.log(this)
}
foo()
// 2 函数定义在对象中,但是被独立调用-this指向window
var obj = {
bar: function () {
console.log(this)
}
}
var baz = obj.bar
baz()
// 3 高阶函数,函数作为实参被调用-this指向window
var obj2 = {
bar: function () {
console.log(this)
}
}
function test(fn) {
fn()
}
test(obj2.bar)
|
隐式绑定
1
2
3
4
5
6
7
8
9
10
|
function foo() {
console.log(this)
}
var obj = {
bar: foo
}
// 通过对象调用函数-this指向该对象
obj.bar()
|
显式绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function foo(name, age, height) {
console.log(this, name, age, height)
}
// 1 apply调用:第一个参数绑定this,第二个参数传入额外的实参(以数组的形式)
foo.apply("apply", ["mocha", 18, 1.88])
// 2 call调用:第一个参数绑定this,其余传入参数列表作为实参
foo.call("call", "latte", 19, 1.99)
// 3 bind绑定
var obj = { name: "obj" }
// 调用函数时候,总是绑定到对象上,但函数不写入对象中
var bar = foo.bind(obj, "mocha", 18, 1.88)
bar()
|
new 绑定
1
2
3
4
5
6
|
// new绑定-创建一个新的空对象,this指向这个对象
function foo() {
console.log(this)
}
new foo()
|
注意
- 函数在调用的时候,JavaScript会默认给this绑定一个值
- this的绑定和定义的位置(编写的位置)没有关系
- this的绑定和调用方式以及调用的位置有关系
- this是在运行时被绑定的
二、this的绑定优先级
- 默认绑定优先级最低
- 显式绑定高于隐式绑定
- new 绑定高于隐式绑定
- new 绑定高于bing,new不能和call,apply同时使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
function foo() {
console.log(this)
}
// 1、显式绑定高于隐式绑定
var obj = { bar : foo }
obj.bar.apply("apply") // this指向apply
obj.bar.call("call") // this指向call
var bar = foo.bind("bind")
var obj2 = { baz: bar }
obj2.baz() // this指向bind
// 2、new绑定高于隐式绑定
var obj3 = {
foo: function () {
console.log(this)
}
}
new obj3.foo() // this指向新的空对象
// 3、new 绑定高于bind
var bindFn = foo.bind("bind")
new bindFn() // this指向新的空对象
|
三、绑定之外的情况
情况一
在显式绑定中,传入null/undefined,这个显式绑定会被忽略,使用默认规则,但在严格模式下将为原始值
1
2
3
4
5
6
|
function foo() {
console.log(this)
}
foo.apply(null) // this指向window,严格模式下为null
foo.apply(undefined) // this指向window,严格模式下为undefined
|
情况二
间接函数引用,使用默认规则
1
2
3
4
5
6
7
8
9
|
var obj = {
foo: function () {
console.log(this)
}
}
var obj2 = {}
(obj2.foo = obj.foo)() // this指向window
|
四、箭头函数的使用
特性
- 箭头函数不会绑定this,也不会有arguments属性
- 箭头函数不能作为构造函数
编写优化
- 只有一个参数的时候可以省略()
- 函数体只有一行代码时可以省略{},但会将这行代码作为返回值
- 如果默认返回一个对象的时候,必须加上()
1
2
3
4
5
6
7
8
9
10
|
var names = [ "mocha", "latte", "coffee", "tea-milk" ]
names.forEach(item => console.log(item))
var arrFn = () => ({ name: "mocha", age: 18})
var nums = [ 1, 2, 3, 4, 5]
var totalNums = nums.filter(item => item % 2 === 0)
.map(item => item ** 2)
.reduce((preValue, item) => preValue + item)
|
箭头函数中的this用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
function request(url, callbackFn) {
var result = ["aaa", "bbb", "ccc"]
callbackFn(result)
}
var obj = {
names: [],
network: function () {
// es5之前使用变量保存this
// _this = this
request("/names", (res) => {
this.names = [].concat(res)
})
}
}
obj.network()
console.log(obj.names)
|
五、this练习题分析
练习题一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
},
};
function sayName() {
var test = person.sayName;
test(); // window 默认绑定(独立函数调用)
person.sayName(); // person 隐式绑定
person.sayName(); // person 隐式绑定
(b = person.sayName)(); // window 间接函数调用(独立函数调用)
}
sayName();
|
练习题二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
var name = "window";
var person1 = {
name: "person1",
foo1: function () {
console.log(this.name);
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name);
};
},
foo4: function () {
return () => {
console.log(this.name);
};
},
};
var person2 = { name: "person2" };
person1.foo1(); // window (独立函数调用)
person2.foo1.call(person2); // person2 (显示绑定)
person1.foo2(); // window (箭头函数不绑定this,上层作用域是全局)
person1.foo2.call(person2); // window (箭头函数不适用this的绑定规则)
person1.foo3()(); // window (独立函数调用)
person1.foo3.call(person2)(); // window (独立函数调用)
person1.foo3().call(person2); // person2 (最终调用返回函数时,使用的是显示绑定)
person1.foo4()(); // person1 (箭头函数不绑定this,上层作用域是person1)
person1.foo4.call(person2); // person2 (上层作用域被显示地绑定为person2)
person1.foo4().call(person2); // peerson1 (上层找到了person1)
|
练习题三
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
var name = "window";
function Person(name) {
this.name = name;
this.foo1 = function () {
console.log(this.name);
};
this.foo2 = () => console.log(this.name);
this.foo3 = function () {
return function () {
console.log(this.name);
};
};
this.foo4 = function () {
return () => {
console.log(this.name);
};
};
}
var person1 = new Person("person1");
var person2 = new Person("person2");
person1.foo1(); // person1 (隐式绑定)
person1.foo1.call(person2); // person2 (显示绑定)
person1.foo2(); // person1 上层作用域是person1
person1.foo2.call(person2); // person1 call不绑定箭头函数中的this
person1.foo3()(); // window (独立函数调用)
person1.foo3.call(person2)(); // window (独立函数调用)
person1.foo3().call(person2); // person2 (显示绑定)
person1.foo4()(); // person1
person1.foo4.call(person2)(); // person2
person1.foo4().call(person2); // person1
|
练习题四
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
var name = "window";
function Person(name) {
this.name = name;
this.obj = {
name: "obj",
foo1: function () {
return function () {
console.log(this.name);
};
},
foo2: function () {
return () => {
console.log(this.name);
};
},
};
}
var person1 = new Person("person1");
var person2 = new Person("person2");
person1.obj.foo1()(); // window (独立函数调用)
person1.obj.foo1.call(person2)(); // window (独立函数调用)
person1.obj.foo1().call(person2); // person2 (显示绑定)
person1.obj.foo2()(); // obj
person1.obj.foo2.call(person2)(); // person2
person1.obj.foo2().call(person2); // obj
|