理解This

Know Everything About This

这是一篇目前篇幅不长,可是写起来至关漫长的文章。中途,我翻译了这篇文章,很是有必要一读。javascript

1、是什么?

「在函数内部,有两个特殊的对象:arguments和this」java

你可能见过这样一个函数:segmentfault

function addUp(){
  return Array.from(arguments).reduce(function (prev, curr) {
    return prev + curr
  })
}

并无定义过它,可是arguments直接就这么冒出来了。this和它同样,它们自动定义在全部函数的做用域中,即便你并无用它。app

NZ在《JavaScript高级程序设计》里给this下了个定义:「this引用的是函数据以执行的环境对象」。这就像是不少人强调的,不要看函数在哪里定义,要看函数在哪里被调用。ide

2、为何?

this机制能够解耦对象和函数:函数

function identify () {
 return this.name.toUpperCase()
}

function speak () {
  var greeting = "Hello,I'm + identify.call(this)"
  console.log(greeting) 
}

var me = {
  name: 'cyq'
}

var you = {
  name: 'reader'
}

identify.call(me) // CYQ
identify.call(you) // READER
 
 speak.call(me) // Hello,I'm CYQ
 speak.call(you) // Hello,I'm READER

这段代码能够在不一样的上下文对象中重复使用identifyspeak,不用针对每一个对象编写不一样版本的函数。若是没有this,就须要给函数显式地传递一个上下文对象。随着你的模式愈来愈复杂,显式传递上下文会让代码愈来愈混乱。当涉及到原型委托的时候,你就会明白函数能够自动引用合适的上下文对象有多重要。this

3、怎么绑?

我在kangax博客里看到过多是最短的总结:prototype

  • The keyword "this" refers to whatever is left of the dot at call-time.翻译

  • If there's nothing to the left of the dot, then "this" is the root scope (e.g. Window).设计

  • A few functions change the behavior of "this"—bind, call and apply

  • The keyword "new" binds this to the object just created

若是看过了这个四条法则,那判断this指向就好办了啊,倒过来看就行了:

  1. 函数是否被new修饰(new绑定)?若是是的话,this绑定的就是新建立的对象

  2. 函数是否经过call、apply()或者bind()硬绑定(显式绑定)?若是是的话,this绑定的就是指定的对象。

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

  4. 若是都不是的话,使用默认绑定。若是在严格模式下,就绑定undefined,不然是全局对象。

这多是这篇文章最核心的地方,可是鉴于这四条太常见了,我假设读者已经知道它们了。

1、陷阱

1. 指向自身

this指向函数自身,从语法角度来讲是说得通的

function foo(num) { 
  this.count++
}

foo.count = 0

for(let i = 0;i < 10;i++) {
  if(i > 5) {
    foo(i)
  }
}

console.log(foo.count) // 0

若是this指向的是函数自身的话,foo.count就该是4而不是0了。仔细看看就能明白这里是符合第四条的默认绑定的。

2. 指向词法做用域

不要看它是在哪定义的,也不要看它是在哪被调用的,看它是怎么被调用的

function foo() {
  let a = 2
  bar()
}

function bar() {
  console.log(this.a)
}

foo() // ReferenceError: a is not a defined

对照第四条,默认绑定。

每当你想要把this和词法做用域的查找混合使用时,记得提醒本身,这是无法实现的,箭头函数例外。

3. 隐式丢失

隐式丢失一般包括这几种状况:

  • 赋值运算符 : (f = foo.bar)()

  • 逗号运算符 : (1, foo.bar)()

  • 回调函数

这一点能够说是一个庞大的工程,为此我翻译了这篇博客。翻译这篇博客这是件很是累的事儿,幸运的是,看完这篇博客以后,我明白了不少我没想到的东西。
全部须要了解的东西均可以在这篇博客里找到答案。

2、问题

1. self = this是什么?

咱们知道,当代码进入一个函数时,做用域会把函数的参数、this 对象和 arguments对象包括在内。所以咱们不可能在一个函数里面用this取到其余函数做用域内的this对象。

var object = {
    name: 'My object',

    getName: function() {
        return function() {
            return this.name
        }
    }
}

console.log(object.getName()())

这段代码会输出的是什么?虽然看上去很像是'My Object',可是结果是undefined.缘由上面提到了:最内层的匿名函数的做用域内的this和getName的this是两回事。那想要输出'My Object'的话,天然要想办法把getName的this绑定到匿名函数下:

var me = {
    name: 'cyq',

    getName: function() {
      var self = this
        return function() {
            return self.name
        }
    }
}

console.log(object.getName()())

能够看到,self=this经常使用的场景就是帮咱们在一个内层的函数里找到外层函数的this对象。咱们须要把外层的this绑定在一个变量上,从而让内层函数完成某种相似于词法做用域的查找。我我的是很不喜欢这个写法的,后来我发现我不是一我的:搞懂JavaScript的Function.prototype.bind

若是你了解过箭头函数的话,你可能知道箭头函数能够淘汰掉这种写法。

2. 箭头函数

虽然少打好几个字符就已经足够让人开心了,可是箭头函数的能力还不止于此。

var me = {
    name: 'cyq',

    getName: function() {
        return () => {
            return this.name
        }
    }
}

console.log(object.getName()()) //cyq

因此箭头函数必定对它里面的this作了从新设计咯?答案是否认的。

箭头函数能实现相似词法做用域的查找是由于,它里面根本就没有this对象。前面提到,this自动定义在全部函数的做用域内,内嵌函数是不能查找到外部的this对象的,由于本身的做用域里就有this了。而箭头函数这个特殊的函数直接去做用域里找this

相关文章
相关标签/搜索