2 回答

TA貢獻1878條經驗 獲得超4個贊
在 JavaScript 中,無論 g() 是否從 f() 內部調用,當 g() 引用表達式中的變量 x 時發(fā)生的事情實際上沒有區(qū)別。只有一個變量x,無論何時運行代碼,獲取它都是相同的內部操作g()。
JavaScript 與 C++ 有很大的不同;表面上的相似性可能具有欺騙性。此外,在討論JavaScript 語義時很少使用術語“捕獲”(根據我的經驗,例如在 Stack Overflow 上),盡管規(guī)范在其詳盡的描述中使用了它來描述進入范圍時發(fā)生的情況。這里的相關詞是閉包,如“x 在 g() 的閉包中。(我對術語很草率,所以有人可能會改進我的措辭。)
更多:注意,我們可以修改g()來演示,x仍然不僅可以訪問獲取其值,還可以修改:
function f() {
function g() { console.log(x = x + 1); }
let x = 0;
g(); // prints 1
x = 1;
g(); // prints 2
return g;
}
g = f();
g();
g();
g();
變量x
繼續(xù)表現得像普通變量一樣。

TA貢獻2016條經驗 獲得超9個贊
簡而言之
你幾乎是正確的,除了它超出范圍時它是如何工作的。
更多細節(jié)
如何在 JavaScript 中“捕獲”變量?
JavaScript 使用詞法環(huán)境來確定哪個函數使用哪個變量。詞法環(huán)境由環(huán)境記錄表示。在你的情況下:
有一個全球環(huán)境;
該函數
f()
定義了它的詞法環(huán)境,在其中x
被定義,即使它在 之后g()
;內部函數
g()
定義了它的詞法環(huán)境,它是空的。
所以g()
使用x
. 由于那里沒有綁定x
,JavaScriptx
在封閉環(huán)境中查找。由于它在其中找到,因此x
ing()
將使用 in 的x
綁定f()
。這看起來像詞法范圍綁定。
如果稍后您在調用x
的環(huán)境中定義一個,仍然會綁定到in :g()
g()
x
f()
function f() {
function g() { console.log(x); }
let x = 0;
g(); // prints 0
x = 1;
g(); // prints 1
return g;
}
let x = 4;
let g = f();
g(); // prints 1 (the last known value in f before returning)
這表明綁定是靜態(tài)的,并且將始終在定義x
的詞法范圍內引用已知的g()
。
這篇優(yōu)秀的文章用非常漂亮的圖形詳細解釋了它是如何工作的。它適用于閉包(即具有執(zhí)行上下文的匿名函數),但也適用于普通函數。
為什么超出范圍的變量的值會被保留?
如何解釋這種非常特殊的行為,即x
只要x
仍在范圍內,JavaScript 將始終采用當前值(如 C++ 中的引用),而在超出范圍時它將采用最后一個已知值x
(當超出范圍的引用時) C++ 將是 UB)?當變量消失時,JavaScript 是否將值復制到閉包中?不,比這更簡單!
這與垃圾收集有關:g()
返回到外部上下文。由于g()
使用了x
in f()
,垃圾收集器將意識到這個x
對象f()
仍在使用中。因此,只要g()
可訪問,x
inf()
就會保持活動狀態(tài),并保持其仍處于活動狀態(tài)的綁定可訪問。所以不需要復制值:x
對象將保持不變(未修改)。
作為不是拷貝的證明,你可以研究下面的代碼。f()
它在能夠更改(相同)的上下文中定義了第二個函數x
:
let h;
function f() {
function g() { console.log(x); }
h = function () { x = 27; }
let x = 0;
g(); // prints 0
x = 1;
g(); // prints 1
x = 3;
return g;
}
let x = 4;
let g = f();
g(); // prints 3
h();
g(); // prints 27
編輯:在稍微復雜的上下文中解釋這種現象的附加獎勵文章。有趣的是,它解釋說如果不采取預防措施,這種情況會導致內存泄漏。
添加回答
舉報