跟我學(xué)習(xí)javascript的this關(guān)鍵字
本文僅就這一問(wèn)題展開(kāi)討論,閱罷本文,讀者若能正確回答 JavaScript 中的 What 's this 問(wèn)題,作為作者,我就會(huì)覺(jué)得花費(fèi)這么多功夫,撰寫(xiě)這樣一篇文章是值得的。
我們要記住一句話:this永遠(yuǎn)指向函數(shù)運(yùn)行時(shí)所在的對(duì)象!而不是函數(shù)被創(chuàng)建時(shí)所在的對(duì)象。也即:誰(shuí)調(diào)用,指向誰(shuí)。切記…
本文將分三種情況來(lái)分析this對(duì)象到底身處何方。
1、普通函數(shù)中的this
無(wú)論this身處何處,第一要?jiǎng)?wù)就是要找到函數(shù)運(yùn)行時(shí)的位置。
var name="全局"; function getName(){ var name="局部"; return this.name; }; alert(getName());
當(dāng)this出現(xiàn)在全局環(huán)境的函數(shù)getName中時(shí),此時(shí)函數(shù)getName運(yùn)行時(shí)的位置在
alert(getName());
顯然,函數(shù)getName所在的對(duì)象是全局對(duì)象,即window,因此this的安身之處定然在window。此時(shí)的this指向window對(duì)象,則getName返回的this.name其實(shí)是window.name,因此alert出來(lái)的是“全局”!
那么,當(dāng)this不是出現(xiàn)在全局環(huán)境的函數(shù)中,而是出現(xiàn)在局部環(huán)境的函數(shù)中時(shí),又會(huì)身陷何方呢?
var name="全局"; var xpg={ name:"局部", getName:function(){ return this.name; } }; alert(xpg.getName());
其中this身處的函數(shù)getName不是在全局環(huán)境中,而是處在xpg環(huán)境中。無(wú)論this身處何處,一定要找到函數(shù)運(yùn)行時(shí)的位置。此時(shí)函數(shù)getName運(yùn)行時(shí)的位置
alert(xpg.getName());
顯然,函數(shù)getName所在的對(duì)象是xpg,因此this的安身之處定然在xpg,即指向xpg對(duì)象,則getName返回的this.name其實(shí)是xpg.name,因此alert出來(lái)的是“局部”!
舉個(gè)例子鞏固一下:
var someone = { name: "Bob", showName: function(){ alert(this.name); } }; var other = { name: "Tom", showName: someone.showName } other.showName(); //Tom
this關(guān)鍵字雖然是在someone.showName中聲明的,但運(yùn)行的時(shí)候是other.showName,所以this指向other.showName函數(shù)的當(dāng)前對(duì)象,即other,故最后alert出來(lái)的是other.name。
2、閉包中的this
閉包也是個(gè)不安分子,本文暫且不對(duì)其過(guò)于贅述,簡(jiǎn)而言之:所謂閉包就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù),且內(nèi)部函數(shù)訪問(wèn)了外部的變量。
浪子this與痞子閉包混在一起,可見(jiàn)將永無(wú)寧日??!
var name="全局"; var xpg={ name:"局部", getName:function(){ return function(){ return this.name; }; } }; alert(xpg.getName()());
此時(shí)的this明顯身處困境,竟然處在getName函數(shù)中的匿名函數(shù)里面,而該匿名函數(shù)又調(diào)用了變量name,因此構(gòu)成了閉包,即this身處閉包中。
無(wú)論this身處何處,一定要找到函數(shù)運(yùn)行時(shí)的位置。此時(shí)不能根據(jù)函數(shù)getName運(yùn)行時(shí)的位置來(lái)判斷,而是根據(jù)匿名函數(shù)的運(yùn)行時(shí)位置來(lái)判斷。
function (){ return this.name; };
顯然,匿名函數(shù)所在的對(duì)象是window,因此this的安身之處定然在window,則匿名函數(shù)返回的this.name其實(shí)是window.name,因此alert出來(lái)的就是“全局”!
那么,如何在閉包中使得this身處在xpg中呢?—緩存this
var name="全局"; var xpg={ name:"局部", getName:function(){ var that=this; return function(){ return that.name; }; } }; alert(xpg.getName()());
在getName函數(shù)中定義that=this,此時(shí)getName函數(shù)運(yùn)行時(shí)位置在
alert(xpg.getName());
則this指向xpg對(duì)象,因此that也指向xpg對(duì)象。在閉包的匿名函數(shù)中返回that.name,則此時(shí)返回的that.name其實(shí)是xpg.name,因此就可以alert出來(lái) “局部”!
3、new關(guān)鍵字創(chuàng)建新對(duì)象
new關(guān)鍵字后的構(gòu)造函數(shù)中的this指向用該構(gòu)造函數(shù)構(gòu)造出來(lái)的新對(duì)象:
function Person(__name){ this.name = __name; //這個(gè)this指向用該構(gòu)造函數(shù)構(gòu)造的新對(duì)象,這個(gè)例子是Bob對(duì)象 } Person.prototype.show = function(){ alert(this.name); //this 指向Person,this.name = Person.name; } var Bob = new Person("Bob"); Bob.show(); //Bob
4、call與apply中的this
在JavaScript中能管的住this的估計(jì)也就非call與apply莫屬了。
call與apply就像this的父母一般,讓this住哪它就得住哪,不得不聽(tīng)話!當(dāng)無(wú)參數(shù)時(shí),當(dāng)前對(duì)象為window
var name="全局"; var xpg={ name:"局部" }; function getName(){ alert(this.name); } getName(xpg); getName.call(xpg); getName.call();
其中this身處函數(shù)getName中。無(wú)論this身處何處,一定要找到函數(shù)運(yùn)行時(shí)的位置。此時(shí)函數(shù)getName運(yùn)行時(shí)的位置
getName(xpg);
顯然,函數(shù)getName所在的對(duì)象是window,因此this的安身之處定然在window,即指向window對(duì)象,則getName返回的this.name其實(shí)是window.name,因此alert出來(lái)的是“全局”!
那么,該call與apply登場(chǎng)了,因?yàn)閠his必須聽(tīng)他們的指揮!
getName.call(xpg);
其中,call指定this的安身之處就是在xpg對(duì)象,因?yàn)閠his被迫只能在xpg那安家,則此時(shí)this指向xpg對(duì)象, this.name其實(shí)是xpg.name,因此alert出來(lái)的是“局部”!
5、eval中的this
對(duì)于eval函數(shù),其執(zhí)行時(shí)候似乎沒(méi)有指定當(dāng)前對(duì)象,但實(shí)際上其this并非指向window,因?yàn)樵摵瘮?shù)執(zhí)行時(shí)的作用域是當(dāng)前作用域,即等同于在該行將里面的代碼填進(jìn)去。下面的例子說(shuō)明了這個(gè)問(wèn)題:
var name = "window"; var Bob = { name: "Bob", showName: function(){ eval("alert(this.name)"); } }; Bob.showName(); //Bob
6、沒(méi)有明確的當(dāng)前對(duì)象時(shí)的this
當(dāng)沒(méi)有明確的執(zhí)行時(shí)的當(dāng)前對(duì)象時(shí),this指向全局對(duì)象window。
例如對(duì)于全局變量引用的函數(shù)上我們有:
var name = "Tom"; var Bob = { name: "Bob", show: function(){ alert(this.name); } } var show = Bob.show; show(); //Tom
你可能也能理解成show是window對(duì)象下的方法,所以執(zhí)行時(shí)的當(dāng)前對(duì)象時(shí)window。但局部變量引用的函數(shù)上,卻無(wú)法這么解釋?zhuān)?/p>
var name = "window"; var Bob = { name: "Bob", showName: function(){ alert(this.name); } }; var Tom = { name: "Tom", showName: function(){ var fun = Bob.showName; fun(); } }; Tom.showName(); //window
在瀏覽器中setTimeout、setInterval和匿名函數(shù)執(zhí)行時(shí)的當(dāng)前對(duì)象是全局對(duì)象window,這條我們可以看成是上一條的一個(gè)特殊情況。
var name = "Bob"; var nameObj ={ name : "Tom", showName : function(){ alert(this.name); }, waitShowName : function(){ setTimeout(this.showName, 1000); } }; nameObj.waitShowName();
所以在運(yùn)行this.showName的時(shí)候,this指向了window,所以最后顯示了window.name。
7、dom事件中的this
(1)你可以直接在dom元素中使用
<input id="btnTest" type="button" value="提交" onclick="alert(this.value))" />
分析:對(duì)于dom元素的一個(gè)onclick(或其他如onblur等)屬性,它為所屬的html元素所擁有,直接在它觸發(fā)的函數(shù)里寫(xiě)this,this應(yīng)該指向該html元素。
(2)給dom元素注冊(cè)js函數(shù)
a、不正確的方式
<script type="text/javascript"> function thisTest(){ alert(this.value); // 彈出undefined, this在這里指向?? } </script> <input id="btnTest" type="button" value="提交" onclick="thisTest()" />
分析:onclick事件直接調(diào)用thisTest函數(shù),程序就會(huì)彈出undefined。因?yàn)閠hisTest函數(shù)是在window對(duì)象中定義的,
所以thisTest的擁有者(作用域)是window,thisTest的this也是window。而window是沒(méi)有value屬性的,所以就報(bào)錯(cuò)了。
b、正確的方式
<input id="btnTest" type="button" value="提交" /> <script type="text/javascript"> function thisTest(){ alert(this.value); } document.getElementById("btnTest").onclick=thisTest; //給button的onclick事件注冊(cè)一個(gè)函數(shù) </script>
分析:在前面的示例中,thisTest函數(shù)定義在全局作用域(這里就是window對(duì)象),所以this指代的是當(dāng)前的window對(duì)象。而通過(guò)document.getElementById(“btnTest”).onclick=thisTest;這樣的形式,其實(shí)是將btnTest的onclick屬性設(shè)置為thisTest函數(shù)的一個(gè)副本,在btnTest的onclick屬性的函數(shù)作用域內(nèi),this歸btnTest所有,this也就指向了btnTest。其實(shí)如果有多個(gè)dom元素要注冊(cè)該事件,我們可以利用不同的dom元素id,用下面的方式實(shí)現(xiàn):
document.getElementById("domID").onclick=thisTest; //給button的onclick事件注冊(cè)一個(gè)函數(shù)。
因?yàn)槎鄠€(gè)不同的HTML元素雖然創(chuàng)建了不同的函數(shù)副本,但每個(gè)副本的擁有者都是相對(duì)應(yīng)的HTML元素,各自的this也都指向它們的擁有者,不會(huì)造成混亂。
為了驗(yàn)證上述說(shuō)法,我們改進(jìn)一下代碼,讓button直接彈出它們對(duì)應(yīng)的觸發(fā)函數(shù):
<input id="btnTest1" type="button" value="提交1" onclick="thisTest()" /> <input id="btnTest2" type="button" value="提交2" /> <script type="text/javascript"> function thisTest(){ this.value="提交中"; } var btn=document.getElementById("btnTest1"); alert(btn.onclick); //第一個(gè)按鈕函數(shù) var btnOther=document.getElementById("btnTest2"); btnOther.onclick=thisTest; alert(btnOther.onclick); //第二個(gè)按鈕函數(shù) </script> 其彈出的結(jié)果是: //第一個(gè)按鈕 function onclick(){ thisTest() } //第二個(gè)按鈕 function thisTest(){ this.value="提交中"; }
從上面的結(jié)果你一定理解的更透徹了。
By the way,每新建一個(gè)函數(shù)的副本,程序就會(huì)為這個(gè)函數(shù)副本分配一定的內(nèi)存。而實(shí)際應(yīng)用中,大多數(shù)函數(shù)并不一定會(huì)被調(diào)用,于是這部分內(nèi)存就被白白浪費(fèi)了。所以我們通常都這么寫(xiě):
<input id="btnTest1" type="button" value="提交1" onclick="thisTest(this)" /> <input id="btnTest2" type="button" value="提交2" onclick="thisTest(this)" /> <input id="btnTest3" type="button" value="提交3" onclick="thisTest(this)" /> <input id="btnTest4" type="button" value="提交4" onclick="thisTest(this)" /> <script type="text/javascript"> function thisTest(obj){ alert(obj.value); } </script>
這是因?yàn)槲覀兪褂昧撕瘮?shù)引用的方式,程序就只會(huì)給函數(shù)的本體分配內(nèi)存,而引用只分配指針。這樣寫(xiě)一個(gè)函數(shù),調(diào)用的地方給它分配一個(gè)(指針)引用,這樣效率就高很多。當(dāng)然,如果你覺(jué)得這樣注冊(cè)事件不能兼容多種瀏覽器,可以寫(xiě)下面的注冊(cè)事件的通用腳本:
//js事件 添加 EventUtil.addEvent(dom元素,事件名稱,事件觸發(fā)的函數(shù)名) 移除EventUtil.removeEvent(dom元素,事件名稱,事件觸發(fā)的函數(shù)名) var EventUtil = new eventManager(); //js事件通用管理器 dom元素 添加或者移除事件 function eventManager() { //添加事件 //oDomElement:dom元素,如按鈕,文本,document等; ****** oEventType:事件名稱(如:click,如果是ie瀏覽器,自動(dòng)將click轉(zhuǎn)換為onclick);****** oFunc:事件觸發(fā)的函數(shù)名 this.addEvent = function(oDomElement, oEventType, oFunc) { //ie if (oDomElement.attachEvent) { oDomElement.attachEvent("on" + oEventType, oFunc); } //ff,opera,safari等 else if (oDomElement.addEventListener) { oDomElement.addEventListener(oEventType, oFunc, false); } //其他 else { oDomElement["on" + oEventType] = oFunc; } } this.removeEvent = function(oDomElement, oEventType, oFunc) { //ie if (oDomElement.detachEvent) { oDomElement.detachEvent("on" + oEventType, oFunc); } //ff,opera,safari等 else if (oDomElement.removeEventListener) { oDomElement.removeEventListener(oEventType, oFunc, false); } //其他 else { oDomElement["on" + oEventType] = null; } } }
正像注釋寫(xiě)的那樣,要注冊(cè)dom元素事件,用EventUtil.addEvent(dom元素,事件名稱,事件觸發(fā)的函數(shù)名)即可,移除時(shí)可以這樣寫(xiě):EventUtil.removeEvent(dom元素,事件名稱,事件觸發(fā)的函數(shù)名),這是題外話,不說(shuō)了。
以上就是本文的全部?jī)?nèi)容,希望通過(guò)這篇文章大家更加了解javascript的this關(guān)鍵字,大家共同進(jìn)步。
相關(guān)文章
使用JS實(shí)現(xiàn)簡(jiǎn)單的圖片切換功能
這篇文章主要為大家詳細(xì)介紹了使用JS實(shí)現(xiàn)簡(jiǎn)單的圖片切換功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07Javascript中JSON數(shù)據(jù)分組優(yōu)化實(shí)踐及JS操作JSON總結(jié)
這篇文章主要介紹了Javascript中JSON數(shù)據(jù)分組優(yōu)化實(shí)踐,文中還對(duì)JS操作JSON的要領(lǐng)做了總結(jié),需要的朋友可以參考下2017-12-12js判斷輸入是否為正整數(shù)、浮點(diǎn)數(shù)等數(shù)字的函數(shù)代碼
js判斷輸入是否為正整數(shù)、浮點(diǎn)數(shù)等數(shù)字的函數(shù)代碼,學(xué)習(xí)js的朋友可以參考下。2010-11-11JavaScript簡(jiǎn)單獲取頁(yè)面圖片原始尺寸的方法
這篇文章主要介紹了JavaScript簡(jiǎn)單獲取頁(yè)面圖片原始尺寸的方法,可通過(guò)Image()對(duì)象直接獲取圖片的原始寬高,簡(jiǎn)單實(shí)用,需要的朋友可以參考下2016-06-06限制textbox或textarea輸入字符長(zhǎng)度的JS代碼
textbox或textarea的輸入字符限制有很多方法,在本將為大家詳細(xì)介紹下js中時(shí)如何實(shí)現(xiàn)的,感興趣的朋友不要錯(cuò)過(guò)2013-10-10JS實(shí)現(xiàn)可拖曳、可關(guān)閉的彈窗效果
這篇文章主要介紹了JS實(shí)現(xiàn)可拖曳、可關(guān)閉的彈窗效果,可實(shí)現(xiàn)點(diǎn)擊文字彈出可拖動(dòng)的窗口,同時(shí)背景出現(xiàn)變暗的遮罩效果,點(diǎn)擊遮罩層即可關(guān)閉彈出,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09使用JavaScript實(shí)現(xiàn)一個(gè)拖拽縮放效果
這篇文章主要介紹了如何使用JS實(shí)現(xiàn)一個(gè)這樣的拖拽縮放效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05