成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript優(yōu)化之管理作用域

fox_soyoung / 3010人閱讀

摘要:當被創(chuàng)建時,它的作用域鏈初始化為當前運行函數(shù)的屬性中的對象,這些值按照他們出現(xiàn)在函數(shù)中的順序,被復制到執(zhí)行環(huán)境的作用域鏈中。然后這個對象被推入作用域鏈最前端。

在計算機科學中,數(shù)據(jù)存儲的位置關(guān)系到代碼執(zhí)行過程中數(shù)據(jù)的檢索速度,有一個經(jīng)典的問題即為:通過改變數(shù)據(jù)的存儲位置來獲得最佳的讀寫性能。

Javascript中四種基本的數(shù)據(jù)存儲位置

字面量
字面量只代表自身,不存儲在特定的位置。JavaScript中的字面量有:字符串,數(shù)字,布爾值,對象,數(shù)組,函數(shù),正則表達式,以及null&undefined。
字面量是用于表達源代碼中一個固定值的表示法,例如:string str="hello world";
hello world為字面量

本地變量
開發(fā)人員使用關(guān)鍵字var定義的數(shù)據(jù)存儲單元

數(shù)組元素
存儲在JavaScript數(shù)組對象內(nèi)部,以數(shù)字作為索引,這里注意和本地變量的區(qū)別,
var arr = new Array();
arr為本地變量,arr[0]為一個數(shù)組元素

對象成員
儲存在JavaScript對象內(nèi)部,以字符串作為索引

每一種數(shù)據(jù)存儲的位置都有不同的讀寫消耗,一般而言:

從一個字面量中存取數(shù)據(jù)的性能約等于局部變量

數(shù)組元素和對象成員成本較高,高出多少由瀏覽器決定

管理作用域

作用域概念是理解JavaScript的關(guān)鍵所在,不僅僅從性能,還包括從功能的角度。作用域?qū)avaScript有很多影響,從確定哪些變量可以被函數(shù)訪問,到確定this的賦值。

作用域鏈
function可以理解為一個“制造機器的機器”,那么我們可以這樣理解:每一個JavaScript函數(shù)都是一個function對象的實例
那么function對象和其他對象一樣,擁有可以編程訪問的屬性和一系列不同通過代碼訪問而僅供JavaScript引擎存取的內(nèi)部屬性

內(nèi)部屬性之Scope
先放一個Scope的有趣解釋;
Scope屬性包含了一個函數(shù)被創(chuàng)建時的作用域中的對象的集合,這個集合被稱為作用域鏈,它決定哪些數(shù)據(jù)能被函數(shù)訪問。
函數(shù)作用域中的每個對象被稱為一個可變對象,每個可變對象都以“鍵值對”的形式存在。
當一個函數(shù)創(chuàng)建后,它的作用域鏈會被創(chuàng)建此函數(shù)的作用域中可訪問的數(shù)據(jù)對象所填充。
我說下自己的理解:作用域、作用域鏈、內(nèi)置屬性(Scope)其實可以類比權(quán)限、管理組、全局管理員,作用域中的對象以鍵值對的形式存在,成為可變對象,作用域鏈用來連接作用域和Scope,而Scope就好像一種專門管理全局對象的全局管理員;

舉一個例子:
我們先創(chuàng)建一個函數(shù):

function add(num1,num2){
    var sun = num1 + num2;
    return sum;
}

這里我們創(chuàng)建了一個add()函數(shù),當他被創(chuàng)建的時候,在這個函數(shù)的內(nèi)置屬性Scope所包含的作用域鏈中插入一個對象變量,這個全局對象代表著所有在全局范圍內(nèi)定義的變量。 改全局對象包含像window,navigator,document等;

當我們來執(zhí)行上面的函數(shù)又會發(fā)生什么呢?
假如執(zhí)行如下代碼:

 var total = add(5,10);

此時函數(shù)會創(chuàng)建一個稱為 執(zhí)行環(huán)境 或者叫 執(zhí)行上下文 (execution context)的內(nèi)部對象。

一個execution context定義了一個函數(shù)執(zhí)行時的環(huán)境。

函數(shù)每次執(zhí)行時對應的execution context都是獨一無二的,所以多次調(diào)用同一個函數(shù)就會創(chuàng)建多個不一樣的execution context

當函數(shù)執(zhí)行完畢,execution context就會被銷毀

每個execution context都有自己的作用域鏈,用于解析標識符。

execution context被創(chuàng)建時,它的作用域鏈初始化為當前運行函數(shù)的Scope屬性中的對象,這些值按照他們出現(xiàn)在函數(shù)中的順序,被復制到執(zhí)行環(huán)境的作用域鏈中。

前面這個過程完成之后,一個“活動對象”也為execution context創(chuàng)建好了,該對象作為函數(shù)運行時的變量對象,包含了所有的局部變量,參數(shù)集合以及this。

然后這個對象被推入作用域鏈最前端。

