瞭解閉包

江湖上都說要了解閉包,得先了解做用域鏈,因此,先從做用域鏈開始吧。javascript

做用域鏈

  1. 做用域鏈是一個對象列表或鏈表,這組對象定義了這段代碼「做用域」中的變量;
  2. 每當調用一個函數,這個函數會建立一個新的對象來儲存它的變量(變量綁定對象),而且將這個對象添加到做用域鏈上;當函數返回時,就從做用域鏈中將這個對象刪除;
  3. 當javascript須要查找一個變量時,它會沿着做用域鏈一級一級地搜索變量。搜索過程始終從做用域鏈的前端開始,而後逐級地向後回溯,直至做用域鏈的最頂層(全局對象)爲止。
  4. 對於嵌套函數,每一個嵌套的函數都各自對應一個做用域鏈,而且這個做用域鏈都指向一個變量綁定對象

咱們來看一個栗子html

var word = " the window"
function sayWord(){
    var word = "sayWord"
     function sayHello(){
        var word = "sayHello"
        alert(word)
    }
    return sayHello
}
sayWord()();

上例的做用域鏈就是:
sayHello[word="sayHello"]——sayWord[word="sayWord"]——window[word="the window"];
當執行sayHello函數時,會沿着這個做用域鏈一級一級往上找word這個變量,直到找到爲止。前端

閉包

javacript高級程序設計上說「有很多開發人員老是搞不清匿名函數和閉包這兩個概念」。很遺憾,本人就是。java

@javacript高級程序設計git

閉包是指有權訪問另外一個函數做用域中的變量的函數。github

@xiaotie閉包

閉包是從用戶角度考慮的一種設計概念,它基於對上下文的分析,把齷齪的事情、複雜的事情和外部環境交互的事情都本身作了,留給用戶一個很天然的接口。函數

@javacript權威指南this

函數對象經過做用域鏈相互關聯起來,函數體內部的變量均可以保存在函數做用域內設計


上栗子

var word = " the window"
function sayWord(){
    var word = "sayWord"
     function sayHello(){
        alert(word)
    }
    return sayHello
}
sayWord()();

sayHello函數在sayWord函數內部,它能訪問sayWord函數內部的變量。sayHello函數就是閉包,

利用閉包實現私有屬性

function createCounter() {
  var counter = 0;
  function increment() {
    counter = counter + 1;
    console.log("Number of events: " + counter);
  }
  return increment;
}

var counter1 = createCounter();
var counter2 = createCounter();

counter1(); // Number of events: 1
counter1(); // Number of events: 2

counter2(); // Number of events: 1
 
counter1(); // Number of events: 3

每次調用函數都會建立變量綁定對象添加到做用域鏈中,因此每次調用外部函數的時候,做用域鏈都是不一樣的。而對於嵌套函數,每次調用外部函數時,內部函數又會從新定義一遍。

閉包存在的問題

this對象的指向問題

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        var name = "The v";
        return function(){
            return this.name;
        };
    }
};
alert(object.getNameFunc()()); //"The Window"(在非嚴格模式下)

每一個函數在被調用時都會自動取得兩個特殊變量:thisarguments。內部函數在搜索這兩個變量時,只會搜索到其活動對象爲止,所以永遠不可能直接訪問外部函數中的這兩個變量。

這裏

object.getNameFunc()()==(function(){return this.name;})()

因此其活動對象爲window;

解決辦法

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};
alert(object.getNameFunc()()); //"My Object"

內存泄露問題

function sayWord(){
    var word = "hello"
    add = function(){
        word = word + " world"
    }
    function sayHello(){
        alert(word)
    }
    return sayHello;
    word = null;
}
var say = sayWord();
say();  //hello
add();
say();  //hello world
  • 首先調用say();結果輸出hello
  • 而後調用add,add是個全局變量,因此能夠在外部調用,由於它又是閉包,因此能夠訪問到變量word,因此world=「hello world」;
  • 最後再調用say();發現結果輸出hello world;
    這說明函數sayWord中的局部變量word一直保存在內存中,並無在sayWord調用後被自動清除。產生這個問題主要是因爲匿名函數保存了一個對word的引用,因此它所佔用的內存就永遠不會被回收。

注意:由於閉包的這個特性,因此外部函數的變量是其內部全部閉包的共享值,所以,不能在閉包中隨意的改變外部函數的變量值,牽一髮而動全身。

若是以爲本文不錯的話,幫忙點擊下面的推薦哦
>>>>點擊閱讀原文

相關文章
相關標籤/搜索