閉包closure

之前對閉包的理解就是函數(shù)嵌套,內層函數(shù)使用了外層函數(shù)的變量,然后外層函數(shù)執(zhí)行時返回內層函數(shù)。很繞的樣子。。。

最近在啃js基礎,才發(fā)現(xiàn)這只是表象;其實際是就如同“閉包”closure這個單詞的意思一樣,函數(shù)將變量close起來了; 《你不知道的Javascript(上卷)》里邊的閉包講的一針見血

當函數(shù)可以記住并訪問所在的詞法作用域時,就產生了閉包,即使函數(shù)是在當前詞法作用域之外執(zhí)行。

個人理解:

  1. 函數(shù)執(zhí)行依賴于作用域鏈,而函數(shù)申明的時候就確定了作用域范圍;
  2. 函數(shù)執(zhí)行的時候資源的查找是自下而上的,從自身的局部作用域往上層局部作用域(嵌套函數(shù)的外層)直至全局作用域上查找,如果函數(shù)內部包含作用域鏈上的資源,就產生了閉包。
  3. 被閉包起來的資源不管其是值類型還是引用類型,都是共享成為引用,下面的 經(jīng)典問題代碼 就是由于閉包引起的結果
  4. 生是定義時作用域鏈的人,死(執(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"
    });
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容