摘要:所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。所以本文中將以維基百科中的定義為準(zhǔn)即在計(jì)算機(jī)科學(xué)中,閉包,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。
閉包(closure)是JavaScript中一個(gè)“神秘”的概念,許多人都對(duì)它難以理解,我也一直處于似懂非懂的狀態(tài),前幾天深入了解了一下執(zhí)行環(huán)境以及作用域鏈,可戳查看詳情,而閉包與作用域及作用域鏈的關(guān)系密不可分,所以就再深入去理解了一番。
詞法作用域Lexical Scope首先我們來理解一下作用域的概念:
通常來說,一段程序代碼中所用到的標(biāo)識(shí)符并不總是有效/可用的,而限定這個(gè)標(biāo)識(shí)符的可用性的代碼范圍就是這個(gè)標(biāo)識(shí)符的作用域
作用域有詞法作用域與動(dòng)態(tài)作用域之分,詞法作用域也可稱為靜態(tài)作用域,這樣與動(dòng)態(tài)作用域看起來更對(duì)應(yīng)。
詞法作用域在詞法分析階段就確定了作用域,之后不會(huì)再改變;也就是說詞法作用域是由你把代碼寫在哪里來決定的,與之后的運(yùn)行情況無(wú)關(guān)
動(dòng)態(tài)作用域在運(yùn)行時(shí)根據(jù)程序的流程信息來動(dòng)態(tài)確定作用域;也就是說動(dòng)態(tài)作用域與運(yùn)行情況有關(guān)
大部分編程語(yǔ)言都是基于詞法作用域,其中包括JavaScript
下面我們使用代碼來說明兩者的區(qū)別(此處僅僅使用JavaScript來說明兩種情況,實(shí)際上JavaScript只基于詞法作用域)
var cc = 6; function foo() { console.log(cc); // 會(huì)輸出6還是66? } function bar() { var cc = 66; foo(); } bar();
如果是詞法作用域:會(huì)輸出6,詞法作用域在寫代碼時(shí)就靜態(tài)確定了,也就是定義foo函數(shù)的時(shí)候就確定了,foo函數(shù)的內(nèi)部要訪問變量cc,由于foo的內(nèi)部作用域中沒有cc變量,所以會(huì)根據(jù)作用域鏈訪問到全局中的cc變量;這與在何處調(diào)用foo函數(shù)無(wú)關(guān)。
如果是動(dòng)態(tài)作用域:會(huì)輸出66,動(dòng)態(tài)作用域要根據(jù)代碼的運(yùn)行情況來確定,它關(guān)心foo函數(shù)在何處被調(diào)用,而不關(guān)心它定義在哪里;foo函數(shù)的內(nèi)部要訪問變量cc,而foo的內(nèi)部作用域中沒有cc變量時(shí),會(huì)順著調(diào)用棧在調(diào)用 foo() 的地方查找變量cc,此處是在bar函數(shù)中調(diào)用的,所以引擎會(huì)在bar的內(nèi)部作用域中查找cc變量,這個(gè)cc變量的值為66
詞法作用域鏈Lexical Scope Chainvar cc = 1; function foo() { var dd = 2; console.log(cc);//1 console.log(dd);//2 } foo(); console.log(dd); //ReferenceError: dd is not defined
上面這一段代碼中,有全局變量cc以及局部變量dd,在foo函數(shù)內(nèi)部可以直接訪問全局變量cc,而在foo函數(shù)外部無(wú)法讀取foo函數(shù)內(nèi)的局部變量dd。
這種結(jié)果的產(chǎn)生源于JavaScript的作用域鏈,也正是因?yàn)檫@個(gè)作用域鏈才有了生成閉包的可能。
作用域鏈這一部分在另一篇文章中有詳細(xì)介紹,可戳JavaScript基礎(chǔ)系列---執(zhí)行環(huán)境與作用域鏈,看完可以幫助更好的理解下文
關(guān)于閉包沒有一個(gè)官方的定義,不同的書籍解讀可能有些不同
在《JavaScript權(quán)威指南》中:
是指函數(shù)變量可以被隱藏于作用域鏈之內(nèi),因此看起來是函數(shù)將變量“包裹”了起來
在《JavaScript高級(jí)程序設(shè)計(jì)》中:
閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)
在《你不知道的JavaScript--上卷》中:
當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時(shí),就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用
域之外執(zhí)行
在維基百科的定義:
在計(jì)算機(jī)科學(xué)中,閉包(Closure),又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures),是引用了自由變量的函數(shù)。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。閉包在運(yùn)行時(shí)可以有多個(gè)實(shí)例,不同的引用環(huán)境和相同的函數(shù)組合可以產(chǎn)生不同的實(shí)例。
其中自由變量指:
在函數(shù)中使用的,但既不是函數(shù)參數(shù)也不是函數(shù)的局部變量的變量
一開始我也一直糾結(jié)于閉包的定義,想確切的知道閉包是什么,但是由于沒有官方的定義,難以確定。所以本文中將以維基百科中的定義為準(zhǔn)即:
在計(jì)算機(jī)科學(xué)中,閉包(Closure),又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures),是引用了自由變量的函數(shù)。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。閉包的創(chuàng)建
根據(jù)閉包的定義我們可以看出,閉包的產(chǎn)生條件是函數(shù)以及該函數(shù)引用了自由變量,二者缺一不可。
而這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外這一描述是閉包的特性,使用閉包后能觀察到的一種現(xiàn)象,而不是閉包產(chǎn)生的條件。所以之前看到有些人說,需要將一個(gè)函數(shù)的內(nèi)部函數(shù)返回才能算閉包的言論我覺得應(yīng)該是不正確的,這應(yīng)該是在使用閉包。
常說的閉包會(huì)導(dǎo)致性能問題,也是因?yàn)?strong>這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外這一閉包特性,按理來說,在函數(shù) 執(zhí)行后,函數(shù)的整個(gè)內(nèi)部作用域通常都會(huì)被銷毀,因?yàn)槲覀冎酪嬗欣?br>圾回收器用來釋放不再使用的內(nèi)存空間,但是閉包可以阻止這件事的發(fā)生,從而可能導(dǎo)致內(nèi)存中保存大量的變量,從而消耗大量?jī)?nèi)存產(chǎn)生網(wǎng)頁(yè)性能問題。(注意是可以,可能而非一定)
下面我們直接來看幾個(gè)栗子:
1.如果考慮全局對(duì)象,那么引用了全局變量的函數(shù)可以看做創(chuàng)建了閉包,因?yàn)槿肿兞肯鄬?duì)于該函數(shù)來說是自由變量
var a = 1; function fa() { console.log(a); } fa();
此處,函數(shù)fa引用了自由變量a,fa創(chuàng)建了閉包
2.更常見的是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)
function outer(){ var b = 2; function inner(){ console.log(b); } inner(); } outer();
此處,函數(shù)inner引用了自由變量b,inner創(chuàng)建了閉包。
根據(jù)JavaScript基礎(chǔ)系列---執(zhí)行環(huán)境與作用域鏈中的描述我們可以知道,調(diào)用outer()后,會(huì)進(jìn)入Function Execution Context outer的創(chuàng)建階段:
創(chuàng)建作用域鏈,outer函數(shù)的[[Scopes]]屬性被加入其中
創(chuàng)建outer函數(shù)的活動(dòng)對(duì)象AO(作為該Function Execution Context的變量對(duì)象VO),并將創(chuàng)建的這個(gè)活動(dòng)對(duì)象AO加到作用域鏈的最前端
確定this的值
此時(shí)Function Execution Context outer可表示為:
outerEC = { scopeChain: { pointer to outerEC.VO, outer.[[Scopes]] }, VO: { arguments: { length: 0 }, b: 2, inner: pointer to function inner(), }, this: { ... } }
接著進(jìn)入Function Execution Context outer的執(zhí)行階段:
當(dāng)遇到inner函數(shù)定義語(yǔ)句,進(jìn)入inner函數(shù)的定義階段,inner的[[Scopes]]屬性被確定
inner.[[Scopes]] = { pointer to outerEC.VO, pointer to globalEC.VO }
遇到inner()調(diào)用語(yǔ)句,進(jìn)入inner函數(shù)調(diào)用階段,此時(shí)進(jìn)入Function Execution Context inner的創(chuàng)建階段:
創(chuàng)建作用域鏈,inner函數(shù)的[[Scopes]]屬性被加入其中
創(chuàng)建inner函數(shù)的活動(dòng)對(duì)象AO(作為該Function Execution Context的變量對(duì)象VO),并將創(chuàng)建的這個(gè)活動(dòng)對(duì)象AO加到作用域鏈的最前端
確定this的值
此時(shí)Function Execution Context inner可表示為:
innerEC = { scopeChain: { pointer to innerEC.VO, inner.[[Scopes]] }, VO: { arguments: { length: 0 }, }, this: { ... } }
接著進(jìn)入Function Execution Context inner的執(zhí)行階段:遇到打印語(yǔ)句console.log(b);,通過inner.[[Scopes]]訪問到變量b=2
至此,函數(shù)inner執(zhí)行完畢,Function Execution Context inner的作用域鏈及變量對(duì)象被銷毀
然后函數(shù)outer也執(zhí)行完畢,Function Execution Context outer的作用域鏈及變量對(duì)象被銷毀。
這種情況下,函數(shù)執(zhí)行完畢后該銷毀的都被銷毀了,沒有占用內(nèi)存,所以這種情況下閉包是不會(huì)對(duì)性能有占用內(nèi)存方面的影響的。
3.最常被討論的閉包
栗子1
function fa(){ var n = 666; function fb(){ console.log(n); } return fb; } var getN = fa(); getN();
此處,函數(shù)fb引用了自由變量n,fb創(chuàng)建了閉包,并且fb被傳遞到了創(chuàng)造它的環(huán)境以外(所在的詞法作用域以外)。
這段代碼的執(zhí)行情況與上面類似,鑒于篇幅就不一一展開詳細(xì)描述了,大家可以自己推一遍;現(xiàn)在主要描述一下不同之處,在fa函數(shù)的最后,fa函數(shù)將它的內(nèi)部函數(shù)fb返回了,按理說返回之后fa函數(shù)就執(zhí)行完畢了,其作用域鏈和活動(dòng)對(duì)象應(yīng)該被銷毀,但是閉包fb阻止了這件事的發(fā)生:
函數(shù)fb定義之后其[[Scopes]]屬性被確定,這個(gè)屬性至此之后一直保持不變,直至函數(shù)fb被銷毀,可以表示為
fb.[[Scopes]] = { pointer to fa.VO, pointer to globalEC.VO }
函數(shù)fa執(zhí)行完畢后,將其返回值--fb函數(shù)賦給了全局變量getN,這樣一來由于getN是全局變量,而全局變量是在Global Execution Context中的,需要等到應(yīng)用程序退出后 —— 如關(guān)閉網(wǎng)頁(yè)或?yàn)g覽器 —— 才會(huì)被銷毀,那么也就意味著fb函數(shù)也要到這時(shí)才會(huì)被銷毀
fb函數(shù)的[[Scopes]]屬性中引用了fa函數(shù)的變量(活動(dòng))對(duì)象,意味著fa函數(shù)的變量(活動(dòng))對(duì)象可能隨時(shí)還需要用到,這樣一來fa函數(shù)執(zhí)行完畢之后,只有Function Execution Context fa的作用域鏈會(huì)被銷毀,而變量(活動(dòng))對(duì)象仍然會(huì)在內(nèi)存中
這樣遇到getN()語(yǔ)句時(shí),實(shí)際上就是調(diào)用fb函數(shù),于是順著fb的作用域鏈找到變量n并打印出來
這里我們分析一下,變量n是閉包fb引用的自由變量,創(chuàng)造這個(gè)n這個(gè)自由變量的是函數(shù)fa,此時(shí)fa執(zhí)行完畢之后,自由變量n仍然可以訪問到(仍然存在),并且在fa函數(shù)外也能訪問到(離開fa之后)。這一點(diǎn)也就正對(duì)應(yīng)于這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外
除了將內(nèi)部函數(shù)return這種方式之外,還有其他方式可以使用閉包,這些方式的共同之處是:將內(nèi)部函數(shù)傳遞到創(chuàng)造它的環(huán)境以外(所在的詞法作用域以外),之后無(wú)論在何處執(zhí)行這個(gè)函數(shù)就都會(huì)使用閉包。
栗子2
function foo() { var a = 2; function baz() { console.log( a ); // 2 } bar( baz ); } function bar(fn) { fn(); } foo();
這個(gè)栗子中,是通過函數(shù)傳參來將內(nèi)部函數(shù)baz傳遞到它所在的詞法作用域以外的
栗子3
var fn; function foo() { var a = 2; function baz() { console.log( a ); } fn = baz; // 將baz 賦給全局變量 } foo(); fn(); // 2
這個(gè)栗子中,是通過賦值給全局變量fn來將內(nèi)部函數(shù)baz傳遞到它所在的詞法作用域以外的。
在栗子1和栗子3這種情況下呢,閉包使得它自己的變量對(duì)象以及包含它的函數(shù)的變量對(duì)象都存在于內(nèi)存中,如果濫用就很有可能導(dǎo)致性能問題。所以在不需要閉包后,最好主動(dòng)解除對(duì)閉包的引用,告訴垃圾回收機(jī)制將其清除,比如在上面這些例子中進(jìn)行getN = null;fn = null的操作。
4.經(jīng)常用但可能并沒有意識(shí)到它就是閉包的閉包
栗子1
function wait(msg) { setTimeout( function timer() { console.log( msg ); }, 1000 ); } wait( "Hello, closure!" );
上面的代碼其實(shí)可以理解為下面這樣:
function wait(msg) { function timer(){ console.log( msg ); } setTimeout( timer, 1000 ); } wait( "Hello, closure!" );
內(nèi)部函數(shù)timer引用了自由變量msg,timer創(chuàng)建了閉包,然后將timer傳遞給setTimeout(..),也就是將內(nèi)部函數(shù)timer傳遞到了所在的詞法作用域以外。
當(dāng)wait(..) 執(zhí)行1000 毫秒后,wait的變量對(duì)象并不會(huì)消失,timer函數(shù)可以訪問變量msg,只有當(dāng)setTimeout(..)執(zhí)行完畢后,wait的變量對(duì)象才會(huì)被銷毀。
栗子2
function bindName(name, selector) { $( selector ).click( function showName() { console.log( "This name is: " + name ); } ); } bindName( "Closure", "#closure" );
上面的代碼其實(shí)可以理解為下面這樣:
function bindName(name, selector) { function showName(){ console.log( "This name is: " + name ); } $( selector ).click( showName ); } bindName( "Closure", "#closure" );
內(nèi)部函數(shù)showName引用了自由變量name,showName創(chuàng)建了閉包,然后將showName傳遞給click事件作為回調(diào)函數(shù),也就是將內(nèi)部函數(shù)showName傳遞到了所在的詞法作用域以外。
當(dāng)bindName(..)執(zhí)行之后,bindName的變量對(duì)象并不會(huì)消失,每當(dāng)這個(gè)click事件觸發(fā)的時(shí)候showName函數(shù)可以訪問變量name。
5.同一個(gè)調(diào)用函數(shù)創(chuàng)建的閉包共享引用的自由變量
function change() { var num = 10; return{ up:function() { num++; console.log(num); }, down:function(){ num--; console.log(num); } } } var opt = change(); opt.up();//11 opt.up();//12 opt.down();//11 opt.down();//10
opt.up和opt.down共享變量num的引用,它們操作的是同一個(gè)變量num,因?yàn)檎{(diào)用一次change只會(huì)創(chuàng)建并進(jìn)入一個(gè)Function Execution Context change,通過閉包留在內(nèi)存中的變量對(duì)象只有一個(gè)。
6.不同調(diào)用函數(shù)創(chuàng)建的閉包互不影響
function change() { var num = 10; return{ up:function() { num++; console.log(num); }, down:function(){ num--; console.log(num); } } } var opt1 = change(); var opt2 = change(); opt1.up();//11 opt1.up();//12 opt2.down();//9 opt2.down();//8
change函數(shù)被調(diào)用了兩次,分別賦值給opt1和opt2,此時(shí)opt1.up,opt2.up以及opt1.down,opt2.down是互不影響的,因?yàn)槊空{(diào)用一次就會(huì)創(chuàng)建并進(jìn)入一個(gè)新的Function Execution Context change,也就會(huì)有新的變量對(duì)象,所以不同調(diào)用函數(shù)通過閉包留在內(nèi)存中的變量對(duì)象是獨(dú)立的,互不影響的。
7.關(guān)于上面提到的兩點(diǎn),有一個(gè)談到閉包就被拿出來的例子:
for(var i=1;i<6;i++){ setTimeout(function(){ console.log(i); },i*1000); }
上述例子乍一看會(huì)覺得輸出的結(jié)果是:每隔1s分別打印出1,2,3,4,5;然而實(shí)際上的結(jié)果是:每隔1s分別打印出6,6,6,6,6。
那么是為什么會(huì)這樣呢?下面就來解析一下(ES6之前沒有let命令,不存在真正的塊級(jí)作用域):
變量i此處為全局變量,我們考慮全局變量,那么傳遞給setTimeout(...)的這個(gè)匿名函數(shù)創(chuàng)建了閉包,因?yàn)樗昧俗兞?b>i;雖然循環(huán)中的五個(gè)函數(shù)是在各次迭代中分別定義的,但是它們引用的是全局變量i,這個(gè)i只有一個(gè),所以它們引用的是同一個(gè)變量(如果在此處將全局對(duì)象想象成一個(gè)僅調(diào)用了一次的函數(shù)的返回值,那么這個(gè)現(xiàn)象便可以對(duì)應(yīng)于 ———— 同一個(gè)調(diào)用函數(shù)創(chuàng)建的閉包共享引用的自由變量)
而setTimeout()的回調(diào)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行,即使每個(gè)迭代中執(zhí)行的是setTimeout(.., 0),而循環(huán)結(jié)束時(shí)全局變量i的值已經(jīng)變成6了,所以最后輸出的結(jié)果是每隔1s分別打印出6,6,6,6,6。
要解決上面這個(gè)問題,最簡(jiǎn)單的方式當(dāng)然是ES6中喜人的let命令了,僅需將var改為let即可,for 循環(huán)頭部的let 聲明會(huì)有一個(gè)特殊的行為。這個(gè)行為指出變量在循環(huán)過程中不止被聲明一次,每次迭代都會(huì)聲明。隨后的每個(gè)迭代都會(huì)使用上一個(gè)迭代結(jié)束時(shí)的值來初始化這個(gè)變量。
拋開喜人的ES6,又該怎么解決呢,既然上面的問題是由于共享同一個(gè)變量而導(dǎo)致的,那么我想辦法讓它不共享,而是每個(gè)函數(shù)引用一個(gè)不同的變量不就好了。上面提到了 ———— 不同調(diào)用函數(shù)創(chuàng)建的閉包互不影響,我們就要利用這個(gè)來解決這個(gè)問題:
for(var i=1;i<6;i++){ waitShow(i); } function waitShow(j){ setTimeout(function(){ console.log(j); },j*1000); }
我們將循環(huán)內(nèi)的代碼改成了一個(gè)函數(shù)調(diào)用語(yǔ)句waitShow(i),而waitShow函數(shù)的內(nèi)容就是之前循環(huán)體內(nèi)的內(nèi)容;waitShow內(nèi)部傳遞給setTimeout(...)的這個(gè)匿名函數(shù)仍然創(chuàng)建了閉包,只不過這次引用的是waitShow的參數(shù)j。
現(xiàn)在每迭代一次,便會(huì)調(diào)用waitShow一次,而我們從上文中已經(jīng)知道不同調(diào)用函數(shù)創(chuàng)建的閉包互不影響,所以就可以解決問題了!當(dāng)然,這還不是你常見的樣子,現(xiàn)在我們稍稍改動(dòng)一下,就變成非常常見的IIFE形式了:
for(var i=1;i<6;i++){ (function(j){ setTimeout(function(){ console.log(j); },j*1000); })(i) }
balabala說了這么多,其實(shí)我們平常寫代碼的時(shí)候經(jīng)常無(wú)意識(shí)的就創(chuàng)建了閉包,但是創(chuàng)建了我們不一定會(huì)去使用閉包,而閉包的“威力”需要通過使用才能看得到。
閉包的應(yīng)用閉包到底有什么用呢?我覺得總結(jié)成一句話就是:
“凍結(jié)”閉包的包含函數(shù)調(diào)用時(shí)的變量對(duì)象(使其以當(dāng)前值留在內(nèi)存中),并只有通過該閉包才能“解凍”(訪問/操作留在內(nèi)存中的變量對(duì)象)
粗看可能不是很能理解,下面我們結(jié)合具體的應(yīng)用場(chǎng)景來理解:
恩。。。首先我們來看一個(gè)老朋友,剛剛見過面的老朋友
for(var i=1;i<6;i++){ (function(j){ setTimeout(function(){ console.log(j); },j*1000); })(i) }
在這個(gè)栗子中,每個(gè)IIFE自調(diào)用時(shí),其內(nèi)部創(chuàng)建的閉包將其當(dāng)時(shí)的變量對(duì)象“凍結(jié)”了,并且通過將這個(gè)閉包作為setTimeout的參數(shù)傳遞到IIFE作用域以外;所以第一次循環(huán)“凍結(jié)”的j的值是1,第二次循環(huán)“凍結(jié)”的j的值是2......當(dāng)循環(huán)結(jié)束后,延遲時(shí)間到了后,setTimeout的回調(diào)執(zhí)行(即使用閉包),“解凍”了之前“凍結(jié)”的變量j,然后打印出來。
既然提到setTimeout,那再來看看另外一個(gè)應(yīng)用,我們知道在標(biāo)準(zhǔn)的setTimeout是可以向延遲函數(shù)傳遞額外的參數(shù)的,形式是這樣:setTimeout(function[, delay, param1, param2, ...]),,一旦定時(shí)器到期,它們會(huì)作為參數(shù)傳遞給function。但是萬(wàn)惡的IE搞事情,在IE9及其之前的版本中是不支持傳遞額外參數(shù)的。那有時(shí)候我們確實(shí)有需要傳參數(shù),怎么辦呢。通常的解決方法有下面這些:
function fullName( givenName ){ let familyName = "Swift"; console.log("The fullName is: " + givenName + " " + familyName); } setTimeout(fullName,1000,"Taylor Alison");
使用一個(gè)匿名函數(shù)包裹
setTimeout(function(){ fullName("Taylor Alison"); },1000);
使用bind(ES5引入)
setTimeout(fullName.bind(undefined,"Taylor Alison"),1000);
polyfill
使用閉包
function fullName( givenName ){ let familyName = "Swift"; return function(){ console.log("The fullName is: " + givenName + " " + familyName); } } let showFullName = fullName("Taylor Alison"); setTimeout(showFullName,1000);
fullName內(nèi)的匿名函數(shù)創(chuàng)建了閉包,并作為返回值返回,調(diào)用fullName()后返回值賦給變量showFullName,此時(shí)fullName的變量對(duì)象被“凍結(jié)”,只能通過showFullName才能“解凍”,定時(shí)器到期后,showFullName被調(diào)用,通過之前被“凍結(jié)”的變量對(duì)象訪問到givenName和familyName。
待續(xù)(有時(shí)間補(bǔ)上)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/95533.html
摘要:對(duì)數(shù)組函數(shù)而言,相當(dāng)于產(chǎn)生了個(gè)閉包。關(guān)于對(duì)象在閉包中使用對(duì)象也會(huì)導(dǎo)致一些問題。不過,匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此其對(duì)象通常指向。由于聲明函數(shù)時(shí)與聲明函數(shù)時(shí)的值是不同的,因此閉包與閉包貌似將會(huì)表示各自不同的值。 這幾天看到閉包一章,從工具書到各路大神博客,都各自有著不同的理解,以下我將選擇性的抄(咳咳,當(dāng)然還是會(huì)附上自己理解的)一些大神們對(duì)閉包的原理及其使用文章,當(dāng)作是自己初步理解...
摘要:內(nèi)存泄露內(nèi)存泄露概念在計(jì)算機(jī)科學(xué)中,內(nèi)存泄漏指由于疏忽或錯(cuò)誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存。判斷內(nèi)存泄漏,以字段為準(zhǔn)。 本文是 重溫基礎(chǔ) 系列文章的第二十二篇。 今日感受:優(yōu)化學(xué)習(xí)方法。 系列目錄: 【復(fù)習(xí)資料】ES6/ES7/ES8/ES9資料整理(個(gè)人整理) 【重溫基礎(chǔ)】1-14篇 【重溫基礎(chǔ)】15.JS對(duì)象介紹 【重溫基礎(chǔ)】16.JSON對(duì)象介紹 【重溫基礎(chǔ)】1...
摘要:談起閉包,它可是兩個(gè)核心技術(shù)之一異步基于打造前端持續(xù)集成開發(fā)環(huán)境本文將以一個(gè)標(biāo)準(zhǔn)的項(xiàng)目為例,完全拋棄傳統(tǒng)的前端項(xiàng)目開發(fā)部署方式,基于容器技術(shù)打造一個(gè)精簡(jiǎn)的前端持續(xù)集成的開發(fā)環(huán)境。 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果讀完本文還不懂,可以揍我。 不論你是javascript新手還是老鳥,不論是面試求職,還是日...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁(yè)面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個(gè)方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識(shí)之 HTTP 協(xié)議 詳細(xì)介紹 HTT...
摘要:這是因?yàn)槲覀冊(cè)L問了數(shù)組中不存在的數(shù)組元素它超過了最后一個(gè)實(shí)際分配到內(nèi)存的數(shù)組元素字節(jié),并且有可能會(huì)讀取或者覆寫的位。包含個(gè)元素的新數(shù)組由和數(shù)組元素所組成中的內(nèi)存使用中使用分配的內(nèi)存主要指的是內(nèi)存讀寫。 原文請(qǐng)查閱這里,本文有進(jìn)行刪減,文后增了些經(jīng)驗(yàn)總結(jié)。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第三章。 我們將會(huì)討論日常使用中另一個(gè)被開發(fā)...
閱讀 2107·2021-10-08 10:05
閱讀 1948·2021-09-22 15:31
閱讀 3139·2021-09-22 15:13
閱讀 3656·2021-09-09 09:34
閱讀 2266·2021-09-03 10:46
閱讀 3218·2019-08-30 15:56
閱讀 1762·2019-08-30 15:53
閱讀 2419·2019-08-30 15:44