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