要理解閉包須要先理解變量做用域javascript
明確幾點:html
按照做用域區分,變量有全局變量和局部變量,因爲做用域鏈,函數內部是能夠直接讀取全局變量的,而函數外部沒法直接讀取函數內的局部變量。java
「函數」和「函數內部能訪問到的變量」(也叫環境)的總和,就是一個閉包。chrome
本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑
瀏覽器
來看一個栗子bash
function fn(){
// default
var n=66;
// get
function getN(){
return n;
}
// set
function setN(num){
n=num;
}
// add
function addN(){
n++;
}
return {
getN:getN,
setN:setN,
addN:addN
}
}
var test=fn();
console.log(test.getN()); //66
test.setN(666);
console.log(test.getN()); // 666
test.addN();
console.log(test.getN()); // 667
複製代碼
咱們經過fn暴露出來的接口訪問到了函數內部的n
,同時咱們對n
的值作了修改,能夠看到n依然是存在於內存中的。閉包
具體應用方面:函數
1)因爲閉包會使得函數中的變量都被保存在內存中,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。ui
2)閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數看成對象(object)使用,把閉包看成它的公用方法(Public Method),把內部變量看成它的私有屬性(private value),這時必定要當心,不要隨便改變父函數內部變量的值。this
讓咱們理論結合實(mian)踐(shi)來理解如下閉包 如下皆爲chrome瀏覽器環境運行的結果
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()()); // The Window
複製代碼
拆成兩步就很容易看懂
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()()); // My Object
複製代碼
咱們一樣分兩步
上級
做用域尋找that
,也就是getNameFunc裏面的that
,that=this
,此時的this指向調用getNameFunc
的對象object
。function foo(x) {
var tmp = 3;
function bar(y) {
console.log(x + y + (++tmp));
}
bar(10);
}
foo(2); //16
foo(2); //16
foo(2); //16
foo(2); //16
複製代碼
這裏只是函數調用哦,不是閉包哦
function foo(x) {
var tmp = 3;
return function (y) {
console.log(x + y + (++tmp));
}
}
var bar = foo(2);
bar(10); //16
bar(10); //17
bar(10); //18
bar(10); //19
複製代碼
當你return的是內部function時,就是一個閉包。
一個函數訪問了它的外部變量,那麼它就是一個閉包
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); // undefined
a.fun(1); // 0
a.fun(2); // 0
a.fun(3); // 0
複製代碼
這題有點繞,咱們來分解一下:
a = fun(0)
等價於執行後,console.log(o)
沒有在當前以及父級做用域中尋找到o,因此輸出的是undefined
,同時a被賦值爲{fun:function(m){return fun(m,0)}}
。a.fun(1)
執行,簡化後爲(function(1){return fun(1,0)})()
===fun(1,0)
,***當前做用域裏面沒有fun,最後找到了頂級的function fun(n,o)***,執行console.log(0)
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var b = fun(0).fun(1).fun(2).fun(3);
// undefined
// 0
// 1
// 2
複製代碼
這鏈式看的我好慌,咱們繼續分解一下
fun(0)
會輸出o
,此時沒有o因此爲undefind
,同時fun(0)表達式執行結果爲{fun:function(m){return fun(m,0)}}
(先忽略掉後面的fun(1)、fun(2)、fun(3))。fun(1)
等價於執行(function(1){return fun(1,0)})()
,繼續輸出o
,此時o爲0
,表達式簡化爲{fun:function(m){ return fun(m,1)}}