前端基本功(八): javascript的變量做用域與閉包

1. 變量的做用域

  1. 變量的做用域只有兩種: 全局變量與局部變量。
  2. javascript你能夠在函數內部直接讀取全局變量,函數外部沒法讀取函數內的局部變量。
  3. 若是想獲得函數內部的局部變量你能夠在函數內部再定義一個函數。javascript特有"鏈式做用域"結構。子對象會一級一級地向上尋找全部父對象的變量。因此,父對象的全部變量,對子對象都是可見的,反之則不成立。

2. 閉包

1. 閉包的定義(大多數認知)

JavaScript中的函數會造成閉包。 閉包是由函數以及建立該函數的詞法環境組合而成。這個環境包含了這個閉包建立時所能訪問的全部局部變量。(閉包就是可以讀取其餘函數內部變量的函數。在本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑。)javascript

2. 閉包實例
for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}
console.log(new Date, i);
/* 結果:第 1 個 5 直接輸出,1 秒以後,輸出 5 個 5 Tue Mar 26 2019 11:25:19 GMT+0800 (中國標準時間) 5 Tue Mar 26 2019 11:25:20 GMT+0800 (中國標準時間) 5 Tue Mar 26 2019 11:25:20 GMT+0800 (中國標準時間) 5 Tue Mar 26 2019 11:25:20 GMT+0800 (中國標準時間) 5 Tue Mar 26 2019 11:25:20 GMT+0800 (中國標準時間) 5 Tue Mar 26 2019 11:25:20 GMT+0800 (中國標準時間) 5 */

// 若是指望代碼的輸出變成:5 -> 0,1,2,3,4 改造方法
1. 方案1: 匿名函數
for (var i = 0; i < 5; i++) {
    (function(j) {  // j = i
        setTimeout(function() {
            console.log(new Date, j);
        }, 1000);
    })(i);
}
console.log(new Date, i);
2. 方案2: 塊級做用域
for (let i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}
複製代碼
3. 閉包的弊端

閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。html

4. 網上對閉包的定義不少,IIFE是否是閉包呢?

在requireJS出現以前,實現模塊化編程主要經過IIFE,而在IIFE中常見的操做就是經過window.fn = fn來暴露接口,而這個fn就是閉包,而IIFE只是一個包含閉包的函數調用前端

(function(){
    var a = 0;
    function fn(){
        console.log(a); 
    }
    window.fn = fn;
})()
fn();

// 定義一:須要經過做用域鏈查找變量的函數就是閉包
var a = 2;
(function foo(){
    console.log(a);//2
})();

// 定義二: 訪問上層函數的做用域的內層函數就是閉包
function foo(){
    var a = 2;
    function bar(){
        console.log(a); // 2
    }
    bar();
}
foo();

// 定義三: 在函數聲明時的做用域之外的地方調用函數,須要經過將該函數做爲返回值或者做爲參數被傳遞
function foo(){
    var a = 2;
    function bar(){
        console.log(a); //2
    }
    return bar;
}
foo()();
複製代碼
5. 我的認同:由於做用域鏈,全部函數都爲閉包。
  1. 從理論角度:全部的函數。由於它們都在建立的時候就將上層上下文的數據保存起來了。哪怕是簡單的全局變量也是如此,由於函數中訪問全局變量就至關因而在訪問自由變量,這個時候使用最外層的做用域。
  2. 從實踐角度:如下函數纔算是閉包:1. 即便建立它的上下文銷燬,仍然存在(好比內部函數從父函數返回) 2. 代碼中引用了自由變量(跨了本身的做用域的變量都叫自由變量)

參考文獻

  1. 學習javascript閉包
  2. 深刻理解javascript系列(16):閉包
  3. 破解前端面試:從閉包提及
相關文章
相關標籤/搜索