摘要:下面這個(gè)例子就是閉包,函數(shù)能夠訪問(wèn)到不在其代碼塊里的變量。然而事實(shí)恰恰相反,唯一的解釋就是是一個(gè)閉包。性能問(wèn)題執(zhí)行一次,就會(huì)重新構(gòu)造兩個(gè)函數(shù)。正確的做法應(yīng)該是參考資料深入理解閉包學(xué)習(xí)閉包阮一峰
概念
閉包(closure)是一個(gè)擁有任意變量以及綁定這些變量的環(huán)境(environment)的表達(dá)式(一般來(lái)說(shuō)是就是function)
A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).
在作用域內(nèi)且在function定義時(shí)被訪問(wèn)的變量,那么這個(gè)變量就一直能夠被那個(gè)function訪問(wèn)。
variables that are in scope and accessed from a function declaration will stay accessible by that function.
下面這個(gè)例子就是閉包,displayName函數(shù)能夠訪問(wèn)到不在其代碼塊里的name變量。
function init() { var name = "Mozilla"; function displayName() { alert(name); } displayName(); } init();函數(shù)的作用域 functional scoping
一個(gè)變量的作用域是以其所在的源代碼的位置來(lái)定義的,嵌套在里面的function可以訪問(wèn)到聲明在外層作用域的變量
The scope of a variable is defined by its location within the source code, and nested functions have access to variables declared in their outer scope.
還是拿剛才那個(gè)例子來(lái)說(shuō),displayName函數(shù)是嵌套在init函數(shù)里的,所以它能夠訪問(wèn)到init函數(shù)里的變量
function init() { var name = "Mozilla"; function displayName() { alert(name); } displayName(); } init();閉包的組成
先看一下這個(gè)例子:
function makeFunc() { var name = "Mozilla"; function displayName() { alert(name); } return displayName; } var myFunc = makeFunc(); myFunc();
按照java或C++的經(jīng)驗(yàn),局部變量name的生命周期在函數(shù)的執(zhí)行后就結(jié)束了,所以會(huì)推斷name在makeFunc()訪問(wèn)后應(yīng)該就訪問(wèn)不到了。
然而事實(shí)恰恰相反,唯一的解釋就是myFunc是一個(gè)閉包(closure)。
閉包由兩部分組成:
function
創(chuàng)建該function的環(huán)境(創(chuàng)建閉包時(shí),作用域內(nèi)的所有局部變量)
對(duì)應(yīng)到上面的這個(gè)例子里:
function: displayName
環(huán)境:name="Mozilla"
再看一個(gè)例子:
function makeAdder(x) { return function(y) { return x + y; }; } var add5 = makeAdder(5); var add10 = makeAdder(10); alert(add5(2)); // 7 alert(add10(2)); // 12
這個(gè)例子說(shuō)明閉包的function可以是相同的,但是環(huán)境可以是不同的,因此就會(huì)有不同的結(jié)果。
歸納因此可以將閉包歸納為:
定義時(shí),確定可訪問(wèn)變量
執(zhí)行時(shí),確定變量的值
常見(jiàn)錯(cuò)誤下面這段代碼實(shí)際上執(zhí)行的時(shí)候并不是alert 0,1,2,3,4,而是alert 5次5。
這是為什么?因?yàn)閕變量在for循環(huán)后變成了5,而在執(zhí)行的時(shí)候我們才會(huì)確定閉包里i的值,在定義的時(shí)候不會(huì)記住i的值是什么的。
var funcs = []; for(var i=0; i < 5; i++) { funcs[i] = function() { alert(i); } } for(var j=0; j < funcs.length; j++) { funcs[j](); }
正確的寫(xiě)法是:
var funcs = []; function makeFunc(x) { return function() { alert(x); } } for(var i=0; i < 5; i++) { funcs[i] = makeFunc(i) } for(var j=0; j < funcs.length; j++) { funcs[j](); }閉包實(shí)踐 函數(shù)工廠
function makeSizer(size) { return function() { document.body.style.fontSize = size + "px"; }; } var size12 = makeSizer(12); var size14 = makeSizer(14); var size16 = makeSizer(16);私有變量
var Counter = (function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } })(); alert(Counter.value()); /* Alerts 0 */ Counter.increment(); Counter.increment(); alert(Counter.value()); /* Alerts 2 */ Counter.decrement(); alert(Counter.value()); /* Alerts 1 */
在這個(gè)例子里:
外界不能訪問(wèn): privateCounter,changeBy
外界間接訪問(wèn): increment,decrement,value
私有變量+函數(shù)工廠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(); alert(Counter1.value()); /* Alerts 0 */ Counter1.increment(); Counter1.increment(); alert(Counter1.value()); /* Alerts 2 */ Counter1.decrement(); alert(Counter1.value()); /* Alerts 1 */ alert(Counter2.value()); /* Alerts 0 */
Counter1和Counter2綁定的環(huán)境相互獨(dú)立。
性能問(wèn)題function MyObject(name, message) { this.name = name.toString(); this.message = message.toString(); this.getName = function() { return this.name; }; this.getMessage = function() { return this.message; }; }
執(zhí)行一次,就會(huì)重新構(gòu)造兩個(gè)函數(shù)。
正確的做法應(yīng)該是:
function MyObject(name, message) { this.name = name.toString(); this.message = message.toString(); } MyObject.prototype = { getName: function() { return this.name; }, getMessage: function() { return this.message; } }; function MyObject(name, message) { this.name = name.toString(); this.message = message.toString(); } MyObject.prototype.getName = function() { return this.name; }; MyObject.prototype.getMessage = function() { return this.message; };參考資料
Closures - MDN
Explaning Javascript Scope and closures
深入理解JavaScript閉包(closure)
學(xué)習(xí)Javascript閉包(Closure) - 阮一峰
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/90878.html
摘要:最近剛剛看完了你不知道的上卷,對(duì)有了更進(jìn)一步的了解。你不知道的上卷由兩部分組成,第一部分是作用域和閉包,第二部分是和對(duì)象原型。附錄詞法這一章并沒(méi)有說(shuō)明機(jī)制,只是介紹了中的箭頭函數(shù)引入的行為詞法。第章混合對(duì)象類類理論類的機(jī)制類的繼承混入。 最近剛剛看完了《你不知道的 JavaScript》上卷,對(duì) JavaScript 有了更進(jìn)一步的了解。 《你不知道的 JavaScript》上卷由兩部...
摘要:使用上一篇文章的例子來(lái)說(shuō)明下自由變量進(jìn)階期深入淺出圖解作用域鏈和閉包訪問(wèn)外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第7天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)階計(jì)...
摘要:在編程的世界里有兩種基本類型的編程函數(shù)式編程強(qiáng)調(diào)將一系列的動(dòng)作組合成一個(gè)體系對(duì)象式編程強(qiáng)調(diào)將一系列的成分聚合到一個(gè)類中對(duì)于這種弱類語(yǔ)言來(lái)說(shuō),它既有的特點(diǎn)通過(guò)或者封裝一個(gè)類又有的特點(diǎn)。 在編程的世界里有兩種基本類型的編程:函數(shù)式編程(OFP):強(qiáng)調(diào)將一系列的動(dòng)作組合成一個(gè)體系;對(duì)象式編程(OOP):強(qiáng)調(diào)將一系列的成分聚合到一個(gè)類中;對(duì)于javascript這種弱類語(yǔ)言來(lái)說(shuō),它既有OOP的...
摘要:插件開(kāi)發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實(shí)現(xiàn)文件分片斷點(diǎn)續(xù)傳。 Vue.js 插件開(kāi)發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。插....
摘要:對(duì)于采用面向堆棧模型來(lái)存儲(chǔ)局部變量的系統(tǒng)而言,就意味著當(dāng)函數(shù)調(diào)用結(jié)束后,其局部變量都會(huì)從堆棧中移除。因?yàn)樗彩呛瘮?shù)的局部變量,也會(huì)隨著的返回而移除。 概要 本文將介紹一個(gè)在JavaScript經(jīng)常會(huì)拿來(lái)討論的話題 —— 閉包(closure)。閉包其實(shí)已經(jīng)是個(gè)老生常談的話題了; 有大量文章都介紹過(guò)閉包的內(nèi)容(其中不失一些很好的文章,比如,擴(kuò)展閱讀中Richard Cornford的文...
閱讀 3201·2021-11-25 09:43
閱讀 3206·2021-11-23 09:51
閱讀 3519·2019-08-30 13:08
閱讀 1569·2019-08-29 12:48
閱讀 3594·2019-08-29 12:26
閱讀 397·2019-08-28 18:16
閱讀 2562·2019-08-26 13:45
閱讀 2429·2019-08-26 12:15