簡體   English   中英

了解v8中的JavaScript閉包變量捕獲

[英]Understanding javascript closure variable capture in v8

我了解閉包持有對變量的引用的語義,這延長了它的生命周期,使原始變量不受調用堆棧的限制,因此,應特別對待閉包捕獲的那些變量。

我還了解,可以根據當前的javascript引擎中的閉包是否捕獲相同范圍內的變量來進行不同的處理。 例如,

function foo(){
    var a=2;
    var b=new Array(a_very_big_number).join('+');
    return function(){
        console.log(a);
    };
}
var b=foo();

由於沒有人在foo保留對b的引用,因此無需將b保留在內存中,因此可以在foo返回時立即釋放使用的內存(甚至在進一步的優化下也不會創建)。

我的問題是,為什么v8似乎在每個調用上下文中將所有閉包引用的所有變量打包在一起? 例如,

function foo(){
    var a=0,b=1,c=2;
    var zig=function(){
        console.log(a);
    };
    var zag=function(){
        console.log(b);
    };
    return [zig,zag];
}

zigzag似乎都擁有對ab的引用,即使很顯然b對於zig都不可用。 b很大並且zig持續很長時間時,這可能會很糟糕。

但是從實現的角度來看,我不明白為什么這是必須的。 根據我的知識,無需調用eval即可在執行前確定作用域鏈,從而可以確定引用關系。 引擎應注意,當zig不再可用時,請執行a以便引擎將其標記為垃圾。

chrome和Firefox似乎都遵守規則。 標准是否說任何實現都必須這樣做? 還是這種實現更實用,更有效? 我很困惑。

主要障礙是可變性。 如果兩個閉包共享相同的var則它們必須以一種可見的方式在另一個閉包中進行更改。 因此,不可能像函數語言那樣將引用變量的值復制到每個閉包環境中(綁定是不可變的)。 您需要共享一個指向公共可變堆位置的指針。

現在,您可以將每個捕獲的變量分配為堆上的一個單獨的單元,而不是一個包含所有變量的數組。 但是,這在空間和時間上通常會更昂貴,因為您需要多個分配和兩個間接級別(每個閉包都指向其自己的閉包環境,該閉包環境指向每個共享的可變變量單元格)。 在當前的實現中,它僅是每個作用域的一種分配,並且是一種間接訪問變量(單個作用域內的所有閉包都指向同一可變變量數組)。 缺點是某些使用壽命比您預期的更長。 這是一個權衡。

其他考慮因素是實現的復雜性和可調試性。 借助eval類的可疑功能以及調試器可以檢查作用域鏈的期望,基於作用域的實現更加易於處理。

該標准沒有對垃圾回收進行任何說明,但提供了一些應該發生的線索。 參考: 標准

外部詞法環境當然可以具有自己的外部詞法環境。 詞法環境可以用作多個內部詞法環境的外部環境。 例如,如果一個函數聲明包含兩個嵌套的函數聲明,則每個嵌套函數的詞法環境將以周圍函數當前執行的詞法環境作為其外部詞法環境。”

Section 13 Function definition
  step 4: "Let closure be the result of creating a new Function object as specified in 13.2"

Section 13.2 "a Lexical Environment specified by Scope" (scope = closure)

Section 10.2 Lexical Environments:
"The outer reference of a (inner) Lexical Environment is a reference to the Lexical Environment that logically surrounds the inner Lexical Environment.

因此,一個函數將有權訪問父級的環境。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM