理解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

相關文章
相關標籤/搜索