JavaScript 閉包那些事

moZLeo發表於2019-02-12

好吧那我接下來在重新記錄下閉包吧:

其他時效優質文章,歡迎 ❤️ Blog ?

專業名詞解釋:

在計算機中,閉包指引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在。

侷限自定義: 在Javascript 中子函式使用了其父函式或者外層函式的變數就產生了一個閉包。這時外層變數的值能被子函式使用且外層變數在子函式未銷燬之前一直被分配不會被釋放。

說源頭說起:

閉包的產生由於變數作用域鏈引起的(由詞法作用域導致)。

從JavaScript作用域說閉包:

在ES5及之前的語言規範中作用域分3種:

  • 全域性作用域
  • 區域性(函式作用域)
  • eval作用域。 [注意:沒有塊級作用域]

在函式中定義的變數,就屬於區域性作用域,且只對函式範圍內其他表示式可見。 而函式內部又可以使用父函式中的變數這就是由於作用域鏈,當JavaScript查詢與變數關聯的值時,會遵循一個查詢鏈。這個鏈是基於作用域的層次結構。 如下程式碼:

var a = "global variable"; 
( function () { 
	console.log(a); 
	var fn = function () { 
		var a = "local variable" 
		console.log(a);
	}	 
	fn(); 
})()

//輸出
//global variable
//local variable
複製程式碼

我們在window全域性物件下宣告瞭變數a,隨後呼叫了一個立即執行函式,其中向控制檯直接列印變數a,由於立即執行函式沒有宣告區域性變數a所以導致JavaScript向其作用域鏈繼續查詢接著就在window物件中找到a變數並列印出它的值"global variable"。接著這個立即執行函式宣告瞭一個區域性函式變數再呼叫它,在這個函式變數中首先宣告瞭一個區域性變數a然後在向控制檯輸出a得值。JavaScript在執行時由於在fn函式作用域內部查詢到了變數a就直接使用變數a的值所以列印出了local variable。

值得注意的是:

  1. 這裡其實有2個閉包環境一個是window物件和立即執行函式所建立的閉包;另一個是立即執行函式和其內部宣告的函式變數fn建立的閉包。
  2. 閉包其實就是個稱呼,重要的是在這種場景下內部函式可以呼叫外部函式的變數,而這正是因為詞法作用域鏈。
  3. 當閉包產生,在區域性函式未被釋放之前,被引用外部函式的變數就無法被釋放。

清楚作用域的含義了嗎???

那麼我們剛剛說的“詞法“作用域又是什麼。 其實詞法就是指代環境:由於函式決定作用域,並且函式是一等公民可以直接用來引數傳遞等。那麼作用域鏈是怎樣來確定的呢: 下面的話背熟了: 作用域鏈是根據函式定義時候的位置確定的而不是在呼叫時。--這就是“詞法”作用域

如果你還不懂閉包我TM。。。

栗子?:

  • [閉包的影響]對一些li繫結點選事件並列印其索引,對比2斷程式碼不解釋:
var liListlength = 3; 
for(var i=0;i<liListlength;i++){ 
	var ele=document.querySelectorAll(".test > li")[i]; 
	ele.addEventListener("click",function(){ 
		alert("index is :" + i); 
	})		 
}

var liListlength = 3; 
for(var i=0;i<liListlength;i++){ 
	var ele=document.querySelectorAll(".test > li")[i]; 
	ele.addEventListener("click",(function(i){ 
		return function(){ 
			alert("index is :" + i); 
		} 
	})(i))	 
}
複製程式碼
  • [閉包的妙用]模擬封裝 我們可以運用閉包模擬模組的實現,即我們可以只暴露方法介面隱藏區域性變數,具體如下:
var countMoudle = (function(){ 
    var _count = 0; 
    
    var plus = function(){ 
        _count++; 
    }; 
    
    var minus = function(){ 
        _count--; 
    }; 
    
    var print = function(){ 
    	console.log(_count); 
    } 
    
    return { 
        plus: plus, 
        minus: minus, 
        print: print 
    }; 
})();

countMoudle.print()  //0
countMoudle.plus()   
countMoudle.print()  //1
countMoudle.minus()
countMoudle.print()  //0
複製程式碼

此時我們只暴露出了方法名而沒有暴露出變數屬性,這時要對變數的修改只有通過介面方法呼叫。

相關文章