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