乐趣区

关于javascript:踩碎js中的this

一、前言

this 是前端开发人员绕不过来的一个点,而 this 的指向有总是让咱们很头疼,明天就让咱们一起来彻底踩碎 this 相干问题吧。

二、this 的作用

很多人会说 this 这么厌恶,为什么咱们须要它呢,其实 this 在代码外面是起到了十分大的作用的,简略来说,this 提供了一种更优雅的形式来隐式“传递”一个对象援用,因而就能够将 API 设计得更加简洁并且易于复用。随着你的应用模式越来越简单,显示传递上下文对象会让代码变得越来越凌乱,应用 this 则不会这样。

三、this 到底指向谁

总体来说,this 有四条绑定规定,当你遇到一个 this, 你只须要看看它合乎那条规定,而后就能找到对应的指向,听起来是不是很简略呢?
四条规定如下:

1. 默认绑定:非严格模式下指向全局对象,严格模式下指向 undefined。

独立函数调用无奈实用其余规定,所有它被认为是默认绑定,比方:

function foo(){console.log(this.a)
}
var a = 1;
foo();   //1

严格模式下,独立函数调用的 this 是指向 undefined 的。须要留神的是,决定 this 绑定对象的并不是函数的调用地位是否处于严格模式中,而是函数体是否处于严格模式中。

2. 隐式绑定:当函数调用地位领有上下文对象的时候,隐式绑定规定会将函数调用中的 this 绑定到这个对象上(留神有隐式失落的状况)

隐式绑定应该是最常见也最令人头疼的了,不过当初咱们晓得了规定,只有找打了上下文对象,就能够轻松的找到 this 到底指向谁啦。

 function foo(){console.log(this.a) 
 } 
 var obj = { 
     a:2, 
     foo:foo 
}; 
 obj.foo();//2

在这个例子中,咱们能够说函数被调用时,obj 对象“领有”或者“蕴含”它,此时 obj 就是函数的上下文,this.a 也就相当于 obj.a。

对象属性援用链中只有上一层或者说最初一层在调用地位中起作用。(能够了解为就近准则)
举例:

 function foo(){console.log(this.a) 
 } 
 var obj2 = { 
     a:2, 
     foo:foo 
};
var obj1 = { 
     a:1, 
     obj2:obj2 
};
obj1.obj2.foo();  //2

联合第 1、2 两条规定,咱们能够简略的总结为,函数执行时首先看函数名后面是否有 ”.”,有的话,”.” 后面是谁,this 就是谁;没有的话 this 就是 window

值得注意的是隐式失落的状况:

隐式失落是指被隐式绑定的函数会失落绑定对象,从而利用默认绑定规定的状况。
最常见的例子就是当你把一个函数传入语言内置的函数的时候,就会产生隐式失落。比方常见的 setTimeout:

function foo(){console.log(this.a) 
} 
var obj = { 
    a:2, 
    foo:foo 
} 
var a = "global" 
setTimeout(obj.foo,100);//global

此时 foo 函数内的 this 并不是执行 obj 的,而是利用了默认规定,指向了全局对象,是不是很意外?这是为什么呢?让咱们来看一看内置 setTimeout 函数实现的伪代码吧:

 function setTimeout(fn,delay){fn(); 
 }

看到没,其实传入的函数其实是独立调用的,所以应用了默认绑定规定。
隐式失落最常见的就是产生在回调函数中,大家要特地留神哦

3. 显示绑定:通过 call(..)和 apply(..)等办法本人显示的指定 this 的指向。

4. new 绑定:this 指向新结构进去的对象。

应用 new 来调用函数,或者说产生结构函数调用时,产生了哪几步?

1. 创立一个新对象

2. 将构造函数的对象赋给新对象(因而 this 就指向了这个新对象)

3. 执行构造函数中的代码(为新对象增加属性)

4. 返回新对象

–《javascript 高级程序设计》

从下面的步骤来看,有一步就是:将构造函数的对象赋给新对象(因而 this 就指向了这个新对象)
举个例子:

function foo(a){this.a = a;} 
var bar = new foo(2); 
console.log(bar.a);//2

应用 new 来调用 foo()时,咱们会结构一个新对象 bar, 并把它绑定到 foo()调用中的 this 上。

四、上述四条规定的优先级

1. 函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象

2. 函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是指定的对象

3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象

4. 如果都不是的话,应用默认绑定,严格模式下,绑定到 undefined, 非严格模式下,绑定到全局对象

每看到一个 this 咱们只须要通过上述步骤一步一步的走下来,便能很轻松的找到它的指向了,是不是很轻松呢?

五、非凡状况

1. 箭头函数:

箭头函数没有本人的 this,箭头函数的 this 不是调用的时候决定的,而是在定义的时候处在的对象就是它的 this。
换句话说,箭头函数的 this 看外层的是否有函数,如果有,外层函数的 this 就是外部箭头函数的 this,如果没有,则 this 是 window

2. 间接援用:

 function foo(){console.log(this.a) 
 } 
 var a = 2; 
 var o = 
 {
     a:3,
     foo:foo
 }; 
 var p = {a:4}; 
 o.foo();//3 
 (p.foo = o.foo)();//2

赋值表达式 p.foo=o.foo 的返回值是指标函数的援用。

六、this 总结

1.this 既不指向本身,也不指向函数的词法作用域。

2.this 实际上是在函数被调用时产生的绑定,它指向什么齐全取决于函数在哪里调用
因为确定 this 的指向是在执行上下文的创立阶段实现的,而执行上下文的创立阶段位于函数被调用但未执行任何外部函数的时候,也就是说在函数调用的时候才决定了 this 的指向 — 箭头函数是例外

七、参考

《你不晓得的 JavaScript》(上卷)
《javascript 高级程序设计》

退出移动版