一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

深入理解JavaScript作用域和作用域鏈

 埃德溫會館 2011-09-08

深入理解JavaScript作用域和作用域鏈

2011-09-07 10:21 | 1413次閱讀 | 來源:夢想天空 【已有10條評論】發(fā)表評論

關(guān)鍵詞:JavaScript | 作者:夢想天空 | 收藏這篇資訊

作用域是JavaScript最重要的概念之一,想要學(xué)好JavaScript就需要理解JavaScript作用域和作用域鏈的工作原理。今天這篇文章對JavaScript作用域和作用域鏈作簡單的介紹,希望能幫助大家更好的學(xué)習(xí)JavaScript。

JavaScript作用域

任何程序設(shè)計(jì)語言都有作用域的概念,簡單的說,作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期。在JavaScript中,變量的作用域有全局作用域和局部作用域兩種。

1. 全局作用域(Global Scope)

在代碼中任何地方都能訪問到的對象擁有全局作用域,一般來說一下幾種情形擁有全局作用域:

(1)最外層函數(shù)和在最外層函數(shù)外面定義的變量擁有全局作用域,例如:

  1. var authorName="山邊小溪";  
  2. function doSomething(){  
  3.     var blogName="夢想天空";  
  4.     function innerSay(){  
  5.         alert(blogName);  
  6.     }  
  7.     innerSay();  
  8. }  
  9. alert(authorName); //山邊小溪  
  10. alert(blogName); //腳本錯(cuò)誤  
  11. doSomething(); //夢想天空  
  12. innerSay() //腳本錯(cuò)誤 

(2)所有末定義直接賦值的變量自動聲明為擁有全局作用域,例如:

  1. function doSomething(){  
  2.     var authorName="山邊小溪";  
  3.     blogName="夢想天空";  
  4.     alert(authorName);  
  5. }  
  6. alert(blogName); //夢想天空  
  7. alert(authorName); //腳本錯(cuò)誤 

變量blogName擁有全局作用域,而authorName在函數(shù)外部無法訪問到。

(3)所有window對象的屬性擁有全局作用域

一般情況下,window對象的內(nèi)置屬性都都擁有全局作用域,例如window.name、window.location、window.top等等。

2. 局部作用域(Local Scope)

和全局作用域相反,局部作用域一般只在固定的代碼片段內(nèi)可訪問到,最常見的例如函數(shù)內(nèi)部,所有在一些地方也會看到有人把這種作用域成為函數(shù)作用域,例如下列代碼中的blogName和函數(shù)innerSay都只擁有局部作用域。

  1. function doSomething(){  
  2.     var blogName="夢想天空";  
  3.     function innerSay(){  
  4.         alert(blogName);  
  5.     }  
  6.     innerSay();  
  7. }  
  8. alert(blogName); //腳本錯(cuò)誤  
  9. innerSay(); //腳本錯(cuò)誤 

作用域鏈(Scope Chain)

在JavaScript中,函數(shù)也是對象,實(shí)際上,JavaScript里一切都是對象。函數(shù)對象和其它對象一樣,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內(nèi)部屬性。其中一個(gè)內(nèi)部屬性是[[Scope]],由ECMA-262標(biāo)準(zhǔn)第三版定義,該內(nèi)部屬性包含了函數(shù)被創(chuàng)建的作用域中對象的集合,這個(gè)集合被稱為函數(shù)的作用域鏈,它決定了哪些數(shù)據(jù)能被函數(shù)訪問。

當(dāng)一個(gè)函數(shù)創(chuàng)建后,它的作用域鏈會被創(chuàng)建此函數(shù)的作用域中可訪問的數(shù)據(jù)對象填充。例如定義下面這樣一個(gè)函數(shù):

  1. function add(num1,num2) {  
  2.     var sum = num1 + num2;  
  3.     return sum;  

在函數(shù)add創(chuàng)建時(shí),它的作用域鏈中會填入一個(gè)全局對象,該全局對象包含了所有全局變量,如下圖所示(注意:圖片只例舉了全部變量中的一部分):

JavaScript作用域鏈

函數(shù)add的作用域?qū)趫?zhí)行時(shí)用到。例如執(zhí)行如下代碼:

  1. var total = add(5,10); 

