this 是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。关于 this,经常会有一些误解:比如会把 this 理解成指向函数自身,又或者认为 this 指向函数的作用域。
其实,this 是在函数调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。因此,函数的不同使用场合,this 也有不同的值。那么我们该如何判断 this 呢?
可以按照下面的顺序来进行判断:
1.函数是否在 new 中调用(new绑定)?如果是的话,this 绑定的是新创建的对象。
首先,回想下 new 运算符会执行哪些操作:
a.创建(或者说构造)一个全新的对象
b.这个新对象会被执行[[Prototype]]连接
c.这个新对象会绑定到函数调用的 this
d.如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象
function foo() {
this.num = 10;
return '';
}
function foo2() {
this.num = 10;
return 1;
}
function foo3() {
this.num = 10;
return [];
}
function foo4() {
this.num = 10;
return {};
}
function foo5() {
this.num = 10;
return function(){};
}
function foo6() {
this.num = 10;
return null;
}
console.log(new foo().num, new foo2().num, new foo3().num, new foo4().num, new foo5().num, new foo6().num); // 10 10 undefined undefined undefined 10
2.函数是否通过 call,apply(显示绑定)或者bind(硬绑定)调用?如果是的话,this 绑定的是指定的对象。
var obj = {
num: 20
};
function foo() {
this.num = 10;
console.log(this);
}
foo(); // Window 对象
foo.call(obj); // {num: 10}
foo.apply(obj); // {num: 10}
foo.bind(obj)(); // {num: 10}
call, apply, bind 都可以改变函数的 this 指向,区别是:
1.call, apply 将立即执行该函数,bind不执行函数,只返回一个可供执行的函数
2.调用方式不一样:
call(this, arg1, arg2, arg3);
apply(this, [arg1, arg2, arg3]);
bind(this, arg1, arg2, arg3)();
3.函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象。
注:对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
4.如果以上都不是的话,使用默认绑定。
如果在严格模式下,就绑定到 undefined,否则绑定到全局对象。
对于默认绑定来说,决定 this 绑定对象的并不是调用位置是否处于严格模式,而是函数体是否处于严格模式。如果函数体处于严格模式,this 会被绑定到 undefined,否则 this 会被绑定到全局对象。
关于全局对象:
浏览器环境下指向的是 window,node 环境下指向的是 module.exports。
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
// 函数体是严格模式,则不能将全局对象用于默认绑定,此时 this 会绑定到 undefined。
function foo() {
"use strict";
console.log( this.a );
}
var a = 2;
foo(); // Uncaught TypeError: Cannot read property 'a' of undefined
// 函数体是非严格模式,this 默认绑定到全局对象 Window
function foo() {
console.log( this.a );
}
var a = 2;
(function(){
"use strict";
foo(); // 2
})();
5.箭头函数
箭头函数的 this 在声明的时候就决定了。
箭头函数本身是没有 this 和 arguments 的,在箭头函数中引用 this 实际上调用的是定义是的上一层作用域的 this。这里强调一下是上一层作用域,因此对象是不能形成独立的作用域的。
var obj2 = {
num: 10
};
var obj = {
fn: () => {
console.log(this);
},
fn2: function() {
console.log(this);
}
}
obj.fn.call(obj2); // Window对象,因为箭头函数在定义的时候 this 就固定了,用 call 不能改变其指向
obj.fn2.call(obj2); // {num: 10},普通函数,this 指向 obj2