this
是一个很特别的关键字,被自动定义在所有函数的作用域中。this
实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用(这句话不适用于箭头函数,想了解箭头函数的可以前往ES6—箭头函数)。
要判断一个运行中函数的 this
绑定,就需要找到这个函数的直接调用位置。找到之后就可以应用下面这四条规则来判断 this
的绑定对象:
- 函数是否在
new
中调用(new
绑定)?如果是的话this
绑定的是新创建的对象。
var obj = new fun();
- 函数是否通过
call
、apply
、bind
(显式绑定)或者硬绑定调用?如果是的话,this
绑定的是指定的对象。
fun.apply(obj);
fun.call(obj);
fun.bind(obj)();
- 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象。
obj.fun();
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到
undefined
,否则绑定到全局对象。
fun();
上面讲述的四种情况分别 this
的四种绑定规则:默认绑定、隐式绑定、显式绑定、new绑定。
默认绑定:是没有应用其他绑定规则时使用的规则,通常是独立函数调用。这时 this
在非严格模式下指向的是全局对象 window
,在严格模式下指向 undefined
。
function fun(){
console.log(this === window);
}
fun(); //true
function fun(){
'use strict';
console.log(this === undefined);
}
fun(); //true
注意:对于默认绑定来说,决定 this
绑定对象的并不是调用位置是否处于严格模式,而是函数体是否处于严格模式。如果函数体处于严格模式,this
会被绑定到 undefined
,否则 this
会被绑定到全局对象。
function fun(){
console.log(this === window);
}
(function(){
'use strict';
fun();
})(); //true
隐式绑定:函数的调用是在某个对象上触发的,即调用位置上存在上下文对象。这时 this
会指向这个上下文对象。
var obj = {
fun : function(){
console.log(this === obj);
}
};
obj.fun(); //true
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
function fun(){
console.log(this === obj1);
}
var obj1 = {
fun : fun
}
var obj2 = {
obj1 : obj1
}
var obj3 = {
obj2 : obj2
}
obj3.obj2.obj1.fun(); //true
显式绑定:使用call()
、apply()
、bind()
把指定对象绑定到 this
,这种方式称为显式绑定。想了解这三个方法的可以前往apply()、call()与bind()的用法与区别。
var obj = {};
function fun(){
console.log(this === obj);
}
fun(); //false
fun.apply(obj); //true
fun.call(obj); //true
fun.bind(obj)(); //true
如果你传入了一个原始值(字符串类型、布尔类型或者数字类型)来当作 this 的绑定对象,这个原始值会被转换成它的对象形式(也就是 new String(..)
、new Boolean(..)
或者new Number(..)
)。这通常被称为“装箱”。
var num = 123;
function fun(){
console.log(this instanceof Number);
console.log(this === num);
console.log(typeof this);
}
fun.apply(num); //true false object
如果你把 null
或者 undefined
作为 this
的绑定对象传入 call
、apply
或者 bind
,这些值在调用时会被忽略,实际应用的是默认绑定规则。也就是在非严格模式下 this
会绑定全局对象,在严格模式下指向 undefined
。
function fun(){
console.log(this === window);
}
fun.apply(null); //true
fun.apply(undefined); //true
硬绑定是显式绑定的一个变种,是一种显式的强制绑定。
var obj1 = {};
var obj2 = {};
function fun1(){
console.log(this === obj1);
}
function fun2(){
fun1.apply(obj1);
}
fun2.apply(obj2); //true
这里我们创建了函数 fun2()
,并在它的内部手动调用了 fun1.apply(obj1)
,因此强制把 fun1
的 this
绑定到了 obj1
。无论之后如何调用函数 fun2
,它总会手动在 obj1
上调用 fun1
。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定。
new绑定:使用 new
来调用函数,会将 this
绑定到新创建的对象上面。
function fun(){
this.age = 23;
}
var obj = new fun();
console.log(window.age); //undefined
console.log(obj.age); //23
使用 new
来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
- 创建一个新对象;
- 将构造函数的作用域赋给新对象(因此
this
就指向了这个新对象); - 执行构造函数中的代码(为这个新对象添加属性);
- 返回新对象。
如果某个函数的调用位置应用了多条规则,那么将按照:new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定 的优先级来决定哪条规则会生效。