摘要:下面我們就羅列閉包的幾個(gè)常見問題,從回答問題的角度來理解和定義你們心中的閉包。函數(shù)可以通過作用域鏈相互關(guān)聯(lián)起來,函數(shù)內(nèi)部的變量可以保存在其他函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中稱為閉包。
寫這篇文章之前,我對閉包的概念及原理模糊不清,一直以來都是以通俗的外層函數(shù)包裹內(nèi)層....來欺騙自己。并沒有說這種說法的對與錯(cuò),我只是不想擁有從眾心理或者也可以說如果我們說出更好更低層的東西,逼格會(huì)提升好幾個(gè)檔次。。。
談起閉包,它可是JavaScript兩個(gè)核心技術(shù)之一(異步和閉包),在面試以及實(shí)際應(yīng)用當(dāng)中,我們都離不開它們,甚至可以說它們是衡量js工程師實(shí)力的一個(gè)重要指標(biāo)。下面我們就羅列閉包的幾個(gè)常見問題,從回答問題的角度來理解和定義你們心中的閉包。
問題如下:
1.什么是閉包? 2.閉包的原理可不可以說一下? 3.你是怎樣使用閉包的?閉包的介紹
我們先看看幾本書中的大致介紹:
1.閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)
2.函數(shù)對象可以通過作用域關(guān)聯(lián)起來,函數(shù)體內(nèi)的變量都可以保存在函數(shù)作用域內(nèi),這在計(jì)算機(jī)科學(xué)文獻(xiàn)中稱為“閉包”,所有的javascirpt函數(shù)都是閉包
3.閉包是基于詞法作用域書寫代碼時(shí)所產(chǎn)生的必然結(jié)果。
4.. 函數(shù)可以通過作用域鏈相互關(guān)聯(lián)起來,函數(shù)內(nèi)部的變量可以保存在其他函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中稱為閉包。
可見,它們各有各自的定義,但要說明的意思大同小異。筆者在這之前對它是知其然而不知其所以然,最后用了一天的時(shí)間從詞法作用域到作用域鏈的概念再到閉包的形成做了一次總的梳理,發(fā)現(xiàn)做人好清晰了...。
下面讓我們拋開這些抽象而又晦澀難懂的表述,從頭開始理解,內(nèi)化最后總結(jié)出自己的一段關(guān)于閉包的句子。我想這對面試以及充實(shí)開發(fā)者自身的理論知識(shí)非常有幫助。
閉包的構(gòu)成 詞法作用域要理解詞法作用域,我們不得不說起JS的編譯階段,大家都知道JS是弱類型語言,所謂弱類型是指不用預(yù)定義變量的儲(chǔ)存類型,并不能完全概括JS或與其他語言的區(qū)別,在這里我們引用黃皮書(《你不知道的javascript》)上的給出的解釋編譯語言。
編譯語言編譯語言在執(zhí)行之前必須要經(jīng)歷三個(gè)階段,這三個(gè)階段就像過濾器一樣,把我們寫的代碼轉(zhuǎn)換成語言內(nèi)部特定的可執(zhí)行代碼。就比如我們寫的代碼是var a = 1;,而JS引擎內(nèi)部定義的格式是var,a,=,1 那在編譯階段就需要把它們進(jìn)行轉(zhuǎn)換。這只是一個(gè)比喻,而事實(shí)上這只是在編譯階段的第一個(gè)階段所做的事情。下面我們概括一下,三個(gè)階段分別做了些什么。
分詞/詞法分析(Tokenizing/Lexing)
這就是我們上面講的一樣,其實(shí)我們寫的代碼就是字符串,在編譯的第一個(gè)階段里,把這些字符串轉(zhuǎn)成詞法單元(toekn),詞法單元我們可以想象成我們上面分解的表達(dá)式那樣。(注意這個(gè)步驟有兩種可能性,當(dāng)前這屬于分詞,而詞法分析,會(huì)在下面和詞法作用域一起說。)
解析/語法分析(Parsing)
在有了詞法單元之后,JS還需要繼續(xù)分解代碼中的語法以便為JS引擎減輕負(fù)擔(dān)(總不能在引擎運(yùn)行的過程中讓它承受這么多輪的轉(zhuǎn)換規(guī)則吧?) ,通過詞法單元生成了一個(gè)抽象語法樹(Abstract Syntax Tree),它的作用是為JS引擎構(gòu)造出一份程序語法樹,我們簡稱為AST。這時(shí)我們不禁聯(lián)想到Dom樹(扯得有點(diǎn)遠(yuǎn)),沒錯(cuò)它們都是樹,以var,a,=,1為例,它會(huì)以層為單元?jiǎng)澐炙麄?,例? 頂層有一個(gè) stepA 里面包含著 "v",stepA下面有一個(gè)stepB,stepB中含有 "a",就這樣一層一層嵌套下去....
代碼生成(raw code)
這個(gè)階段主要做的就是拿AST來生成一份JS語言內(nèi)部認(rèn)可的代碼(這是語言內(nèi)部制定的,并不是二進(jìn)制哦),在生成的過程中,編譯器還會(huì)詢問作用域的問題,還是以 var a = 1;為例,編譯器首先會(huì)詢問作用域,當(dāng)前有沒有變量a,如果有則忽略,否則在當(dāng)前作用域下創(chuàng)建一個(gè)名叫a的變量.
哈哈,終于到了詞法階段,是不是看了上面的三大階段,甚是懵逼,沒想到j(luò)s還會(huì)有這樣繁瑣的經(jīng)歷? 其實(shí),上面的概括只是所有編譯語言的最基本的流程,對于我們的JS而言,它在編譯階段做的事情可不僅僅是那些,它會(huì)提前為js引擎做一些性能優(yōu)化等工作,總之,編譯器把所有臟活累活全干遍了。
要說到詞法階段這個(gè)概念,我們還要結(jié)合上面未結(jié)的分詞/詞法分析階段.來說...
詞法作用域是發(fā)生在編譯階段的第一個(gè)步驟當(dāng)中,也就是分詞/詞法分析階段。它有兩種可能,分詞和詞法分析,分詞是無狀態(tài)的,而詞法分析是有狀態(tài)的。
那我們?nèi)绾闻袛嘤袩o狀態(tài)呢?以 var a = 1為例,如果詞法單元生成器在判斷a是否為一個(gè)獨(dú)立的詞法單元時(shí),調(diào)用的是有狀態(tài)的解析規(guī)則(生成器不清楚它是否依賴于其他詞法單元,所以要進(jìn)一步解析)。反之,如果它不用生成器判斷,是一條不用被賦予語意的代碼(暫時(shí)可以理解為不涉及作用域的代碼,因?yàn)閖s內(nèi)部定義什么樣的規(guī)則我們并不清楚),那就被列入分詞中了。
這下我們知道,如果詞法單元生成器拿不準(zhǔn)當(dāng)前詞法單元是否為獨(dú)立的,就進(jìn)入詞法分析,否則就進(jìn)入分詞階段。
沒錯(cuò),這就是理解詞法作用域及其名稱來歷的基礎(chǔ)。
簡單的說,詞法作用域就是定義在詞法階段的作用域。詞法作用域就是你編寫代碼時(shí),變量和塊級(jí)作用域寫在哪里決定的。當(dāng)詞法解析器(這里只當(dāng)作是解析詞法的解析器,后續(xù)會(huì)有介紹)處理代碼時(shí),會(huì)保持作用域不變(除動(dòng)態(tài)作用域)。
在這一小節(jié)中,我們只需要了解:
詞法作用域是什么?
詞法階段中 分詞/詞法分析的概念?
它們對詞法作用域的形成有哪些影響?
這節(jié)有兩個(gè)個(gè)忽略掉的知識(shí)點(diǎn)(詞法解析器,動(dòng)態(tài)作用域),因主題限制沒有寫出來,以后有機(jī)會(huì)為大家介紹。下面開始作用域。
作用域鏈 1. 執(zhí)行環(huán)境執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù)。
環(huán)境??梢詴簳r(shí)理解為一個(gè)數(shù)組(JS引擎的一個(gè)儲(chǔ)存棧)。
在web瀏覽器中,全局環(huán)境即window是最外層的執(zhí)行環(huán)境,而每個(gè)函數(shù)也都有自己的執(zhí)行環(huán)境,當(dāng)調(diào)用一個(gè)函數(shù)的時(shí)候,函數(shù)會(huì)被推入到一個(gè)環(huán)境棧中,當(dāng)他以及依賴成員都執(zhí)行完畢之后,棧就將其環(huán)境彈出,
先看一個(gè)圖 !
環(huán)境棧也有人稱做它為函數(shù)調(diào)用棧(都是一回事,只不過后者的命名方式更傾向于函數(shù)),這里我們統(tǒng)稱為棧。位于環(huán)境棧中最外層是 window , 它只有在關(guān)閉瀏覽器時(shí)才會(huì)從棧中銷毀。而每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境,
到這里我們應(yīng)該知道:
每個(gè)函數(shù)都有一個(gè)與之對應(yīng)的執(zhí)行環(huán)境。
當(dāng)函數(shù)執(zhí)行時(shí),會(huì)把當(dāng)前函數(shù)的環(huán)境押入環(huán)境棧中,把當(dāng)前函數(shù)執(zhí)行完畢,則摧毀這個(gè)環(huán)境。
window 全局對象時(shí)棧中對外層的(相對于圖片來說,就是最下面的)。
函數(shù)調(diào)用棧與環(huán)境棧的區(qū)別 。 這兩者就好像是 JS中原始類型和基礎(chǔ)類型 | 引用類型與對象類型與復(fù)合類型 汗!
2. 變量對象與活動(dòng)對象執(zhí)行環(huán)境,所謂環(huán)境我們不難聯(lián)想到房子這一概念。沒錯(cuò),它就像是一個(gè)大房子,它不是獨(dú)立的,它會(huì)為了完成更多的任務(wù)而攜帶或關(guān)聯(lián)其他的概念。
每個(gè)執(zhí)行環(huán)境都有一個(gè)表示變量的對象-------變量對象,這個(gè)對象里儲(chǔ)存著在當(dāng)前環(huán)境中所有的變量和函數(shù)。
變量對象對于執(zhí)行環(huán)境來說很重要,它在函數(shù)執(zhí)行之前被創(chuàng)建。它包含著當(dāng)前函數(shù)中所有的參數(shù),變量,函數(shù)。這個(gè)創(chuàng)建變量對象的過程實(shí)際就是函數(shù)內(nèi)數(shù)據(jù)(函數(shù)參數(shù)、內(nèi)部變量、內(nèi)部函數(shù))初始化的過程。
在沒有執(zhí)行當(dāng)前環(huán)境之前,變量對象中的屬性都不能訪問!但是進(jìn)入執(zhí)行階段之后,變量對象轉(zhuǎn)變?yōu)榱?strong>活動(dòng)對象,里面的屬性都能被訪問了,然后開始進(jìn)行執(zhí)行階段的操作。所以活動(dòng)對象實(shí)際就是變量對象在真正執(zhí)行時(shí)的另一種形式。
function fun (a){ var n = 12; function toStr(a){ return String(a); } }
在 fun 函數(shù)的環(huán)境中,有三個(gè)變量對象(壓入環(huán)境棧之前),首先是arguments,變量n 與 函數(shù) toStr ,壓入環(huán)境棧之后(在執(zhí)行階段),他們都屬于fun的活動(dòng)對象。 活動(dòng)對象在最開始時(shí),只包含一個(gè)變量,即argumens對象。
到這里我們應(yīng)該知道:
每個(gè)執(zhí)行環(huán)境有一個(gè)與之對應(yīng)的變量對象。
環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對象里。
對于函數(shù),執(zhí)行前的初始化階段叫變量對象,執(zhí)行中就變成了活動(dòng)對象。
3. 作用域鏈當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對象的一個(gè)作用域鏈。用數(shù)據(jù)格式表達(dá)作用域鏈的結(jié)構(gòu)如下。
[{當(dāng)前環(huán)境的變量對象},{外層變量對象},{外層的外層的變量對象}, {window全局變量對象}] 每個(gè)數(shù)組單元就是作用域鏈的一塊,這個(gè)塊就是我們的變量對象。
作用于鏈的前端,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對象。全局執(zhí)行環(huán)境的變量對象也始終都是鏈的最后一個(gè)對象。
function foo(){ var a = 12; fun(a); function fun(a){ var b = 8; console.log(a + b); } } foo();
再來看上面這個(gè)簡單的例子,我們可以先思考一下,每個(gè)執(zhí)行環(huán)境下的變量對象都是什么? 這兩個(gè)函數(shù)它們的變量對象分別都是什么?
我們以fun為例,當(dāng)我們調(diào)用它時(shí),會(huì)創(chuàng)建一個(gè)包含 arguments,a,b的活動(dòng)對象,對于函數(shù)而言,在執(zhí)行的最開始階段它的活動(dòng)對象里只包含一個(gè)變量,即arguments(當(dāng)執(zhí)行流進(jìn)入,再創(chuàng)建其他的活動(dòng)對象)。
在活動(dòng)對象中,它依然表示當(dāng)前參數(shù)集合。對于函數(shù)的活動(dòng)對象,我們可以想象成兩部分,一個(gè)是固定的arguments對象,另一部分是函數(shù)中的局部變量。而在此例中,a和b都被算入是局部變量中,即便a已經(jīng)包含在了arguments中,但他還是屬于。
有沒有發(fā)現(xiàn)在環(huán)境棧中,所有的執(zhí)行環(huán)境都可以組成相對應(yīng)的作用域鏈。我們可以在環(huán)境棧中非常直觀的拼接成一個(gè)相對作用域鏈。
下面我們大致說下這段代碼的執(zhí)行流程:
在創(chuàng)建foo的時(shí)候,作用域鏈已經(jīng)預(yù)先包含了一個(gè)全局對象,并保存在內(nèi)部屬性[[ Scope ]]當(dāng)中。
執(zhí)行foo函數(shù),創(chuàng)建執(zhí)行環(huán)境與活動(dòng)對象后,取出函數(shù)的內(nèi)部屬性[[Scope]]構(gòu)建當(dāng)前環(huán)境的作用域鏈(取出后,只有全局變量對象,然后此時(shí)追加了一個(gè)它自己的活動(dòng)對象)。
執(zhí)行過程中遇到了fun,從而繼續(xù)對fun使用上一步的操作。
fun執(zhí)行結(jié)束,移出環(huán)境棧。foo因此也執(zhí)行完畢,繼續(xù)移出。
javscript 監(jiān)聽到foo沒有被任何變量所引用,開始實(shí)施垃圾回收機(jī)制,清空占用內(nèi)存。
作用域鏈其實(shí)就是引用了當(dāng)前執(zhí)行環(huán)境的變量對象的指針列表,它只是引用,但不是包含。,因?yàn)樗男螤钕矜湕l,它的執(zhí)行過程也非常符合,所以我們都稱之為作用域鏈,而當(dāng)我們弄懂了這其中的奧秘,就可以拋開這種形式上的束縛,從原理上出發(fā)。
到這里我們應(yīng)該知道:
什么是作用域鏈。
作用域鏈的形成流程。
內(nèi)部屬性 [[Scope]] 的概念。
使用閉包從頭到尾,我們把涉及到的技術(shù)點(diǎn)都過了一遍,寫的不太詳細(xì)也有些不準(zhǔn)確,因?yàn)闆]有經(jīng)過事實(shí)的論證,我們只大概了解了這個(gè)過程概念。
涉及的理論充實(shí)了,那么現(xiàn)在我們就要使用它了。 先上幾個(gè)最簡單的計(jì)數(shù)器例子:
var counter = (!function(){ var num = 0; return function(){ return ++num; } }()) function counter(){ var num = 0; return { reset:function(){ num = 0; }, count:function(){ return num++; } } } function counter_get (n){ return { get counte(){ return ++n; }, set counte(m){ if(m相信看到這里,很多同學(xué)都預(yù)測出它們執(zhí)行的結(jié)果。它們都有一個(gè)小特點(diǎn),就是實(shí)現(xiàn)的過程都返回一個(gè)函數(shù)對象,返回的函數(shù)中帶有對外部變量的引用。
為什么非要返回一個(gè)函數(shù)呢 ?
因?yàn)楹瘮?shù)可以提供一個(gè)執(zhí)行環(huán)境,在這個(gè)環(huán)境中引用其它環(huán)境的變量對象時(shí),后者不會(huì)被js內(nèi)部回收機(jī)制清除掉。從而當(dāng)你在當(dāng)前執(zhí)行環(huán)境中訪問它時(shí),它還是在內(nèi)存當(dāng)中的。這里千萬不要把環(huán)境棧和垃圾回收這兩個(gè)很重要的過程搞混了,環(huán)境棧通俗點(diǎn)就是調(diào)用棧,調(diào)用移入,調(diào)用后移出,垃圾回收則是監(jiān)聽引用。為什么可以一直遞增呢 ?
上面已經(jīng)說了,返回的匿名函數(shù)構(gòu)成了一個(gè)多帶帶執(zhí)行環(huán)境(事實(shí)上函數(shù)作為代碼執(zhí)行的最小單元環(huán)境,每一個(gè)單元[函數(shù)]都是獨(dú)立的),這個(gè)環(huán)境中的變量對象`被其他變量所引用,js進(jìn)行自動(dòng)垃圾回收機(jī)制(GC:Garbage Collecation)時(shí)才不會(huì)對它進(jìn)行垃圾回收(不然呢,如果不這樣,代碼設(shè)計(jì)的會(huì)很繁瑣,js也沒有這么靈活)。所以這個(gè)值會(huì)一直存在,例子中每次執(zhí)行都會(huì)對他進(jìn)行遞增。性能會(huì)不會(huì)有損耗 ?
就拿這個(gè)功能來說,我們?yōu)榱藢?shí)現(xiàn)它使用了閉包,但是當(dāng)我們使用結(jié)束之后呢? 不要忘了還有一個(gè)變量對其他變量對象的引用。這個(gè)時(shí)候我們?yōu)榱俗宩s可以正?;厥账梢允謩?dòng)賦值為null;以第一個(gè)為例:
var counter = (!function(){ var num = 0; return function(){ return ++num; } }()) var n = counter(); n(); n(); n = null; // 清空引用,等待回收我們再來看上面的代碼,第一個(gè)是返回了一個(gè)函數(shù),后兩個(gè)類似于方法,他們都能非常直接的表明閉包的實(shí)現(xiàn),其實(shí)更值得我們注意的是閉包實(shí)現(xiàn)的多樣性。
閉包面試題一. 用屬性的存取器實(shí)現(xiàn)一個(gè)閉包計(jì)時(shí)器
見上例;
二. 看代碼,猜輸出
function fun(n,o) { console.log(o); return { fun:function(m){ return fun(m,n); } }; }var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?這道題的難點(diǎn)除了閉包,還有遞歸等過程,筆者當(dāng)時(shí)答這道題的時(shí)候也答錯(cuò)了,真是惡心。下面我們來分析一下。
首先說閉包部分,fun返回了一個(gè)可用.操作符訪問的fun方法(這樣說比較好理解)。在返回的方法中它的活動(dòng)對象可以分為 [arguments[m],m,n,fun]。在問題中,使用了變量引用(接收了返回的函數(shù))了這些活動(dòng)對象。
在返回的函數(shù)中,有一個(gè)來自外部的實(shí)參m,拿到實(shí)參后再次調(diào)用并返回fun函數(shù)。這次執(zhí)行fun時(shí)附帶了兩個(gè)參數(shù),第一個(gè)是剛才的外部實(shí)參(也就是調(diào)用時(shí)自己賦的),注意第二個(gè)是上一次的fun第一個(gè)參數(shù)。
第一個(gè),把返回的fun賦給了變量a,然后再多帶帶調(diào)用返回的fun,在返回的fun函數(shù)中第二個(gè)參數(shù)n正好把我們上一次通過調(diào)用外層fun的參數(shù)又拿回來了,然而它并不是鏈?zhǔn)降?,可見我們調(diào)用了四次,但這四次,只有第一次調(diào)用外部的fun時(shí)傳進(jìn)去的,后面通過a調(diào)用的內(nèi)部fun并不會(huì)影響到o的輸出,所以仔細(xì)琢磨一下不難看出最后結(jié)果是undefine 0,0,0。
第二個(gè)是鏈?zhǔn)秸{(diào)用,乍一看,和第一個(gè)沒有區(qū)別啊,只不過第一個(gè)是多了一個(gè)a的中間變量,可千萬不要被眼前的所迷惑呀!!!
// 第一個(gè)的調(diào)用方式 a.fun(1) a.fun(2) a.fun(3) { fun:function(){ return fun() // 外層的fun } } //第二個(gè)的調(diào)用方式 fun(1).fun(2).fun(3) //第一次調(diào)用返回和上面的一模一樣 //第二次以后有所不同 return fun() //直接返回外部的fun看上面的返回,第二的不同在于,第二次調(diào)用它再次接收了{(lán)fun:return fun}的返回值,然而在第三次調(diào)用時(shí)候它就是外部的fun函數(shù)了。理解了第一個(gè)和第二個(gè)我相信就知道了第三個(gè)。最后的結(jié)果就不說了,可以自己測一下。
三. 看代碼,猜輸出
for (var i = 1; i <= 5; i++) { setTimeout( function timer() { console.log(i); }, 1000 ); } for (var i = 1; i <= 5; i++) { (function(i){ setTimeout( function () { console.log(i); }, 1000 ); })(i); }上例中兩段代碼,第一個(gè)我們在面試過程中一定碰到過,這是一個(gè)異步的問題,它不是一個(gè)閉包,但我們可以通過閉包的方式解決。
第二段代碼會(huì)輸出 1- 5 ,因?yàn)槊垦h(huán)一次回調(diào)中都引用了參數(shù)i(也就是活動(dòng)對象),而在上一個(gè)循環(huán)中,每個(gè)回調(diào)引用的都是一個(gè)變量i,其實(shí)我們還可以用其他更簡便的方法來解決。
for (let i = 1; i <= 5; i++) { setTimeout( function timer() { console.log(i); }, 1000 ); }let為我們創(chuàng)建局部作用域,它和我們剛才使用的閉包解決方案是一樣的,只不過這是js內(nèi)部創(chuàng)建臨時(shí)變量,我們不用擔(dān)心它引用過多造成內(nèi)存溢出問題。
總結(jié) 我們知道了本章涉及的范圍稍廣,主要是想讓大家更全面的認(rèn)識(shí)閉包,那么到現(xiàn)在你知道了什么呢?我想每個(gè)人心中都有了答案。
1.什么是閉包?
閉包是依據(jù)詞法作用域產(chǎn)生的必然結(jié)果。通過變相引用函數(shù)的活動(dòng)對象導(dǎo)致其不能被回收,然而形成了依然可以用引用訪問其作用域鏈的結(jié)果。
``` (function(w,d){ var s = "javascript"; }(window,document)) ```有些說法把這種方式稱之為閉包,并說閉包可以避免全局污染,首先大家在這里應(yīng)該有一個(gè)自己的答案,以上這個(gè)例子是一個(gè)閉包嗎?
避免全局污染不假,但閉包談不上,它最多算是在全局執(zhí)行環(huán)境之上新建了一個(gè)二級(jí)作用域,從而避免了在全局上定義其他變量。切記它不是真正意義的閉包。
2.閉包的原理可不可以說一下?
結(jié)合我們上面講過的,它的根源起始于詞法階段,在這個(gè)階段中形成了詞法作用域。最終根據(jù)調(diào)用環(huán)境產(chǎn)生的環(huán)境棧來形成了一個(gè)由變量對象組成的作用域鏈,當(dāng)一個(gè)環(huán)境沒有被js正常垃圾回收時(shí),我們依然可以通過引用來訪問它原始的作用域鏈。
3.你是怎樣使用閉包的?
使用閉包的場景有很多,筆者最近在看函數(shù)式編程,可以說在js中閉包其實(shí)就是函數(shù)式的一個(gè)重要基礎(chǔ),舉個(gè)不完全函數(shù)的栗子.
function calculate(a,b){ return a + b; } function fun(){ var ars = Array.from(arguments); return function(){ var arguNum = ars.concat(Array.from(arguments)) return arguNum.reduce(calculate) } } var n = fun(1,2,3,4,5,6,7); var k = n(8,9,10); delete n;上面這個(gè)栗子,就是保留對 fun函數(shù)的活動(dòng)對象(arguments[]),當(dāng)然在我們?nèi)粘i_發(fā)中還有更復(fù)雜的情況,這需要很多函數(shù)塊,到那個(gè)時(shí)候,才能顯出我們閉包的真正威力.
文章到這里大概講完了,都是我自己的薄見和書上的一些內(nèi)容,希望能對大家有點(diǎn)影響吧,當(dāng)然這是正面的...如果哪里文中有描述不恰當(dāng)或大家有更好的見解還望指出,謝謝。
題外話:讀一篇文章或者看幾頁書,也不過是幾分鐘的事情。但是要理解的話需要個(gè)人內(nèi)化的過程,從輸入 到 理解 到 內(nèi)化 再到輸出,這是一個(gè)非常合理的知識(shí)體系。我想不僅僅對于閉包,它對任何知識(shí)來說都是一樣的重要,當(dāng)某些知識(shí)融入到我們身體時(shí),需要把他輸出出去,告訴別人。這不僅僅是“奉獻(xiàn)”精神,也是自我提高的過程。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/83596.html
摘要:談起閉包,它可是兩個(gè)核心技術(shù)之一異步基于打造前端持續(xù)集成開發(fā)環(huán)境本文將以一個(gè)標(biāo)準(zhǔn)的項(xiàng)目為例,完全拋棄傳統(tǒng)的前端項(xiàng)目開發(fā)部署方式,基于容器技術(shù)打造一個(gè)精簡的前端持續(xù)集成的開發(fā)環(huán)境。 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果讀完本文還不懂,可以揍我。 不論你是javascript新手還是老鳥,不論是面試求職,還是日...
摘要:前端日報(bào)精選大前端公共知識(shí)梳理這些知識(shí)你都掌握了嗎以及在項(xiàng)目中的實(shí)踐深入貫徹閉包思想,全面理解閉包形成過程重溫核心概念和基本用法前端學(xué)習(xí)筆記自定義元素教程阮一峰的網(wǎng)絡(luò)日志中文譯回調(diào)是什么鬼掘金譯年,一個(gè)開發(fā)者的好習(xí)慣知乎專 2017-06-23 前端日報(bào) 精選 大前端公共知識(shí)梳理:這些知識(shí)你都掌握了嗎?Immutable.js 以及在 react+redux 項(xiàng)目中的實(shí)踐深入貫徹閉包思...
摘要:難怪超過三分之一的開發(fā)人員工作需要一些知識(shí)。但是隨著行業(yè)的飽和,初中級(jí)前端就業(yè)形勢不容樂觀。整個(gè)系列的文章大概有篇左右,從我是如何成為一個(gè)前端工程師,到各種前端框架的知識(shí)。 為什么 call 比 apply 快? 這是一個(gè)非常有意思的問題。 作者會(huì)在參數(shù)為3個(gè)(包含3)以內(nèi)時(shí),優(yōu)先使用 call 方法進(jìn)行事件的處理。而當(dāng)參數(shù)過多(多余3個(gè))時(shí),才考慮使用 apply 方法。 這個(gè)的原因...
摘要:難怪超過三分之一的開發(fā)人員工作需要一些知識(shí)。但是隨著行業(yè)的飽和,初中級(jí)前端就業(yè)形勢不容樂觀。整個(gè)系列的文章大概有篇左右,從我是如何成為一個(gè)前端工程師,到各種前端框架的知識(shí)。 為什么 call 比 apply 快? 這是一個(gè)非常有意思的問題。 作者會(huì)在參數(shù)為3個(gè)(包含3)以內(nèi)時(shí),優(yōu)先使用 call 方法進(jìn)行事件的處理。而當(dāng)參數(shù)過多(多余3個(gè))時(shí),才考慮使用 apply 方法。 這個(gè)的原因...
摘要:中所有的事件綁定都是異步編程當(dāng)前這件事件沒有徹底完成,不再等待,繼續(xù)執(zhí)行下面的任務(wù)當(dāng)綁定事件后,不需要等待執(zhí)行,繼續(xù)執(zhí)行下一個(gè)循環(huán)任務(wù),所以當(dāng)我們點(diǎn)擊執(zhí)行方法的時(shí)候,循環(huán)早已結(jié)束即是最后。 概念 閉包就是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù) 點(diǎn)擊li標(biāo)簽彈出對應(yīng)數(shù)字 0 1...
閱讀 2856·2021-11-24 10:23
閱讀 1216·2021-11-17 09:33
閱讀 2594·2021-09-28 09:41
閱讀 1514·2021-09-22 15:55
閱讀 3702·2019-08-29 16:32
閱讀 2004·2019-08-29 16:25
閱讀 1112·2019-08-29 11:06
閱讀 3481·2019-08-29 10:55