javascript - 瀏覽TOM大叔部落格的學習筆記

來源:互聯網
上載者:User

javascript - 瀏覽TOM大叔部落格的學習筆記

 

part1 ---------------------------------------------------------------------------------------------------------

 

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

 

part2-----------------------------------------------------------------------------------------------------------

 

1. 前言

 

昨天寫了《js便簽筆記(11)——瀏覽TOM大叔部落格的學習筆記 part1》,簡單記錄了幾個問題。part1的重點還是在於最後那個迴圈建立函數的問題,也就是多個子函數公用一個閉包資料的問題。如果覺得有興趣,可以再重新翻出來看看。

今天繼續把剩下的問題寫完。

2. 範圍鏈

學js的人,即使初級入門的也都知道“原型鏈”,但是“範圍鏈”,可能好多人沒有聽說過。大部分人都知道或者聽說過“閉包”,但是可能有好多人不知道閉包其實和範圍鏈有莫大的聯絡。如果理解閉包不從範圍鏈開始理解,那麼你就只能理解閉包的皮毛。

我也是從TOM大叔的這些部落格中才瞭解到範圍鏈的,之前也看過了許多本書籍,都沒有很清晰的展開範圍鏈這個概念。其實範圍鏈簡單說來也好理解,如下代碼:

        var x = 10;        function fn() {            var y = 20;            return function () {                var z = 30                console.log(x + y + z);            }        }

上面代碼中,如果想要列印 x+y+z 的值,就必須要遍曆三個層次的上下文環境或者範圍,這其實和原型鏈的結構表現形式類似。但要細細將來,連同閉包圖文並茂的說明白,需要很多內容。

此處不再深入進去,以後有機會再另起一篇詳細介紹。

3. 二維鏈尋找

上文講到通過範圍練向上尋找變數,實際在尋找變數的過程中,是使用“二維鏈尋找”——“範圍鏈” + “原型鏈”。看如下代碼:

       Object.prototype.x = 10;        function fn() {            var y = 20;            return function () {                var z = 30                console.log(x + y + z);            }        }

這份代碼跟上文中示範範圍鏈的代碼差不多,但是它卻通過 Object.prototype.x = 10; 這麼一句話,表現出了原型鏈在其中的作用。
因此,在尋找變數值時,是同時兼顧原型鏈和範圍鏈兩個方向的,即“二維鏈尋找”。

4. 獨立範圍只能通過函數來建立

這句話的下半句是——不能通過if/for等語句塊來建立。後半句大家可能知道,但是它的本質確實前半句——獨立範圍只能通過函數來建立(除了獨立範圍之外,剩下的就是全域範圍)。既然獨立範圍只能通過函數來建立,那麼函數中任何地方的自由變數就都是函數層級的,因此,以下代碼希望不要再次出現:

5. 隱式全域變數的本質
var a = 10;b = 20;

以上兩句代碼,看似都是聲明兩個全域變數,但是按照TOM大叔說的,只有var才能聲明一個變數,也就是 var a = 10; 是真正的聲明變數。

而下一句 b = 20,其實是相當於設定window的一個屬性值而已。

因此,第一句的本質是聲明一個全域變數;第二句的本質是設定window的一個屬性值。

當然,不推薦用第二句的形式。

6. 函式宣告和函數運算式的不同

js定義函數的方法有多種,但看看以下這段代碼:

fn();var fn = function() {  //函數運算式    alert(123); // 報錯}//------fn();function fn() {  //函式宣告    alert(123); // 123}

兩種函數定義方式,卻得出不一樣的結果。

此處我當時沒有詳細看,因為這樣使用的情況不是很多,所以就沒有過深入的細看,只是做了個標記。如果有瞭解的朋友,不放解釋一下。

7.js使用靜態範圍

在part1中講過,當一個函數作為參數被傳入,後者作為一個值被返回的時候,連同它一塊被傳遞的,是它的範圍。也就是咱們常說的閉包。且看如下代碼:

var x = 10;            function foo() {    alert(x);}(function (funarg) {    var x = 20;    funarg();    // 10, 不是20})(foo); 

foo是一個函數,把它作為參數傳入進另一個函數中執行,連同一起傳遞的,是foo的範圍。而foo使用的是靜態範圍,其中的變數x在傳遞的時候已經被靜態賦值,不會受其他環境下x變數的影響。
這個道理也同樣適用於函數作為傳回值。如下:

function fn() {    var x = 10;    return function () {        alert(x);    }}var ret = fn();var x = 20;ret();   // 10,不是20

 

 


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.