執(zhí)行此函數(shù)時(shí)會創(chuàng)建一個(gè)稱為“運(yùn)行期上下文(execution context)”的內(nèi)部對象,運(yùn)行期上下文定義了函數(shù)執(zhí)行時(shí)的環(huán)境。每個(gè)運(yùn)行期上下文都有自己的作用域鏈,用于標(biāo)識符解析,當(dāng)運(yùn)行期上下文被創(chuàng)建時(shí),而它的作用域鏈初始化為當(dāng)前運(yùn)行函數(shù)的[[Scope]]所包含的對象。

這些值按照它們出現(xiàn)在函數(shù)中的順序被復(fù)制到運(yùn)行期上下文的作用域鏈中。它們共同組成了一個(gè)新的對象,叫“活動對象(activation object)”,該對象包含了函數(shù)的所有局部變量、命名參數(shù)、參數(shù)集合以及this,然后此對象會被推入作用域鏈的前端,當(dāng)運(yùn)行期上下文被銷毀,活動對象也隨之銷毀。新的作用域鏈如下圖所示:

JavaScript作用域鏈

在函數(shù)執(zhí)行過程中,沒遇到一個(gè)變量,都會經(jīng)歷一次標(biāo)識符解析過程以決定從哪里獲取和存儲數(shù)據(jù)。該過程從作用域鏈頭部,也就是從活動對象開始搜索,查找同名的標(biāo)識符,如果找到了就使用這個(gè)標(biāo)識符對應(yīng)的變量,如果沒找到繼續(xù)搜索作用域鏈中的下一個(gè)對象,如果搜索完所有對象都未找到,則認(rèn)為該標(biāo)識符未定義。函數(shù)執(zhí)行過程中,每個(gè)標(biāo)識符都要經(jīng)歷這樣的搜索過程。

作用域鏈和代碼優(yōu)化

