簡體   English   中英

這個JavaScript閉包是如何工作的?

[英]How does this JavaScript closure work?

這是一些JavaScript:

linkElem.click(function () {
    var data = linkElem.data();
    alert(''+data.mls + ' ' + data.id);
});

有用。

linkElem是我在函數內部循環中創建的局部變量。 我用jQuery的.data()為它分配了一些數據。 如果我沒有調用linkElem .click()linkElem在循環期間重新分配linkElem ,然后在函數返回后再循環。 但是,我創建了一個引用linkElem的匿名函數。 所以我不再確定發生了什么。

我的猜測是在循環期間創建的所有匿名函數和linkElem都被賦予某種UID並移動到持久/全局范圍。 它是否正確? 無償的細節將非常感激。

是的,你的描述非常接近。 Javascript函數調用的本地存儲只是為局部變量分配的內存塊。 如果你通過被調用函數中創建另一個函數來“捕獲”它,那么存儲將被保留,並且局部變量會繼續存在,並且不知道給它們生出的函數可能已經很久了。

重要的是要記住, 只有函數才能創建這樣的存儲 - 諸如括號封閉的循環體之類的東西不是單獨的存儲區域。 因此,常見的錯誤是在函數中聲明變量並在循環中創建的多個函數中重用它。 這本身並不是錯誤的,但效果可能會令人驚訝:

function whatever() {
  for (var i = 0; i < 3; ++i) {
    setTimeout(function() { alert(i); }, 5000);
  }
}

如果你運行它,你會看到三個警告都說“3”。 為什么? 因為它們都共享相同的“i”變量。 你可以通過引入另一個功能層來避免這種情況

function whatever() {
  for (var i = 0; i < 3; ++i) {
    setTimeout((function(private_i) { return function() { alert(private_i); }; })(i), 5000);
  }
}

“包裝器”功能就是提供一個局部變量(參數“private_i”),可以復制循環變量“i”。

請參考這個JavaScript閉包如何工作? 這可以幫助您理解閉包。

每當在另一個函數中看到function關鍵字時,內部函數就可以訪問外部函數中的變量。

function foo(x) {
  var tmp = 3;
  function bar(y) {
    alert(x + y + (++tmp));
  }
  bar(10);
}
foo(2)

這將始終警告16,因為bar可以訪問被定義為foo的參數的x ,並且它還可以從foo訪問tmp

一個關閉。 函數不必返回以便被稱為閉包。 只需訪問直接詞法范圍之外的變量就可以創建一個閉包

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + (++tmp));
  }
}
var bar = foo(2); // bar is now a closure.
bar(10);

上面的函數也會提醒16,因為bar仍然可以引用xtmp ,即使它不再直接在范圍內。

然而,由於tmp仍然懸掛在bar的封閉內,它也在增加。 每次調用bar時它都會遞增。

最簡單的閉包示例是:

var a = 10;
function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

調用Javascript函數時,將創建新的執行上下文。 與函數參數和父對象一起,此執行上下文還接收在其外部聲明的所有變量(在上面的示例中,“a”和“b”)。

可以通過返回它們的列表或將它們設置為全局變量來創建多個閉包函數。 所有這些都將引用相同的 x和相同的tmp ,它們不會制作自己的副本。

[你]:很有意思,告訴我更多!

這里的數字x是一個字面數字。 與JavaScript中的其他文字一樣,當調用foo時,數字x復制foo作為其參數x

另一方面,JavaScript在處理對象時總是使用引用。 如果說,你用一個Object調用了foo ,它返回的閉包將引用那個原始的Object!

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    alert(x.memb);
  }
}
var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

正如預期的那樣,每次調用bar(10)都會增加x.memb 可能沒有預料到的是, x只是指與age變量相同的對象! 經過幾次電話到barage.memb將是2! 此引用是HTML對象的內存泄漏的基礎。

但是,我創建了一個引用linkElem的匿名函數。 所以我不再確定發生了什么。

它仍然會被重新分配,除非你將它包裝在另一個范圍內(NB:另一個函數)。

考慮以下:

for (var j = 0;j < 10;j += 1) {
    arrayOfLinks[j].onclick = function () {
        alert(j);
    };
}

在這種情況下,所有這些鏈接在單擊時都會發出警報10 ,因為j在范圍之外並且正在更新。

如果您以相同的方式創建linkElem ,則很可能只獲得循環中最后一個 linkElem的結果。

這是一種更好的方法:

linkElem.click(function () {
    var data = $(this).data(); // no longer dependent on `linkElem` reference
    alert(''+data.mls + ' ' + data.id);
});

暫無
暫無

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

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