一、默认绑定
function foo() {
console.log(this.a);
}
var a = 2;
foo(); // 2
这里调用foo函数打印的a就是全局作用域中定义的a,这就是第一种情况,this的默认绑定,绑定的对象是全局对象。
在严格模式下,默认绑定的是undefined
二、隐式绑定
function foo() {
console.log( this.a );
}
var obj={
a: 2,
foo: foo
};
obj.foo(); // 2
如果你之前有学过面向对象的语言,比如:Java,那你应该会觉得这个很眼熟。当函数调用具有上下文对象时,隐式绑定规则会将this绑定到这个上下文对象上,在这个例子中,this就绑定到了obj这个对象上。
function foo() {
console.log( this.a );
}
var obj2={
a: 42,
foo: foo
};
var obj1={
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
对象属性引用链中只有最后一层会影响调用绑定结果,上面的例子中,可以理解为(obj1.obj2).foo(),这也比较好理解。
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// fn其实引用的是foo
fn(); // <--调用位置!
}
var obj={
a: 2,
foo: foo
};
var a = "oops, global"; // a 是全局对象的属性
doFoo( obj.foo ); // "oops, global"
隐式丢失在上面的例子中我们将obj.foo作为参数传入到doFoo中,然后在doFoo中调用了传入的fn,结果打印的时全局对象中的a,这是因为在参数的传递过程中是赋值的操作,我们是把obj.foo赋值给了fn,这里的fn其实就是foo函数的一个引用,还有一个更直白的例子可以帮助理解:
function foo() {
console.log( this.a );
}
var obj={
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"
这也是在我们平时写项目中设置回调函数时经常会发生的问题,而大多数情况下,我们时将this的值存储下来,比如:
let self = this;
this.$axios({ ... })
.then( function() { self.$Message() } )
又或者使用箭头函数
let self = this;
this.$axios({ ... })
.then((a) => { this.$Message() })
第一种是将函数和self作为一个闭包传入了回调中,第二种是利用箭头函数没有单独的this,在函数中调用会继承函数的this的特性,下面会介绍一种更加常用的方法来绑定this。
三、显示绑定
显示绑定是通过call或者apply方法将this绑定到对应的obj上。
function foo() {
console.log(this.a);
}
var obj={ a:2 };
foo.call(obj); // 2
我们可以看到通过call方法,我们成功的将这次调用的foo的this绑定到了obj上,而apply的作用与call完全一致,唯一的区别在于,call传递参数时是传递的参数列表,apply传递的时参数数组。
var func1 = function(arg1, arg2) {};
func1.call(this, arg1, arg2);
func1.apply(this, [arg1, arg2]);
function foo() {
console.log( this.a );
}
var obj={
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
硬绑定是一种常用的模式,如上面的例子,创建一个包裹函数bar,在其中调用foo.call(obj),之后我们无论在那里调用这个函数,this绑定的都是obj。由于这个方法过于常用,在es5中,提供了内置的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);//23
console.log( b ); // 5
四、new绑定
学过面向对象语言的对这个词应该不陌生,但与面向对象语言中new不同的是,js中的是以new的方式去执行一个函数,而不是调用构造函数,在JS中目前并没有实际的类,具体的过程如下:
1. 创建或者构造一个全新的对象
2. 这个新对象会执行[[原型]]连接
3. 这个新对象会绑定到函数调用的this
4. 如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个对象
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
五、优先级
new绑定>硬绑定>隐式绑定>默认绑定