閉包(Closure)是JavaScript語言中一個非常重要的特性
在Javascript語言中,只有函數中的子函數才能引用函數中的變量,簡單來說,閉包就是定義在函數中的函數,是函數內外部連接的橋梁
閉包的意義是:當前作用域總是能夠訪問外部作用域中的變量;函數是唯一擁有自身作用域的結構,所以閉包的創建依賴於函數
變量的作用域
閉包是Javascript語言中的一個難點,理解閉包之前,先來理解一下變量的作用域
全局變量、局部變量是變量的作用域僅有的兩種形態;一般來說,全局變量可以在任意作用域中引用,而局部變量則只能在當前作用域中引用。先看如下代碼所示
var number = 1; var Get_Number = function () { console.log(number); }; Get_Number();
輸出結果為1;這是一個全局變量,可以在任意作用域中引用。再看如下代碼所示
var Get_Number = function () { var number = 1; }; console.log(number);
或
var Get_Number = function () { var number = 1; }; Get_Number(); console.log(number);
輸出結果都為ReferenceError: number is not defined;這是一個局部變量,無法在外部作用域中引用該變量,運行該函數后也不能;這里需要注意的是,局部變量的聲明必須使用var表達式,否則運行該函數后相當於聲明了一個全局變量。代碼如下所示
var Get_Number = function () { number = 1; }; Get_Number(); console.log(number);
輸出結果為1;這里實際上是聲明了一個全局變量
引用局部變量
正常來說,局部變量只能在函數中引用。先看如下代碼所示
var Get_Number = function () { var number = 1; var Out_Number = function () { console.log(number ++); }; Out_Number(); }; Get_Number();
輸出結果為1;子函數可以引用當前作用域中的變量,這實際上是JavaScript語言中的一個特色結構——作用域鏈(Scope Chain)。既然子函數可以引用該變量,那么我們return子函數,是不是就可以在外部作用域中引用該變量了。代碼如下所示
var Get_Number = function () { var number = 1; var Out_Number = function () { console.log(number ++); }; return Out_Number; }; var r = Get_Number(); r();
輸出結果為1;並且我們繼續運行r(),輸出值會遞增——2、3、4、5,這個值被存儲於內存中,這個Out_Number子函數正是我們要討論的閉包
閉包的使用
閉包的兩大作用,一個是讀取函數中的變量,另外一個是將函數中的變量的值存儲於內存中。先看如下代碼所示
var Get_Number = function () { var number = 1; return { plus: function () { number ++; }, out: function () { return number; } }; }; var r = Get_Number(); r.plus(); r.out();
返回值為2;2個閉包plus、out都維持着對Get_Number外部作用域的引用,在當前作用域中,只能通過這2個閉包訪問Get_Number外部作用域。代碼如下所示
var Get_Number = function () { var number = 1; return { plus: function () { number ++; }, out: function () { return number; } }; }; var r = Get_Number(); r.change = function () { number = 0; }; r.change(); r.plus(); r.out();
返回值同上;r.change並沒有改變Get_Number外部作用域中的變量number的值,它作用僅僅是聲明或者覆蓋了全局變量number