之前對閉包的理解就是函數(shù)嵌套,內層函數(shù)使用了外層函數(shù)的變量,然后外層函數(shù)執(zhí)行時返回內層函數(shù)。很繞的樣子。。。
最近在啃js基礎,才發(fā)現(xiàn)這只是表象;其實際是就如同“閉包”closure這個單詞的意思一樣,函數(shù)將變量close起來了; 《你不知道的Javascript(上卷)》里邊的閉包講的一針見血
當函數(shù)可以記住并訪問所在的詞法作用域時,就產生了閉包,即使函數(shù)是在當前詞法作用域之外執(zhí)行。
個人理解:
- 函數(shù)執(zhí)行依賴于作用域鏈,而函數(shù)申明的時候就確定了作用域范圍;
- 函數(shù)執(zhí)行的時候資源的查找是自下而上的,從自身的局部作用域往上層局部作用域(嵌套函數(shù)的外層)直至全局作用域上查找,如果函數(shù)內部包含作用域鏈上的資源,就產生了閉包。
- 被閉包起來的資源不管其是值類型還是引用類型,都是共享成為引用,下面的 經(jīng)典問題代碼 就是由于閉包引起的結果
- 生是定義時作用域鏈的人,死(執(zhí)行期)是定義時所處作用域鏈的鬼。
總之就是函數(shù)心心念它被定義時所在的詞法作用域
經(jīng)典代碼
var scope='global scope';
function checkscope(){
var scope='local scope';
function f(){ return scope;}
return f;
}
checkscope()(); // => local scope
經(jīng)典問題代碼
function constfuncs(){
var funcs=[];
for(var i=0; i< 10; i++){
funcs[i] = function(){return i;}
}
return funcs;
}
var funcs = constfuncs();
funcs[5](); // => 10 為毛?
funcs是在同一個函數(shù)調用中定義的,因此它們共享變量i, constfuncs返回的時候變量i的值是10。
解決上述問題
function constfuncs(){
var funcs=[];
for(var i=0; i< 10; i++){
//利用自調用函數(shù)為每個循環(huán)創(chuàng)建一層獨立的函數(shù)作用域
(function(j){
funcs[i] = function(){return j;}
})(i);
}
return funcs;
}
var funcs = constfuncs();
funcs[5](); // => 5
《Javascript權威指南》 (犀牛書)
函數(shù)對象可以通過作用域鏈相互關聯(lián)起來,函數(shù)體內部的變量都可以保存在函數(shù)作用域內,這種特性在計算機科學文獻中成為“閉包”
從技術的角度講,所有的Javascript函數(shù)都是閉包: 它們都是對象,都關聯(lián)到作用域鏈。
當調用函數(shù)時閉包所指向的作用域鏈和定義函數(shù)時的作用域鏈不是同一個作用域鏈時,事情就變得非常微妙。當一個函數(shù)嵌套在另一個函數(shù)內,外部函數(shù)把嵌套的函數(shù)對象作為返回值返回時往往會發(fā)生這種事情。這種編程模式在javascript中非常常見。
函數(shù)定義時的作用域到函數(shù)執(zhí)行時依然有效。
"閉包” 是指函數(shù)變量可以被隱藏于作用域鏈之內,因此看起來是函數(shù)將變量“包裹”起來了
--
其實C#中也有類似的閉包效果,比如函數(shù)內部返回一個委托
function Func<int> TestClosure(){
int count=0;
return new Func<int>(() => count++);
}
var fun= TestClosure();
fun(); // => 0
fun(); // => 1
其實C#的閉包會吧這個int類型的count包裝在一個匿名類中而變成引用類型給委托返回值共用;btw 在多線程編程的時候要特別注意閉包問題,因為多線程共享的資源即使是數(shù)值類型還最終也是會被clr以匿名類的形式弄成引用類型:
for(int i=0; i<5; i++)
{
/* 這個接受操作非常重要,如果省略 每個task的i都會是4 注意:js沒有c#這樣的塊級作用域, 不能簡單的在這里接受?。?!*/
int j=i;
TaskFactory.StartNew(()=>{
// do something with "i" through "j"
});
}