javascript
深入浅出 JavaScript 中的 this
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
在 Java 等面向?qū)ο蟮恼Z(yǔ)言中,this 關(guān)鍵字的含義是明確且具體的,即指代當(dāng)前對(duì)象。一般在編譯期確定下來(lái),或稱為編譯期綁定。而在 JavaScript 中,this 是動(dòng)態(tài)綁定,或稱為運(yùn)行期綁定的,這就導(dǎo)致 JavaScript 中的 this 關(guān)鍵字有能力具備多重含義,帶來(lái)靈活性的同時(shí),也為初學(xué)者帶來(lái)不少困惑。本文僅就這一問(wèn)題展開(kāi)討論,閱罷本文,讀者若能正確回答 JavaScript 中的 What ’s this 問(wèn)題,作為作者,我就會(huì)覺(jué)得花費(fèi)這么多功夫,撰寫(xiě)這樣一篇文章是值得的。
Java 語(yǔ)言中的 this
在 Java 中定義類經(jīng)常會(huì)使用 this 關(guān)鍵字,多數(shù)情況下是為了避免命名沖突,比如在下面例子的中,定義一個(gè) Point 類,很自然的,大家會(huì)使用 x,y 為其屬性或成員變量命名,在構(gòu)造函數(shù)中,使用 x,y 為參數(shù)命名,相比其他的名字,比如 a,b,也更有意義。這時(shí)候就需要使用 this 來(lái)避免命名上的沖突。另一種情況是為了方便的調(diào)用其他構(gòu)造函數(shù),比如定義在 x 軸上的點(diǎn),其 x 值默認(rèn)為 0,使用時(shí)只要提供 y 值就可以了,我們可以為此定義一個(gè)只需傳入一個(gè)參數(shù)的構(gòu)造函數(shù)。無(wú)論哪種情況,this 的含義是一樣的,均指當(dāng)前對(duì)象。
?public?class?Point?{?private?int?x?=?0;?private?int?y?=?0;?public?Point(x,?y){?this.x?=?x;?this.y?=?y;?}?public?Point(y){?this(0,?y);?}?}JavaScript 語(yǔ)言中的 this
由于其運(yùn)行期綁定的特性,JavaScript 中的 this 含義要豐富得多,它可以是全局對(duì)象、當(dāng)前對(duì)象或者任意對(duì)象,這完全取決于函數(shù)的調(diào)用方式。JavaScript 中函數(shù)的調(diào)用有以下幾種方式:作為對(duì)象方法調(diào)用,作為函數(shù)調(diào)用,作為構(gòu)造函數(shù)調(diào)用,和使用 apply 或 call 調(diào)用。下面我們將按照調(diào)用方式的不同,分別討論 this 的含義。
作為對(duì)象方法調(diào)用
在 JavaScript 中,函數(shù)也是對(duì)象,因此函數(shù)可以作為一個(gè)對(duì)象的屬性,此時(shí)該函數(shù)被稱為該對(duì)象的方法,在使用這種調(diào)用方式時(shí),this 被自然綁定到該對(duì)象。
var?point?=?{?x?:?0,?y?:?0,?moveTo?:?function(x,?y)?{?this.x?=?this.x?+?1;?this.y?=?this.y?+?1;?}?};?point.moveTo(1,?1)//this?綁定到當(dāng)前對(duì)象,即?point?對(duì)象作為函數(shù)調(diào)用(小心)
函數(shù)也可以直接被調(diào)用,此時(shí) this 綁定到全局對(duì)象。在瀏覽器中,window 就是該全局對(duì)象。比如下面的例子:函數(shù)被調(diào)用時(shí),this 被綁定到全局對(duì)象,接下來(lái)執(zhí)行賦值語(yǔ)句,相當(dāng)于隱式的聲明了一個(gè)全局變量,這顯然不是調(diào)用者希望的。
function?makeNoSense(x)?{?this.x?=?x;?}?makeNoSense(5);?alert(x);x;//?x?已經(jīng)成為一個(gè)值為?5?的全局變量對(duì)于內(nèi)部函數(shù),即聲明在另外一個(gè)函數(shù)體內(nèi)的函數(shù),這種綁定到全局對(duì)象的方式會(huì)產(chǎn)生另外一個(gè)問(wèn)題。我們?nèi)匀灰郧懊嫣岬降?point 對(duì)象為例,這次我們希望在 moveTo 方法內(nèi)定義兩個(gè)函數(shù),分別將 x,y 坐標(biāo)進(jìn)行平移。結(jié)果可能出乎大家意料,不僅 point 對(duì)象沒(méi)有移動(dòng),反而多出兩個(gè)全局變量 x,y。
?var?point?=?{?x?:?0,?y?:?0,?moveTo?:?function(x,?y)?{?//?內(nèi)部函數(shù)var?moveX?=?function(x)?{?this.x?=?x;//this?綁定到了哪里?};?//?內(nèi)部函數(shù)var?moveY?=?function(y)?{?this.y?=?y;//this?綁定到了哪里?};?moveX(x);?moveY(y);?}?};?point.moveTo(1,?1);?point.x;?//==>0?point.y;?//==>0?x;?//==>1?y;?//==>1這應(yīng)該屬于 JavaScript 的設(shè)計(jì)缺陷,正確的設(shè)計(jì)方式是內(nèi)部函數(shù)的 this 應(yīng)該綁定到其外層函數(shù)對(duì)應(yīng)的對(duì)象上,為了規(guī)避這一設(shè)計(jì)缺陷,聰明的 JavaScript 程序員想出了變量替代的方法,約定俗成,該變量一般被命名為 that。
var?point?=?{?x?:?0,?y?:?0,?moveTo?:?function(x,?y)?{?var?that?=?this;?//?內(nèi)部函數(shù)var?moveX?=?function(x)?{?that.x?=?x;?};?//?內(nèi)部函數(shù)var?moveY?=?function(y)?{?that.y?=?y;?}?moveX(x);?moveY(y);?}?};?point.moveTo(1,?1);?point.x;?//==>1?point.y;?//==>1
作為構(gòu)造函數(shù)調(diào)用
JavaScript 支持面向?qū)ο笫骄幊?#xff0c;與主流的面向?qū)ο笫骄幊陶Z(yǔ)言不同,JavaScript 并沒(méi)有類(class)的概念,而是使用基于原型(prototype)的繼承方式。相應(yīng)的,JavaScript 中的構(gòu)造函數(shù)也很特殊,如果不使用 new 調(diào)用,則和普通函數(shù)一樣。作為又一項(xiàng)約定俗成的準(zhǔn)則,構(gòu)造函數(shù)以大寫(xiě)字母開(kāi)頭,提醒調(diào)用者使用正確的方式調(diào)用。如果調(diào)用正確,this 綁定到新創(chuàng)建的對(duì)象上。
?function?Point(x,?y){?this.x?=?x;?this.y?=?y;?}使用 apply 或 call 調(diào)用
讓我們?cè)僖淮沃厣?#xff0c;在 JavaScript 中函數(shù)也是對(duì)象,對(duì)象則有方法,apply 和 call 就是函數(shù)對(duì)象的方法。這兩個(gè)方法異常強(qiáng)大,他們?cè)试S切換函數(shù)執(zhí)行的上下文環(huán)境(context),即 this 綁定的對(duì)象。很多 JavaScript 中的技巧以及類庫(kù)都用到了該方法。讓我們看一個(gè)具體的例子:
?function?Point(x,?y){?this.x?=?x;?this.y?=?y;?this.moveTo?=?function(x,?y){?this.x?=?x;?this.y?=?y;?}?}?var?p1?=?new?Point(0,?0);?var?p2?=?{x:?0,?y:?0};?p1.moveTo(1,?1);?p1.moveTo.apply(p2,?[10,?10]);在上面的例子中,我們使用構(gòu)造函數(shù)生成了一個(gè)對(duì)象 p1,該對(duì)象同時(shí)具有 moveTo 方法;使用對(duì)象字面量創(chuàng)建了另一個(gè)對(duì)象 p2,我們看到使用 apply 可以將 p1 的方法應(yīng)用到 p2 上,這時(shí)候 this 也被綁定到對(duì)象 p2 上。另一個(gè)方法 call 也具備同樣功能,不同的是最后的參數(shù)不是作為一個(gè)數(shù)組統(tǒng)一傳入,而是分開(kāi)傳入的。
換個(gè)角度理解
如果像作者一樣,大家也覺(jué)得上述四種方式不方便記憶,過(guò)一段時(shí)間后,又搞不明白 this 究竟指什么。那么我向大家推薦 Yehuda Katz 的這篇文章:Understanding JavaScript Function Invocation and “this”。在這篇文章里,Yehuda Katz 將 apply 或 call 方式作為函數(shù)調(diào)用的基本方式,其他幾種方式都是在這一基礎(chǔ)上的演變,或稱之為語(yǔ)法糖。Yehuda Katz 強(qiáng)調(diào)了函數(shù)調(diào)用時(shí) this 綁定的過(guò)程,不管函數(shù)以何種方式調(diào)用,均需完成這一綁定過(guò)程,不同的是,作為函數(shù)調(diào)用時(shí),this 綁定到全局對(duì)象;作為方法調(diào)用時(shí),this 綁定到該方法所屬的對(duì)象。
結(jié)束?
通過(guò)上面的描述,如果大家已經(jīng)能明確區(qū)分各種情況下 this 的含義,這篇文章的目標(biāo)就已經(jīng)完成了。如果大家的好奇心再?gòu)?qiáng)一點(diǎn),想知道為什么 this 在 JavaScript 中的含義如此豐富,那就得繼續(xù)閱讀下面的內(nèi)容了。作者需要提前告知大家,下面的內(nèi)容會(huì)比前面稍顯枯燥,如果只想明白 this 的含義,閱讀到此已經(jīng)足夠了。如果大家不嫌枯燥,非要探尋其中究竟,那就一起邁入下一節(jié)吧。
原文:http://www.ibm.com/developerworks/cn/web/1207_wangqf_jsthis/index.html
轉(zhuǎn)載于:https://my.oschina.net/dlam/blog/468305
總結(jié)
以上是生活随笔為你收集整理的深入浅出 JavaScript 中的 this的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android Shell命令dumps
- 下一篇: Spring Boot快速搭建Sprin