这篇文章主要介绍一下this的四种绑定规则。
本文内容源自:
你不知道的javascript
在讲this的绑定规则之前我们先总结一下this:
- this 既不指向函数自身也不指向函数的词法作用域
- this 实际上是在函数被调用时发生的绑定, 它指向什么完全取决于函数在哪里被调用。
既然this的绑定取决于在哪被调用,那么首先我们需要先找到this的调用位置,查看函数调用栈的倒数第二个元素,这里就是this的调用位置。找到调用位置之后就需要判断this使用了哪种绑定规则,下面就介绍一下this的四种绑定规则。
默认绑定
默认绑定是指在无法应用其他绑定时的默认规则。
首先看以下代码:1
2
3
4
5function foo() {
console.log( this.a );
}
var a = 2;
foo(); //2
当foo()
函数被调用时,this.a被解析成了全局变量a,这时候应用了this的默认绑定,为什么呢?因为foo()
是直接使用不带任何修饰的函数引用进行调用的, 因此只能使用
默认绑定, 无法应用其他规则。
但是如果是在严格模式下:1
2
3
4
5
6function foo() {
;
console.log( this.a );
}
var a = 2;
foo(); // TypeError: this is undefined
之所以会有typeError,是因为在严格模式下全局对象将无法使用默认绑定, 因此 this 会绑定到 undefined;
这里有一个微妙但是非常重要的细节, 虽然 this 的绑定规则完全取决于调用位置, 但是只有 foo() 运行在非严格模式下时(在非严格模式下定义), 默认绑定才能绑定到全局对象; 跟foo()的调用位置是不是在严格模式下无关:1
2
3
4
5
6
7
8function foo() {
console.log( this.a );
}
var a = 2;
(function(){
;
foo(); // 2
})();
注:不要同时使用严格模式或者非严格模式,要统一代码风格
隐式绑定
当函数的调用位置有上下文对象的时候会应用隐式绑定。1
2
3
4
5
6
7
8
9function foo(){
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
}
obj.foo();//2
当foo()被调用时,obj成为了foo()的执行上下文对象,于是,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。
注:对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
隐式绑定会有隐式丢失的问题:1
2
3
4
5
6
7
8
9
10
11function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"
bar是obj.foo的一个引用,但实际指向的是foo函数本身,因此bar没有执行上下文,所以启用了默认绑定。
显式绑定
显示绑定比较简单,就是我们平时使用的call(…)和apply(…),这两个函数可以把对象绑定到执行函数的this上,所以称为显式绑定。1
2
3
4
5
6
7function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2
显式绑定仍然无法解决我们之前提出的丢失绑定问题。
硬绑定
硬绑定是显式绑定的一个变种,但是可以解决丢失绑定的问题1
2
3
4
5
6
7
8
9
10
11
12
13function foo() {
console.log( this.a );
}
var obj = {
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定的 bar 不可能再修改它的 this
bar.call( window ); // 2
bar的绑定总是在执行的时候绑定到obj上面,这是一种显式的强制绑定。在ES5中的bind()方法就是一种硬绑定。
new绑定
js中的new操作符实际上并不是“构造函数”,而是一种对函数的“构造函数调用”。
使用 new 来调用函数, 或者说发生构造函数调用时, 会自动执行下面的操作。
- 创建(或者说构造) 一个全新的对象。
- 这个新对象会被执行 [[ 原型 ]] 连接。
- 这个新对象会绑定到函数调用的 this。
- 如果函数没有返回其他对象, 那么 new 表达式中的函数调用会自动返回这个新对象。
1
2
3
4
5function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
使用 new 来调用 foo(..) 时, 我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this上。 new 是最后一种可以影响函数调用时 this 绑定行为的方法, 我们称之为 new 绑定。
优先级
new绑定>硬绑定>显式绑定>隐式绑定>默认绑定。其中默认绑定在严格模式下会绑定到undefined上,否则绑定到全局对象上