摘要:在文檔中,閉包的定義是。假設是一個被創建的局部變量是內部函數一個閉包使用了父函數中聲明的變量通過以上的代碼塊來看,我們可以看到閉包實際上指的就是一個擁有外部環境變量的函數。
閉包
對于 JavaScript 的初學者來說,閉包的概念和應用都可以算的上是難點。 MDN 的 JavaScript 文檔對閉包的概念給出了準確的定義,也提供了簡單直觀的的實例,是一個非常好的學習材料。 這篇文章將從文檔出發,對閉包的知識點進行一個簡單的梳理。
閉包是什么首先,我們需要對閉包提供一個準確的定義。 在文檔中,閉包的定義是 "A closure is the combination of a function and the lexical environment within which that function was declared"。這個定義是很拗口的一句話。 詞法環境(lexical environment)這個描述對于初學者來說過于學術和抽象,我們只需要記住就好。真正理解定義最好方式就是通過實際的代碼。 假設:
function init() { var name = "Hello"; // name 是一個被 init 創建的局部變量 function displayName() { // displayName() 是內部函數,一個閉包 alert(name); // 使用了父函數中聲明的變量 } displayName(); } init();
通過以上的代碼塊來看,我們可以看到閉包實際上指的就是一個’擁有外部環境變量的函數‘。 在上面的例子中函數 displayName 調用了不屬于本身的外部變量 name,不管此 displayName 函數最終是否被返回,實際上由 name 和 displayName 組成的閉包已經形成。
function init() { var name = "Hello"; // name 是一個被 init 創建的局部變量 function displayName() { // displayName() 是內部函數,一個閉包 alert(name); // 使用了父函數中聲明的變量 } return displayName(); // 閉包被返回 } var fun = init(); fun();
我們再來看一下這一塊的新的代碼,唯一的區別在于這個代碼中 函數init 返回了一個函數 displayName()。也就是返回了一個閉包。通過這個返回的閉包,我們就可以訪問這個函數所相關聯的詞法環境或者說數據。本來應該被銷毀的 name 變量保留了下來,而且只能通過調用閉包的方式來訪問,這也就是私有性。
閉包的作用實際上在上一個例子中,我們已經看到了閉包的作用,閉包可以用來模擬私有變量和方法。 它讓函數和函數所操作的某些數據(環境)關聯了起來。
var makeCounter = function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } }; var Counter1 = makeCounter(); var Counter2 = makeCounter(); console.log(Counter1.value()); /* logs 0 */ Counter1.increment(); Counter1.increment(); console.log(Counter1.value()); /* logs 2 */ Counter1.decrement(); console.log(Counter1.value()); /* logs 1 */ console.log(Counter2.value()); /* logs 0 */
在上面的例子中 我們可以看到變量 privateCounter 和 函數 changeBy 作為下面三個函數共同的詞法環境形成了閉包。 在 makeCounter()執行之后, 本該消失的詞法環境被保留下來,只能通過返回的三個函數進行更改和訪問。這種行為模擬出了類似 JAVA 類中的私有變量和私有方法。
在循環中創建閉包:一個常見錯誤;在 ECMAScript 2015 引入 let 這個關鍵字之前,在循環中有一個常見的閉包創建問題。
Helpful notes will appear here
E-mail:
Name:
Age:
function showHelp(help) { document.getElementById("help").innerHTML = help; } function setupHelp() { var helpText = [ {"id": "email", "help": "Your e-mail address"}, {"id": "name", "help": "Your full name"}, {"id": "age", "help": "Your age (you must be over 16)"} ]; for (var i = 0; i < helpText.length; i++) { var item = helpText[i]; document.getElementById(item.id).onfocus = function() { showHelp(item.help); } } } setupHelp();
這段代碼的結果就是,無論你選擇哪一個輸入框,helper 信息永遠都會顯示 Your age (you must be over 16)"。 原因就在于在返回的三個閉包實際上共享了 item 這一個詞法環境,所以 helper 永遠只顯示為最后 age 的 helper。 這里就是閉包里另一個很重要的知識點,閉包只會捕獲自由變量的引用,所以當 item 指向的helpText值最后變為 age 時,三個閉包的中的 item 也都變成了 age。 根據這一點我們可以將代碼修改如下
function setupHelpAnonymous(){ var helpText = [ {"id": "email", "help": "Your e-mail address"}, {"id": "name", "help": "Your full name"}, {"id": "age", "help": "Your age (you must be over 16)"} ]; for(var i = 0; i < helpText.length; i++){ var item = helpText[i]; (function() { var item = helpText[i]; document.getElementById(item.id).onfocus = function() { showHelp(item.help); } })(); } } setupHelpAnonymous()
在上面的代碼片段中 我們使用了一個 IIFE (立即執行函數表達式) 對 item 這個引用進行了立刻求值。這樣我們就能得到想要的結果。而在ES6中的 ’塊級作用域‘ 也可以解決這個問題。
function showHelp(help) { document.getElementById("help").innerHTML = help; } function setupHelp() { var helpText = [ {"id": "email", "help": "Your e-mail address"}, {"id": "name", "help": "Your full name"}, {"id": "age", "help": "Your age (you must be over 16)"} ]; for (var i = 0; i < helpText.length; i++) { let item = helpText[i]; document.getElementById(item.id).onfocus = function() { showHelp(item.help); } } } setupHelp();
每一次循環,都有一個新的 item 被創建,三個閉包不再共享同一個詞法環境;相比匿名閉包的方式,也沒有創建多余的閉包。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/92988.html
摘要:也許最好的理解是閉包總是在進入某個函數的時候被創建,而局部變量是被加入到這個閉包中。在函數內部的函數的內部聲明函數是可以的可以獲得不止一個層級的閉包。 前言 總括 :這篇文章使用有效的javascript代碼向程序員們解釋了閉包,大牛和功能型程序員請自行忽略。 譯者 :文章寫在2006年,可直到翻譯的21小時之前作者還在完善這篇文章,在Stackoverflow的How do Java...
摘要:但是閉包也不是什么復雜到不可理解的東西,簡而言之,閉包就是閉包就是函數的局部變量集合,只是這些局部變量在函數返回后會繼續存在。可惜的是,并沒有提供相關的成員和方法來訪問閉包中的局部變量。 (收藏自 技術狂) 前言:還是一篇入門文章。Javascript中有幾個非常重要的語言特性——對象、原型繼承、閉包。其中閉包 對于那些使用傳統靜態語言C/C++的程序員來說是一個新的語言特性。本文將...
摘要:當在中調用匿名函數時,它們用的都是同一個閉包,而且在這個閉包中使用了和的當前值的值為因為循環已經結束,的值為。最好將閉包當作是一個函數的入口創建的,而局部變量是被添加進這個閉包的。 閉包不是魔法 這篇文章使用一些簡單的代碼例子來解釋JavaScript閉包的概念,即使新手也可以輕松參透閉包的含義。 其實只要理解了核心概念,閉包并不是那么的難于理解。但是,網上充斥了太多學術性的文章,對于...
摘要:深入系列第八篇,介紹理論上的閉包和實踐上的閉包,以及從作用域鏈的角度解析經典的閉包題。定義對閉包的定義為閉包是指那些能夠訪問自由變量的函數。 JavaScript深入系列第八篇,介紹理論上的閉包和實踐上的閉包,以及從作用域鏈的角度解析經典的閉包題。 定義 MDN 對閉包的定義為: 閉包是指那些能夠訪問自由變量的函數。 那什么是自由變量呢? 自由變量是指在函數中使用的,但既不是函數參數也...
摘要:一言以蔽之,閉包,你就得掌握。當函數記住并訪問所在的詞法作用域,閉包就產生了。所以閉包才會得以實現。從技術上講,這就是閉包。執行后,他的內部作用域并不會消失,函數依然保持有作用域的閉包。 網上總結閉包的文章已經爛大街了,不敢說筆者這篇文章多么多么xxx,只是個人理解總結。各位看官瞅瞅就好,大神還希望多多指正。此篇文章總結與《JavaScript忍者秘籍》 《你不知道的JavaScri...
摘要:但是,必須強調,閉包是一個運行期概念。通過原型鏈可以實現繼承,而與閉包相關的就是作用域鏈。常理來說,一個函數執行完畢,其執行環境的作用域鏈會被銷毀。所以此時,的作用域鏈雖然銷毀了,但是其活動對象仍在內存中。 學習Javascript閉包(Closure)javascript的閉包JavaScript 閉包深入理解(closure)理解 Javascript 的閉包JavaScript ...
閱讀 1543·2023-04-25 18:56
閱讀 1496·2021-09-29 09:34
閱讀 1716·2021-09-22 15:51
閱讀 3506·2021-09-14 18:03
閱讀 1168·2021-07-23 17:54
閱讀 2027·2019-08-29 18:38
閱讀 2908·2019-08-29 12:38
閱讀 618·2019-08-26 13:41