從作用域鏈的結(jié)構(gòu)可以看出,在運(yùn)行期上下文的作用域鏈中,標(biāo)識符所在的位置越深,讀寫速度就會越慢。如上圖所示,因?yàn)槿肿兞靠偸谴嬖谟谶\(yùn)行期上下文作用域鏈的最末端,因此在標(biāo)識符解析的時(shí)候,查找全局變量是最慢的。所以,在編寫代碼的時(shí)候應(yīng)盡量少使用全局變量,盡可能使用局部變量。一個(gè)好的經(jīng)驗(yàn)法則是:如果一個(gè)跨作用域的對象被引用了一次以上,則先把它存儲到局部變量里再使用。例如下面的代碼:

  1. function changeColor(){  
  2.     document.getElementById("btnChange").onclick=function(){  
  3.         document.getElementById("targetCanvas").style.backgroundColor="red";  
  4.     };  

這個(gè)函數(shù)引用了兩次全局變量document,查找該變量必須遍歷整個(gè)作用域鏈,直到最后在全局對象中才能找到。這段代碼可以重寫如下:

  1. function changeColor(){  
  2.     var doc=document;  
  3.     doc.getElementById("btnChange").onclick=function(){  
  4.         doc.getElementById("targetCanvas").style.backgroundColor="red";  
  5.     };  

這段代碼比較簡單,重寫后不會顯示出巨大的性能提升,但是如果程序中有大量的全局變量被從反復(fù)訪問,那么重寫后的代碼性能會有顯著改善。

改變作用域鏈

函數(shù)每次執(zhí)行時(shí)對應(yīng)的運(yùn)行期上下文都是獨(dú)一無二的,所以多次調(diào)用同一個(gè)函數(shù)就會導(dǎo)致創(chuàng)建多個(gè)運(yùn)行期上下文,當(dāng)函數(shù)執(zhí)行完畢,執(zhí)行上下文會被銷毀。每一個(gè)運(yùn)行期上下文都和一個(gè)作用域鏈關(guān)聯(lián)。一般情況下,在運(yùn)行期上下文運(yùn)行的過程中,其作用域鏈只會被 with 語句和 catch 語句影響。

with語句是對象的快捷應(yīng)用方式,用來避免書寫重復(fù)代碼。例如:

  1. function initUI(){  
  2.     with(document){  
  3.         var bd=body,  
  4.             links=getElementsByTagName("a"),  
  5.             i=0,  
  6.             len=links.length;  
  7.         while(i < len){  
  8.             update(links[i++]);  
  9.         }  
  10.         getElementById("btnInit").onclick=function(){  
  11.             doSomething();  
  12.         };  
  13.     }  

這里使用width語句來避免多次書寫document,看上去更高效,實(shí)際上產(chǎn)生了性能問題。

當(dāng)代碼運(yùn)行到with語句時(shí),運(yùn)行期上下文的作用域鏈臨時(shí)被改變了。一個(gè)新的可變對象被創(chuàng)建,它包含了參數(shù)指定的對象的所有屬性。這個(gè)對象將被推入作用域鏈的頭部,這意味著函數(shù)的所有局部變量現(xiàn)在處于第二個(gè)作用域鏈對象中,因此訪問代價(jià)更高了。如下圖所示:

JavaScript作用域鏈

因此在程序中應(yīng)避免使用with語句,在這個(gè)例子中,只要簡單的把document存儲在一個(gè)局部變量中就可以提升性能。

另外一個(gè)會改變作用域鏈的是try-catch語句中的catch語句。當(dāng)try代碼塊中發(fā)生錯(cuò)誤時(shí),執(zhí)行過程會跳轉(zhuǎn)到catch語句,然后把異常對象推入一個(gè)可變對象并置于作用域的頭部。在catch代碼塊內(nèi)部,函數(shù)的所有局部變量將會被放在第二個(gè)作用域鏈對象中。示例代碼:

  1. try{  
  2.     doSomething();  
  3. }catch(ex){  
  4.     alert(ex.message); //作用域鏈在此處改變  

請注意,一旦catch語句執(zhí)行完畢,作用域鏈機(jī)會返回到之前的狀態(tài)。try-catch語句在代碼調(diào)試和異常處理中非常有用,因此不建議完全避免。你可以通過優(yōu)化代碼來減少catch語句對性能的影響。一個(gè)很好的模式是將錯(cuò)誤委托給一個(gè)函數(shù)處理,例如:

  1. try{  
  2.     doSomething();  
  3. }catch(ex){  
  4.     handleError(ex); //委托給處理器方法  

優(yōu)化后的代碼,handleError方法是catch子句中唯一執(zhí)行的代碼。該函數(shù)接收異常對象作為參數(shù),這樣你可以更加靈活和統(tǒng)一的處理錯(cuò)誤。由于只執(zhí)行一條語句,且沒有局部變量的訪問,作用域鏈的臨時(shí)改變就不會影響代碼性能了。

文章出自:夢想天空

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    久久精品中文扫妇内射| 99精品国产一区二区青青| 日韩欧美中文字幕av| 一区二区三区亚洲天堂| 欧美日韩国产福利在线观看| 国产一区在线免费国产一区| 久久91精品国产亚洲| 中文字幕亚洲在线一区| 一个人的久久精彩视频| 五月天婷亚洲天婷综合网| 国产成人精品国产成人亚洲| 日韩欧美第一页在线观看 | 91在线国内在线中文字幕| 国产欧美另类激情久久久| 欧美小黄片在线一级观看| 国产欧美日韩在线精品一二区 | 欧美日韩国产欧美日韩| 91偷拍视频久久精品| 99久只有精品免费视频播放| 亚洲最新av在线观看| 亚洲人妻av中文字幕| 国产午夜福利在线观看精品| 粉嫩国产一区二区三区在线| 在线日本不卡一区二区| 国产日韩精品激情在线观看| 中文日韩精品视频在线| 老熟女露脸一二三四区| 国产精品激情对白一区二区| 国产亚洲欧美另类久久久| 日本高清视频在线播放| 欧美日韩亚洲精品在线观看| 深夜福利亚洲高清性感| 日韩一区二区三区在线日| 免费观看成人免费视频| 伊人久久五月天综合网| 国产偷拍盗摄一区二区| 黄色片一区二区在线观看| 日本一本在线免费福利| 国内九一激情白浆发布| 在线观看免费无遮挡大尺度视频| 亚洲中文字幕三区四区|