JavaScript难点系列(二):this

深入了解js这门语言后,才发现它有着诸多众所周知的难点(例如:闭包、原型链、内存空间等)。有的是因为js的设计缺陷导致的,而有的则是js的优点。不管如何,总需要去学会它们,在学习过程中我觉得只看别人的文章并不能做到深刻理解,所以我决定写这一系列的文章来记录我所学习到的知识点,也方便自己以后回顾,如有写错的地方欢迎指正。
废话不多说,马上进入正题!

一、默认绑定

在看过很多种的this解读思路后,我觉得《你不知道的JavaScript(上卷)》中对this的讲解比较有操作性,即有个很明确的标准让你去判断。

默认绑定指的是在没有new绑定和显示绑定(后面会提到)的前提下解析器会运行的绑定逻辑,它的思路是:this是由调用者提供的,每个函数的this取决于函数的调用位置(也就是调用方法)。

具体的判断标准是:如果函数被一个对象所拥有,那么该函数在调用时内部的this指向该对象。如果函数是独立调用(直接不带任何修饰的函数引用进行调用),那么函数内部的this指向undefined(严格模式下)。

// 独立调用
function foo() {
    console.log(this.a)
}
var a = 2
foo()  // undefined

独立调用中有些情况属于比较隐蔽且容易让人混淆的:

// 隐式丢失(也是属于独立调用的情况)
function foo() {
    console.log(this.a)
}
var obj = {
    a: 2,
    foo: foo
}
var bar = obj.foo  // bar引用的是foo函数本身,即相当于bar=foo
var a = "global"
bar()  // undefined 
// 这里和上面的foo()独立调用同理
// 回调中的独立调用
var a = 20;
function foo () {
  console.log(this.a)
}
var obj = {
  a: 2,
  foo: foo
}
function doFoo (fn) {
  fn() // 相当于在此处直接调用foo()
}
doFoo(obj.foo)
// 被某个对象拥有
function foo() {
    console.log(this.a)
}
var obj = {
    a: 2,
    foo: foo
}
obj.foo();  // 2
// 如果被多个对象调用,this绑定到离函数调用最近的对象
function foo() {
    console.log(this.a)
}
var obj2 = {
    a: 42,
    foo: foo
}
var obj1 = {
    a: 2,
    obj2: obj2
}
obj1.obj2.foo()  // 42
// 离foo()最近的是obj2,所以this绑定到obj2

二、显式绑定

显式绑定是指使用函数的call()、apply()和bind()方法强制地让函数的this绑定到我们想要的对象。显式绑定的优先级比默认绑定优先级高,即如果有显式绑定出现,默认绑定的规则失效。

// call()和apply()方法的第一个参数是一样的,都是this的绑定对象。
function foo() {
    console.log(this.a)
}
var obj = {
    a: 42
}
foo.call(obj)  // 42
foo.apply(obj)  // 42
// bind()方法会返回一个新函数,新函数内部的this绑定到bind()方法的第一个参数
var obj = {
    a: 2,
    foo: function() { return this.a }
}
var bar = obj.foo
bar()  // undefined
var new_bar = bar.bind(obj)
new_bar()  // 2

三、new绑定

new绑定是优先级最高的。使用new来调用函数时,会创建一个全新的对象,并把函数的this绑定到新对象上。

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

四、ES6箭头函数

ES6的最好用的新特性之一就是箭头函数了。箭头函数中的this和我们上述说的规则都没有关系,它是根据箭头函数的外层作用域来决定this(JS中只有函数作用域和全局作用域)

function foo() {
    setTimeout(() => {
        // 这里的this继承自外层作用域的this,即foo的this
        console.log(this.a)  
    }, 100)
}
var obj = {
    a: 2
}
foo.call(obj)  // 2

五、总结

以上三种绑定规则的优先级是:new绑定 > 显式绑定 > 默认绑定。所以我们在判断this时,只要按照优先级来一步一步判断就没有问题了。

posted @ 2017-11-13 11:52  Q-Zhan  阅读(206)  评论(0编辑  收藏  举报