execution context被銷毀,活動對象也隨之銷毀。

標識符解析的性能

我們在執(zhí)行過程中是怎樣使用作用域鏈的呢?
在函數(shù)執(zhí)行過程中,沒遇到一個變量,都會經(jīng)歷一次標識符解析過程以決定從哪里獲取或存儲數(shù)據(jù)。標識符解析是性能開銷的,即有代價的!解析標識符實際上就是搜索execution context的作用域鏈,來匹配同名的標識符。

搜索過程從作用域鏈頭部開始,即作用域鏈中的數(shù)字越小越優(yōu)先,這意味著,一個標識符所在的位置越深,它的讀寫速度越慢

函數(shù)中讀寫局部變量總是最快的,而讀寫全局變量總是最慢的

在查找過程中,如果找到,就使用這個標識符對應的變量,若沒找到,則繼續(xù)查找下一個對象,若整個搜索過程都沒有找到匹配的對象,那么這個標識符將被視為未定義的

正是這個搜索過程影響了性能

在沒有優(yōu)化JavaScript引擎的瀏覽器中,盡可能使用局部變量,一個好的經(jīng)驗法則就是:如果某個跨作用域的值在函數(shù)中被引用一次以上,那么就把它存儲在局部變量里
我們來看一個例子:

function initUI(){
    var bd = document.body,
        links = document.getElementsByTagName("a"),
        i = 0,
        len = links.length;
        while(i

我們看到上面這個函數(shù)用了三次document對象,而很不巧,他又是個全局變量,搜索document需要遍歷整個作用域鏈,那么現(xiàn)在有一種解決方案來減少對性能的影響:先將全局變量的引用存儲在一個局部變量里面,然后用這個局部變量來替代全局變量;
那么上述代碼可以重寫為:

function initUI(){
    var doc = document,
        bd = doc.body,
        links = doc.getElementsByTagName("a"),
        i = 0,
        len = links.length;
        while(i

我們將訪問document的次數(shù)由三次變成了一次,如果這個訪問次數(shù)足夠大的話,那么我們的性能將得到極大的改善!
學到這里,我認為改善標識符的解析性能可以從提高解析速度和減少使用次數(shù)兩方面入手,前者通過優(yōu)化JavaScript引擎來進行,后者我們在編程過程中可以進行實踐,兩者的前提都是搜索能夠正確進行!

改變作用域鏈

一般來說,一個execution context的作用域鏈是不會被改變的,但是在JavaScript中有兩個語句是可以在執(zhí)行時臨時改變作用域鏈的,為動態(tài)作用域。

NO.1 With語句
With語句用來在作用域鏈中新創(chuàng)建一個變量對象,這個可變對象包含了參數(shù)指定的對象的所有屬性。先看看With在編程中怎么使用:

function initUI(){
    with (document){
    var bd = body,
        links = getElementsByTagName("a"),
        i = 0,
        len = links.length;
        while(i

從代碼中可以很直觀看到,它也只在全局對象中執(zhí)行一次搜索,從而避免了多次書寫document,但是這樣會更加高效嗎?
我們來看看執(zhí)行with語句時,作用域鏈中發(fā)生了什么:

當執(zhí)行with語句時,它的execution context被臨時改變了,一個新的對量對象被創(chuàng)建,它包含了參數(shù)指定的對象的所有屬性,并且這個對象被推入了作用域鏈的首位;
在上面的例子中,通過把document對象傳遞給with語句,一個包含了document對象所有屬性的新的可變變量被置于作用域的頭部,這樣就出現(xiàn)了一個問題:我訪問document對象的屬性非??欤钱斘蚁朐L問活動對象(也就是局部變量)或者全局對象的屬性時,我的解析標識符速度反而降低了,所以,在減少全局對象屬性這方面的性能優(yōu)化,將document儲存在一個局部變量中比用with語句改變作用域鏈更加可靠!

NO.2 try-catch語句
try-catch語句中的catch字句也具有臨時改變作用域鏈的效果。
try代碼塊中發(fā)生錯誤,執(zhí)行過程會自動跳轉(zhuǎn)到catch字句,然后將異常對象推入一個變量對象并置于作用域的首位,也就是說,在catch代碼塊內(nèi)部,函數(shù)所有的局部變量都會放在第二個作用域鏈對象中,但是,一旦catch代碼塊執(zhí)行完畢,作用域鏈就會返回到之前的狀態(tài)。

try-catch 語句不應該被用來解決JavaScript錯誤,如果某個錯誤重現(xiàn)率很高,最好是盡快修復。其實作用域鏈的改變是發(fā)生在catch代碼塊執(zhí)行的過程中,那么我們?nèi)绻赾atch代碼塊內(nèi)沒有對局部變量和全局變量的訪問,就可以使catch字句對性能的影響最小化!
這種思想的一種實現(xiàn)方法就是將錯誤委托給一個函數(shù)來處理!

閉包的作用域

閉包是JavaScript最強大的特性之一,它允許函數(shù)訪問局部作用域之外的數(shù)據(jù),但是閉包在使用過程種可能會導致性能問題。

我們先來看一個閉包的例子:

function assignEvents(){
    var id = "xdi9592";
    document.getElementById("btn").onclick = function(){
        saveDocument(id);
    };
}

assignEvents()函數(shù)給一個DOM元素設置事件處理函數(shù),這個事件處理函數(shù)就是一個閉包,它在assignEvents()執(zhí)行時創(chuàng)建,并且可以訪問所屬作用域的id變量。

如圖所示,當assignEvents()函數(shù)執(zhí)行時,一個包含了變量id以及其他數(shù)據(jù)的活動對象被創(chuàng)建,這個活動對象成為execution context作用域鏈中的第一個對象,緊接著就是全局對象,然后閉包被創(chuàng)建,并且它的Scope屬性被初始化為這些對象。

至此,出現(xiàn)了第一個問題:內(nèi)存問題。
一般來說,一個函數(shù)執(zhí)行完了之后,函數(shù)作用域鏈中的活動對象會隨著execution context一起被銷毀,但是引入了閉包之后,由于引用仍然存在于閉包的Scope屬性中,所以此時活動對象沒法被銷毀,這意味著腳本中閉包與非閉包函數(shù)相比,需要更多的內(nèi)存開銷。

然后在閉包代碼執(zhí)行時,又會創(chuàng)建一個閉包的execution context,它的作用域鏈與自身Scope中所引用的兩個相同的作用域鏈對象一起被初始化,然后創(chuàng)建一個閉包的活動對象,并且放在首位;

可以看到閉包內(nèi)代碼所用的id & savaDocument,他們的位置分列2,3,就里就是我們在使用閉包過程中所需要關(guān)注的性能點:在頻繁地訪問跨作用域的標識符的時候,每次訪問都會帶來性能損失。

最后劃一下重點:將常用的跨作用域變量存儲到局部變量中,然后直接通過局部變量來訪問,是一個可行的方法。

--END--

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87997.html

相關(guān)文章

  • 前端開發(fā)周報:JavaScript編程術(shù)語和web圖片優(yōu)化

    摘要:函數(shù)式編程術(shù)語大全函數(shù)式編程有許多優(yōu)點,它也越來越流行了。然而,每個編程范式都有自己獨特的術(shù)語,函數(shù)式編程也不例外。作用域有兩種類似全局作用域和局部作用域。目前最重要的應用場景之一,就是在的握手階段,客戶端服務端利用算法交換對稱密鑰。 1、JavaScript 函數(shù)式編程術(shù)語大全 函數(shù)式編程(FP)有許多優(yōu)點,它也越來越流行了。然而,每個編程范式都有自己獨特的術(shù)語,函數(shù)式編程也不例外。...

    kbyyd24 評論0 收藏0
  • 前端開發(fā)周報:JavaScript編程術(shù)語和web圖片優(yōu)化

    摘要:函數(shù)式編程術(shù)語大全函數(shù)式編程有許多優(yōu)點,它也越來越流行了。然而,每個編程范式都有自己獨特的術(shù)語,函數(shù)式編程也不例外。作用域有兩種類似全局作用域和局部作用域。目前最重要的應用場景之一,就是在的握手階段,客戶端服務端利用算法交換對稱密鑰。 1、JavaScript 函數(shù)式編程術(shù)語大全 函數(shù)式編程(FP)有許多優(yōu)點,它也越來越流行了。然而,每個編程范式都有自己獨特的術(shù)語,函數(shù)式編程也不例外。...

    kelvinlee 評論0 收藏0
  • JavaScript 工作原理三-內(nèi)存管理及如何處理 4 類常見的內(nèi)存泄漏問題(譯)

    摘要:這是因為我們訪問了數(shù)組中不存在的數(shù)組元素它超過了最后一個實際分配到內(nèi)存的數(shù)組元素字節(jié),并且有可能會讀取或者覆寫的位。包含個元素的新數(shù)組由和數(shù)組元素所組成中的內(nèi)存使用中使用分配的內(nèi)存主要指的是內(nèi)存讀寫。 原文請查閱這里,本文有進行刪減,文后增了些經(jīng)驗總結(jié)。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第三章。 我們將會討論日常使用中另一個被開發(fā)...

    weknow619 評論0 收藏0
  • javascript作用和閉包我見

    摘要:查詢是在作用域鏈中,一級級的往上查找該變量的引用。作用域和作用域鏈作用域的概念,應該兩張圖幾句話就能解釋吧。這個建筑代表程序中的嵌套作用域鏈。一層嵌一層的作用域形成了作用域鏈,變量在作用域鏈中的函數(shù)內(nèi)得到了自己的定義。 javascript作用域和閉包之我見 看了《你不知道的JavaScript(上卷)》的第一部分——作用域和閉包,感受頗深,遂寫一篇讀書筆記加深印象。路過的大牛歡迎指點...

    SoapEye 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<