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 绑定 > 显式绑定 > 隐式绑定 > 默认绑定 的优先级来决定哪条规则会生效。