js便籤筆記(11)——瀏覽TOM大叔部落格的學習筆記 part1

王福朋發表於2014-08-31

1. 前言

這兩天看了一下TOM大叔的《深入理解js系列》中的基礎部分,根據自己的實際情況,做了讀書筆記,記錄了部分容易絆腳的問題。寫篇文章,供大家分享。

 

2. 關於HTMLCollection的“實時查詢”

var divs = document.getElementsByTagName("div"),
      i;
for (i = 0; i < divs.length; i++) {
      //……
}

以上程式碼中,會出現效能問題。問題就在於divs是一個HTMLCollection型別的物件,這種型別在每次獲取資料時,都會再重新從dom中分析計算。因此,這裡的for迴圈中,每一步迴圈都會執行一次divs.length的計算,都會令瀏覽器再重新遍歷一遍dom樹。
所以,應該在迴圈之外,早早的計算出divs的length屬性值。如下:

var divs = document.getElementsByTagName("div"),
    i,
    length = divs.length;
for (i = 0; i < length; i++) {
    //……
}

3.  for..in...時,注意hasOwnProperty驗證

var obj = {
                a: 10,
                b: 20
            };
            
            // 注意詞句程式碼
            Object.prototype.c = 30;

            var item;
            for (item in obj) {
                console.log(item);
            }

以上程式碼中,注意中間標註釋的句子。這句程式碼加與不加,會對下面的for..in..迴圈產生影響。加上了就輸出“c”,不加就不輸出“c”。道理很簡單,for..in..迴圈不光能遍歷obj物件本身就有的屬性,還能遍歷obj原型中的屬性。

要想遮蔽掉原型中的屬性,就用hasOwnProperty函式,如下:

           for (item in obj) {
                if (obj.hasOwnProperty(item)) {
                //if (Object.prototype.hasOwnProperty.call(obj, item)) {
                    console.log(item);
                }
            }

這兩句if判斷語句,都可以用,效果是一樣的。第一個程式碼可讀性好,第二個效率相對較高。建議,沒有特殊情況,用第一個即可。

4. 老問題:迴圈中生成函式/事件的閉包問題

var events = [],
                i = 0;
        //迴圈建立函式
        for (; i < 10; i++) {
            events[i] = function () {
                console.log(i);
            };
        }

        //驗證結果
        for (i = 0; i < events.length; i++) {
            events[0]();
        }

先看以上程式碼,有js開發經驗的人肯定都很熟悉,但是有的人知道,有的人不知道。依照以上程式碼,迴圈遍歷events陣列,執行陣列元素中的函式,這10個函式都會列印出什麼數字?答案是:全都會輸出“10”。下面解釋一下原因:

在每個函式生成/被建立時,系統都會給它分配一個變數的環境,這個環境中也包括閉包的資料。但是,當多個環境,公用一個閉包的資料時,是一種引用的關係,這是重點。引用,不是複製。如果改變了這個公用資料,這些公用的環境獲取時,也是改變後的資料。

所以,在建立第一個函式時,i === 0,第一個函式引用的閉包中i的值就是0;第二個函式被建立時,i === 1,這是,第一個、第二個兩個函式應用閉包中i的值,都是1。以此類推,直到第十個函式建立時,i === 9;但是在for迴圈的最後一步,又執行了 i++;所以i === 10。

明白了嗎?

想要改變這個問題,想要每個函式都輸出不同的數值,那就需要不讓每個函式都公用一個閉包——讓每個函式使用單獨的閉包資料,不共享。只需講for迴圈建立函式的部分修改為:

//迴圈建立函式
        for (; i < 10; i++) {
            events[i] = (function (index) {
                return function (index) {
                    console.log(index);
                }
            })(i);
        }

此時,i作為引數,傳入匿名自動執行函式,賦值給index引數。這個匿名函式的變數環境,就把index儲存了下來,而不會隨著i的變化而變化。這就是要點。

 

歡迎關注微博:weibo.com/madai01

 

相關文章