this是什么
this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。学习 this 的第一步是明白 this 既不指向函数自身也不指向函数的词法作用域。this 实际上是在函数调用时发生的绑定,它只想什么完全取决于函数在哪里被调用。
绑定规则
找到函数的调用位置后,判断需要应用下面四条规则中的那一条。首先会解释这四条规则,然后解释多条规则可用湿他们的优先级如何排列
默认绑定
首先是最常用的函数调用类型:独立函数调用。可以把这条规则看做是无法应用其他规则时的默认规则
1 | function foo() { |
隐式绑定
隐式绑定需要考虑调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。
1 | function foo() { |
调用位置会使用 obj 上下文来引用函数,因此可以说函数被调用时 obj 对象“拥有”或者“包含”函数引用。当 foo() 被调用时,它的前面确实加上了对 obj 的引用。当函数引用有上下文对象时,隐式绑定会把函数调用中的 this 绑定到这个上下文对象。因为调用 foo() 时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的。
对象属性引用链中只有上一层或者最后一层在调用位置中起作用,举例来说
1 | function foo() { |
隐式丢失
一个最常见的 this 绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是他会应用默认绑定,从而把 this 绑定到全局对象或者 undefined 上,取决于是否是严格模式。
思考以下代码:
1 | function foo() { |
虽然 bar 是 obj.foo 的一个引用,但是实际上它引用的是 foo 函数本身,因此此刻的 bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定
此外,回调函数有可能会丢失 this 绑定,可以通过固定 this 来解决这个问题
显式绑定
使用 call() 和 apply() 方法可以直接指定 this 的绑定对象,因此称为显式绑定
1 | function foo() { |
通过 foo.call(…),可以在调用 foo 时将它的 this 强制绑定到 obj 上。
从 this 绑定的角度来说,call() 和 apply() 是一样的,区别在于 call() 的参数是依次传入的,apply() 的参数为在数组中一次性传入
new绑定
使用 new 来调用函数,会自动执行以下操作
- 创建一个全新的对象。
- 这个新对象会被执行 Prototype 指向。
- 这个新对象会绑定到函数调用的 this。
- 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。使用 new 调用 foo 时,会构造一个新对象并把它绑定到 foo() 调用中的 this 上。
1
2
3
4
5
6function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a);// 2
优先级
可以按照下面顺序来判断
从先到后分别为
- var bar = new foo() new 绑定
- var bar = foo.call(obj2) call()、apply() 显式绑定
- var bar = obj1.foo 在上下文对象中调用(隐式绑定)
- var bar = foo() 默认绑定