前面的话
关于this指向问题,大家都不陌生,最近做题一些面试题,正好碰到,将其绑定的方式总结一下。
默认绑定
- 独立函数调用: 在普通函数中调用,this指向的全局对象window。
- 严格模式下: 比较特殊,运行在严格模式不能将全局对象绑定到window,而是绑定到undefined。只有函数运行在非严格模式下,才会绑定到window。而函数在严格模式下调用,则不会影响默认绑定。
// 默认绑定
function foo() {
console.log(this.a);
}
var a = 2;
foo();
// 运行在严格模式下:
function foo() {
"use strict";
console.log(this.a);
}
var a = 2;
foo();// Uncaught TypeError: Cannot read property 'a' of undefined
// 在严格模式下调用
function foo() {
console.log(this.a);
}
var a = 2;
(function(){
"use strict";
foo();
})()
隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数的this绑定到这个上下文对象。
function foo() {
console.log(this.a);
}
obj = {
a : 1,
foo: foo
}
// 这里this绑定给了obj
obj.foo(); // 1
这里foo被obj添加到其属性上,最终被obj包含。
注意,只有对象属性引用链的最后一层才会影响调用点:
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
这里obj2这个对象被添加到obj1对象上作为属性,obj1.obj2.foo()其最终调用者是obj2,this会指向obj2,所返回42.
隐式丢失
被隐式绑定的函数特定情况下回丢失绑定对象,应用为默认绑定,把this绑定到全局对象或者undefined上。
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
}
var bar = obj.foo;
var a = 'xiaoqi';
bar(); // xiaoqi
这里var bar = obj.foo;相当于var bar = function foo() {this.a};以默认绑定的情况一样。
function foo() {
console.log(this.a);
}
function doFoo(fn) {
fn();
}
var obj = {
a:2 ,
foo:foo
};
var a = 'xiaoqi';
doFoo(obj.foo); // xiaoqi
这里obj.foo只是foo的一个引用,并没有真正的调用,所以最终this绑定的函数全局对象。
显式绑定
通过call()或者apply()方法。第一个参数是对象,在调用函数时将这个对象绑定到this。因为直接指定了this的绑定对象,称之为显示绑定。
function foo() {
console.log(this.a);
}
var obj = {
a:2
}
foo.call(obj);//2
这里调用foo时强制把foo的this绑定到obj。
硬性绑定
未解决显示绑定无法解决丢失绑定的问题。
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// `bar`将`foo`的`this`硬绑定到`obj`
// 所以它不可以被覆盖
bar.call( window ); // 2
创建一个函数bar(),在其内部手动调用foo.call(obj),由此强调this绑定到obj.无论以后怎么调用函数bar,因为它总是手动使用obj调用foo。this都无法被改变,所有叫做硬绑定。
ES5内置的Function.prototype.bind,bind会返回一个硬绑定的新函数。
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
var obj = {
a: 2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5
这里的bind会改变this的指向,同时返回一个新函数,这一点与call和apply不同。
new绑定
这个就比较简单了,构造函数中this指向实例化对象。
function foo(a) {
this.a = a;
}
var bar = new foo(2); // bar和foo(..)调用中的this进行绑定
console.log( bar.a ); // 2
箭头函数绑定
记住:箭头函数中的this始终指向其父级作用域中的this。换句话说,箭头函数会捕获其所在的上下文的this值,作为自己的this值。在箭头函数中调用 this 时,仅仅是简单的沿着作用域链向上寻找,找到最近的一个 this 拿来使用,它与调用时的上下文无关。
var obj = {
a: 10,
b: () => {
console.log(this.a);// undefined
console.log(this);// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
},
c: function() {
console.log(this.a);// 10
console.log(this);// {a: 10, b: ƒ, c: ƒ, d: ƒ}
},
d: function () {
return () => {
console.log(this.a);// 10
}
}
}
obj.b();
obj.c();
obj.d()()
上例中: obj.b()中的this会继承父级上下文中的this值,也就是window。
obj.c()的this指向即为调用者obj,obj.d()()的this也是继承父级上下文中的this, 即d的this指向为obj.