閉包是指有權存取另一個函數作用域的變數的函數。
–《javascript高階程式設計》
在这个函数fun2中可以访问另一个函数中的变量a,所以fun2()就是一个闭包。 function fun1 () { var a = 0; function fun2 () { console.log(a); } fun2(); }
1.在定義函數外呼叫閉包的方法,逃離方式:
(1).將內部函數指定給一個全域變數;
var globalVar; function outer() { console.log(‘outer’); function inner(){ console.log(‘inner’); } globalVar = inner; } outer(); // outer globalVar(); // inner;
在這個例子中inner()透過全域變數的參考成功逃離,現在可以在全域中調用,而且可以引用outer()的變數
(2).透過返回值來'營救'內部函數的引用
function outer() { console.log(‘outer’); function inner(){ console.log(‘inner’); } return inner; } var fn = outer(); // outer fn(); // inner;
在這個例子中inner()透過回傳值成功逃離,現在可以在全域中調用,而且可以引用outer()的變數
2.在函數外調用閉包的影響:增加記憶體佔用;
本來正常的情況下是函數調用結束之後函數的執行環境離開環境棧,定義的變數廢棄(廢棄與垃圾收集機制有關),活動變數(變數物件)會被銷毀,記憶體釋放。但是現在因為閉包的作用域鏈包含了外部函數的變數對象,外部函數的變數有可能再被引用,垃圾收集機制不會將外部函數的變數廢棄,在記憶體保留的外部函數的變數物件。這樣就加大了對記憶體的佔用。
3.閉包與變數的關係:閉包中常見的誤區及解決技巧
閉包保存的是包含函數的整個變數對象,所取得的外部物件變數為閉包被呼叫時刻的物件變量,一般為外部函數變數的最後一個值。
例:
function createFun() { var result = []; for ( var i = 0; i < 10; i++) { result[i] = function() { return i; }; } return result; } var result = createFun(); console.log(result5); // 10
這裡外部函數的回傳值為一個數組,數組值為不同函數(閉包)的引用,我們會誤以為每個閉包的呼叫的回傳值不同,但實際上每個函數都會傳回一樣的值。因為當閉包呼叫時,呼叫閉包的外部函數已經執行完畢,此時外部函數的變數物件中的i = 10,而我們閉包的回傳值為i,閉包會取得呼叫時的外部變數對象,此時的i為10。
解決:
function createFun() { var result = []; for ( var i = 0; i < 10; i++) { result[i] = function(num) { return function() { return num; }; }(i); } return result; } var result = createFun(); console.log(result5); // 5
在循環中,我們定義了一個匿名數組,並將立即執行該匿名函數的結果賦給數組,這裡的匿名函數有一個參數num,每次將i作為參數傳遞給num,每次循環num都會得到不同的值,所以每次返回了不同的函數(區別在於num值不同),當在外部調用數組值時,會返回不同的值,與預期相符。
4. 注意閉包中this值
首先,關於函數中this指向,我們應該知道this指向調用該函數的對象,若無明確調用對象則指向window對象。
在閉包中容易弄錯的this指向,例:
var name = “window”; var o = { name: “object”, getName: function() { return function() { return this.name; }; } }; console.log(o.getName()()); // window
可以看出閉包this指向了全局對象,分析,可以把o.getName ()()寫成(o.getName())(),這個表達式相當於第一步先執行了o.getName(),這個函數回傳了一個匿名函數(閉包),然後在全域下執行了這個閉包,並不是透過物件o調用,所以this指向全域物件。
5. 記憶體洩漏的問題,如何減少不必要的記憶體佔用
function assignHandler() { var ele = documnet.getElementById(“somenode”); ele.onclick = function() { console.log(ele.id); }; }
在上面的例子中定義ele的方法與匿名函數有關,於是ele保存了對匿名函數的引用,而閉包會引用包含函數也引用了ele對象,這樣就造成了對象的循環引用,ele元素(dom元素佔用內存較大)就一直保存在了內存中無法釋放,解決方法如下:
function assignHandler() { var ele = documnet.getElementById(“somenode”); var id = ele.id; ele.onclick = function() { console.log(id); // 通过id值中介表面上解除了与ele的循环引用 }; ele = null; // 手动解除引用 }
以上是什麼是閉包的詳細內容。更多資訊請關注PHP中文網其他相關文章!