javascript筆記:通過對作用域鏈和執行環境的深入理解所得出的提高javascript代碼性能的建議

上篇文章裏我結束了對象的創建的內容,最後引出了作用域鏈和執行環境的問題。當我對這塊知識有了更深入的瞭解後,回頭看看jQuery源碼才知道大師們寫的代碼是如何的厲害,jQuery源碼裏很好的運用了作用域鏈和執行環境的知識來提升程序性能。

  好了,不廢話了,上篇博文裏對作用域講的比較簡略。其實對作用域的理解是理解整個javascript語言的關鍵所在,特別我在寫javascript筆記時候曾對很多怪異的javascript用法無法理解的透徹,究其原因還是沒有真正理解javascript裏作用域的概念。

  Javascript裏的作用域到底決定了什麼呢?作用域決定了那些變量能被函數所訪問(注意:作用域是函數的內部屬性,談到作用域是繞不開的function),作用域也確定了this指針的指向。上篇博文裏我說道,程序其實就是不斷檢索數據的過程,那麼檢索數據的效率決定了程序的性能,因此作用域既然決定那些變量能被訪問,當然也決定了檢索這些變量的方式,所以想寫出高效的javascript程序靈活運用作用域的原理是關鍵了。

  上篇博文裏面,我寫了一個函數,代碼如下:

複製代碼
<script type="text/javascript">
function add(a,b)
{
    var sum = a + b;
    return sum;
}
</script>
複製代碼

  大家可以看到這個函數屬於window而非function,在頁面被加載時候,add函數會被初始化,生成屬於自己的作用域鏈,上篇文章裏我在firebug裏設定斷點調試貼出了一個我截圖下來的變量圖,認爲這就是函數add的Scope所包含的變量,現在發現當時理解是錯誤的,那些變量是window的“全局環境”的變量圖而非是add函數的內部屬性Scope的變量圖。在此我糾正一下。

  下面我要着重講講作用域鏈和執行環境,這塊知識我在javascript對象創建的中篇裏提到過,這裏我將那些知識回顧下:

複製代碼
    什麼是執行環境呢?在javascript裏面執行環境分爲兩類,一類是全局環境,一類是局部環境,整個頁面裏被共享的方法和屬性就是在全局環境,相對於全局環境,函數{}號裏的執行環境就是局部環境,執行環境定義了變量或函數有權訪問的其他數據,決定了它們各自的行爲,每個執行環境都定義了一個與之相關的變量對象,環境中定義的所有變量和函數都保存在這個對象裏,雖然我們自己編寫的代碼無法訪問這個對象,但解析器在處理數據時候後臺會使用到它。
    全局執行環境另一種說法是最外圍的一個執行環境,在web瀏覽器的範圍中(actionscript也是施行了ECMAScript標準,它的全局範圍就和javascript的全局範圍不同),全局執行環境被認爲是window對象,因此全局變量和函數都是作爲window對象的方法和屬性來創建的,全局執行環境知道應用程序退出比如關閉網頁或瀏覽器纔會被銷燬。而局部環境則是以函數對象作爲關聯對象。
複製代碼

  大家要注意環境的前綴是:執行,也就說只有函數被執行(局部執行環境)和頁面被加載(全局執行環境),下面就是頁面被加載時候的全局執行環境變量:

  現在我們要執行add函數了,代碼如下:

複製代碼
function add(a,b)
{
    var sum = a + b;
    return sum;
}

console.log(add(10,20));
複製代碼

  

  這是add函數的執行環境的變量圖是:

  1.firebug:

  2.chrome的代碼調試器:

  代碼的結構圖如下:

  在函數被執行時候,函數會創建一個“運行期上下文(execution context)”的內部對象,這個運行期上下文在我的理解裏就是函數的執行環境,每個運行期上下文都有自己的作用域鏈,用於標識符解析。

  當運行期上下文被創建時候,他的作用域鏈初始化爲當前運行函數【Scope】屬性中所含的對象(見firebug和chrome調試器裏的內存圖,比如add函數除了a,b,sum,this還有arguments,不過這個在firebug和chrome調試器裏看不到的)。這些值按照它們出現在函數中的順序,被複制到執行期上下文的作用域鏈中。這個過程一旦完成,一個被稱爲“活動對象(activation object)”的新對象就爲執行期上下文創建好了,活動對象作爲函數運行期的可變對象,包含了所有局部變量,命名參數,參數集合以及this。然後此對象被推入到作用域鏈的前端。當運行期上下文被銷燬的時候,活動對象也隨之被銷燬。在函數執行過程中,每遇到一個變量都會進行一次標識符的解析,這種解析決定了我們從啥地方獲取變量或者將變量存儲到什麼地方,這個過程是搜索整個函數運行期上下文的作用域鏈,查找同名的標識符,搜索過程都是從作用域頭部開始,也就是當前運行函數的活動對象。找到了就使用它,沒有找到則會繼續搜索作用域鏈中的下一個對象,搜索過程會延續到找到標識符爲止或者是沒有找到爲止,這種情況就是標識符沒有被定義了

  在javascript裏使用變量就是在做標識符解析,由上面的解釋我們知道標識符的解析一定是有計算機性能的消耗的,當標識符位於執行上下文作用域鏈的位置越深,性能也就越慢了,那麼到底什麼地方最慢了?比如我們舉例的函數add,當我們搜索到global全局變量總會比函數內部的局部變量慢,假如我們定義個更復雜的函數裏面有多層嵌套的話,訪問全局變量就是性能的夢魘了。因此在函數中我們儘量多使用函數內部的局部變量。(由於瀏覽器的產品的差異,個別瀏覽器的訪問變量的性能可能會有差異,但是總體而言,訪問函數局部變量永遠是最快的)。

  儘量使用局部變量,就帶來一個十分重要的提高程序性能的用法:我們在函數內部使用全局變量可以說是一種跨作用域操作,如果某個跨作用域的值在函數的內部被使用到一次以上,那麼我們就把它存儲到局部變量裏

  代碼書寫的格式就是:

function ftn()
{
    var doc = document;
    .......
}

 

  將全局的變量用var定義到局部變量裏。

  這個用法在jQuery源碼裏一開頭就清晰可以到看:

複製代碼
var jQuery = function( selector, context ) {
        // The jQuery object is actually just the init constructor 'enhanced'
        return new jQuery.fn.init( selector, context );
    },

    // Map over jQuery in case of overwrite
    _jQuery = window.jQuery,

    // Map over the $ in case of overwrite
    _$ = window.$,

    // Use the correct document accordingly with window argument (sandbox)
    document = window.document,
複製代碼

  jQuery把經常使用的全局變量都存儲在函數的局部變量裏。


發佈了0 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章