這是一篇目前篇幅不長,可是寫起來至關漫長的文章。中途,我翻譯了這篇文章,很是有必要一讀。javascript
「在函數內部,有兩個特殊的對象: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
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
這段代碼能夠在不一樣的上下文對象中重複使用identify
和speak
,不用針對每一個對象編寫不一樣版本的函數。若是沒有this
,就須要給函數顯式地傳遞一個上下文對象。隨着你的模式愈來愈複雜,顯式傳遞上下文會讓代碼愈來愈混亂。當涉及到原型委託的時候,你就會明白函數能夠自動引用合適的上下文對象有多重要。this
我在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
指向就好辦了啊,倒過來看就行了:
函數是否被new修飾(new綁定)?若是是的話,this
綁定的就是新建立的對象
函數是否經過call、apply()或者bind()硬綁定(顯式綁定)?若是是的話,this
綁定的就是指定的對象。
函數是否在某個上下文對象調用(隱式綁定)?若是是的話,this
綁定的就是那個對象。
若是都不是的話,使用默認綁定。若是在嚴格模式下,就綁定undefined
,不然是全局對象。
這多是這篇文章最核心的地方,可是鑑於這四條太常見了,我假設讀者已經知道它們了。
把
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了。仔細看看就能明白這裏是符合第四條的默認綁定的。
不要看它是在哪定義的,也不要看它是在哪被調用的,看它是怎麼被調用的
function foo() { let a = 2 bar() } function bar() { console.log(this.a) } foo() // ReferenceError: a is not a defined
對照第四條,默認綁定。
每當你想要把this
和詞法做用域的查找混合使用時,記得提醒本身,這是無法實現的,箭頭函數例外。
隱式丟失一般包括這幾種狀況:
賦值運算符 : (f = foo.bar)()
逗號運算符 : (1, foo.bar)()
回調函數
這一點能夠說是一個龐大的工程,爲此我翻譯了這篇博客。翻譯這篇博客這是件很是累的事兒,幸運的是,看完這篇博客以後,我明白了不少我沒想到的東西。
全部須要瞭解的東西均可以在這篇博客裏找到答案。
咱們知道,當代碼進入一個函數時,做用域會把函數的參數、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。
若是你瞭解過箭頭函數的話,你可能知道箭頭函數能夠淘汰掉這種寫法。
雖然少打好幾個字符就已經足夠讓人開心了,可是箭頭函數的能力還不止於此。
var me = { name: 'cyq', getName: function() { return () => { return this.name } } } console.log(object.getName()()) //cyq
因此箭頭函數必定對它裏面的this
作了從新設計咯?答案是否認的。
箭頭函數能實現相似詞法做用域的查找是由於,它裏面根本就沒有this
對象。前面提到,this
自動定義在全部函數的做用域內,內嵌函數是不能查找到外部的this
對象的,由於本身的做用域裏就有this
了。而箭頭函數這個特殊的函數直接去做用域裏找this
。