摘要:什么是閉包閉包是函數(shù)廢話(huà)閉包還是一個(gè)可以訪(fǎng)問(wèn)函數(shù)中變量的函數(shù)。每個(gè)閉包都是引用自己詞法作用域內(nèi)的變量。每次調(diào)用其中一個(gè)計(jì)數(shù)器時(shí),通過(guò)改變這個(gè)變量的值,會(huì)改變這個(gè)閉包的詞法環(huán)境。然而在一個(gè)閉包內(nèi)對(duì)變量的修改,不會(huì)影響到另外一個(gè)閉包中的變量。
為什么要寫(xiě)深入理解
筆者并不是大神,只是一個(gè)在校的大三學(xué)生。開(kāi)始寫(xiě)深入理解系列是為了給js的一些重難點(diǎn)知識(shí)進(jìn)行梳理,而不是每次面試之前都將這些知識(shí)重新理解一遍。有理解的不對(duì)的,請(qǐng)賜教!事不宜遲,我們開(kāi)始吧。
什么是閉包閉包是函數(shù)?。◤U話(huà))閉包還是一個(gè)可以訪(fǎng)問(wèn)函數(shù)中變量的函數(shù)。
function who(){ let name="clong"; function print(){ return name; } return print; } let boy=who(); let myName=boy(); console.log(myName);//"clong"
開(kāi)始的定義可能會(huì)令你感覺(jué)晦澀難懂,看了上面的例子我們一起來(lái)理解一下。
分析下面我們來(lái)分析一下上面的栗子。
首先我們聲明了一個(gè)函數(shù),這個(gè)函數(shù)包含了一個(gè)變量name和一個(gè)print函數(shù),執(zhí)行到return print的時(shí)候進(jìn)行返回。
緊接著我們?cè)谌肿饔糜蛳律艘粋€(gè)變量boy,繼續(xù)向該行后面看,有個(gè)who,接著我們遇到(),這時(shí)我們就會(huì)重新返回到上面查找who有沒(méi)有聲明。
進(jìn)入who體內(nèi),我們聲明了一個(gè)變量name并賦值為clong,然后申明了一個(gè)print函數(shù),接著我們返回print,此時(shí)print函數(shù)被銷(xiāo)毀,而boy保存著print函數(shù)的引用。
接著我們遇到myName,基本流程與上面的boy是差不多一致的,只不過(guò)這時(shí)我們的myName不是保存著一個(gè)函數(shù),而是保存了name的值(注意:這里name已經(jīng)被銷(xiāo)毀了,不信你可以在全局作用域下打印name的值看看!)。
有了上面的栗子,我們來(lái)看看下面這個(gè)栗子:
function createCounter() { let counter = 0 const myFunction = function() { counter = counter + 1 return counter } return myFunction } const increment = createCounter() const c1 = increment() const c2 = increment() const c3 = increment() console.log("example increment", c1, c2, c3)//1,2,3
思考一下,答案與你認(rèn)為的一樣嗎?是不是以為是1,1,1呢?先不要急,我們?cè)賮?lái)看看下面這個(gè)栗子。
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===Counter2); 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 */
看看上面的栗子,你會(huì)不會(huì)疑惑?本質(zhì)上都是調(diào)用的私有函數(shù)的方法,為什么Counter1和Counter2的privateCounter就會(huì)完全不一樣呢?
筆者查看了很多資料,別人總結(jié)的要么就是將mdn上的解釋一貼,要么就是含糊其辭。
請(qǐng)注意兩個(gè)計(jì)數(shù)器 counter1 和 counter2 是如何維護(hù)它們各自的獨(dú)立性的。每個(gè)閉包都是引用自己詞法作用域內(nèi)的變量 privateCounter 。每次調(diào)用其中一個(gè)計(jì)數(shù)器時(shí),通過(guò)改變這個(gè)變量的值,會(huì)改變這個(gè)閉包的詞法環(huán)境。然而在一個(gè)閉包內(nèi)對(duì)變量的修改,不會(huì)影響到另外一個(gè)閉包中的變量?!狹DN
那么為什么對(duì)一個(gè)閉包的變量的改變不會(huì)影響到另一個(gè)閉包中的變量呢?我思考了很久,最后這樣解釋給自己聽(tīng):
前一個(gè)栗子中,我們的increment保存的是myFunction的引用和他的閉包(important:函數(shù)在創(chuàng)建的時(shí)候就會(huì)形成自己的作用域鏈)。了解過(guò)閉包的應(yīng)該都有保存在內(nèi)存中這個(gè)概念,那么我們這里沒(méi)得操作都是直接對(duì)閉包的操作,價(jià)值作用域的執(zhí)行順序(閉包=>父級(jí)=>...),每次都是從閉包中獲取,并且將修改的值保存在內(nèi)存中,自然是1,2,3!
那么后面一個(gè)栗子呢?我先姑且解釋看看(有不對(duì)的希望大??梢灾赋觯?函數(shù)中的變量都是私有的(包括函數(shù)),第二個(gè)栗子返回的是一個(gè)對(duì)象,然后我們后面的操作都是基于這個(gè)對(duì)象的屬性的操作,間接操作了內(nèi)部的私有方法并獲取了內(nèi)部的值。那么,回想一下new一個(gè)對(duì)象發(fā)生了什么?
創(chuàng)建一個(gè)空對(duì)象
將構(gòu)造函數(shù)的作用域賦給新對(duì)象,即將this只想新對(duì)象
講原型中的屬性添加到這個(gè)對(duì)象當(dāng)中
返回新對(duì)象
這個(gè)栗子不是正好跟上面的操作類(lèi)似嗎?如果還是不明白,我們假設(shè)makeCounter是一個(gè)Array對(duì)象,里面的private和changeBy是length和push內(nèi)部實(shí)現(xiàn)原理,返回一個(gè)新對(duì)象,并且給了你一些接口,而這個(gè)接口正好有push,使你可以進(jìn)行push操作,且僅限于該對(duì)象的私有屬性的方式。那么實(shí)例與實(shí)例的私有屬性或方法共有嗎?當(dāng)然不!神奇的利用閉包就實(shí)現(xiàn)了數(shù)據(jù)的私有和封裝了
用處經(jīng)過(guò)了上面的分析,我們大概可以了解到閉包的一些用處了吧。
訪(fǎng)問(wèn)函數(shù)中的變量
函數(shù)屬性的私有化/封裝(PS:這一塊還涉及到原型鏈,下次再寫(xiě))
劣勢(shì)這些也是耳熟能詳了!
性能問(wèn)題,一直暴露在內(nèi)存當(dāng)中,無(wú)法被垃圾回收機(jī)制回收,很有可能造成內(nèi)存泄漏!
場(chǎng)景回調(diào)函數(shù)
頁(yè)面交互操作(同上?。?/p>
setTimeout(不也是回調(diào)函數(shù)嘛?。?/p>
數(shù)據(jù)私有和封裝
tips仍然可以訪(fǎng)問(wèn)外部函數(shù)的中定義的變量即使外部函數(shù)被返回了
閉包存儲(chǔ)對(duì)外部函數(shù)中變量的引用,而不是值
閉包可以實(shí)現(xiàn)js的數(shù)據(jù)的封裝和私有化
參考資料MDN/JS/閉包
Understand JavaScript Closures With Ease
I never understood JavaScript closures
博客地址
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95366.html
摘要:針對(duì)于面向?qū)ο缶幊痰?。因?yàn)槊嫦驅(qū)ο缶褪轻槍?duì)對(duì)象例子中的守候來(lái)進(jìn)行執(zhí)行某些動(dòng)作。這就是閉包的用途之一延續(xù)變量周期。把變量放在閉包里面和放在全局變量里面,影響是一致的。 1.前言 這段時(shí)間,金三銀四,很多人面試,很多人分享面試題。在前段時(shí)間,我也臨時(shí)擔(dān)任面試官,為了大概了解面試者的水平,我也寫(xiě)了一份題目,面試了幾個(gè)前端開(kāi)發(fā)者。在這段時(shí)間里面,我在學(xué),在寫(xiě)設(shè)計(jì)模式的一些知識(shí),想不到的設(shè)計(jì)模式...
摘要:相反,在討論時(shí),面試中通常會(huì)提到三件事。而認(rèn)為最后一個(gè)參賽者說(shuō)了算,只要還能吃的,就重新設(shè)定新的定時(shí)器。試想,如果用戶(hù)的操作十分頻繁他每次都不等設(shè)置的時(shí)間結(jié)束就進(jìn)行下一次操作,于是每次都為該用戶(hù)重新生成定時(shí)器,回調(diào)函數(shù)被延遲了不計(jì)其數(shù)次。本文不是討論最新的 JavaScript 庫(kù)、常見(jiàn)的開(kāi)發(fā)實(shí)踐或任何新的 ES6 函數(shù)。相反,在討論 JavaScript 時(shí),面試中通常會(huì)提到三件事。我自己...
摘要:相反,在討論時(shí),面試中通常會(huì)提到三件事。通過(guò)對(duì)事件對(duì)應(yīng)的回調(diào)函數(shù)進(jìn)行包裹以自由變量的形式緩存時(shí)間信息,最后用來(lái)控制事件的觸發(fā)頻率。而認(rèn)為最后一個(gè)參賽者說(shuō)了算,只要還能吃的,就重新設(shè)定新的定時(shí)器。 showImg(https://segmentfault.com/img/bVboH5x?w=1000&h=750); 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你! 本...
摘要:而閉包的妙處在于,當(dāng)函數(shù)在執(zhí)行完畢后它的活動(dòng)對(duì)象不會(huì)被銷(xiāo)毀,因?yàn)槟涿瘮?shù)的作用域鏈仍然在引用函數(shù)的活動(dòng)對(duì)象它的作用域鏈會(huì)被銷(xiāo)毀。 一、閉包 閉包是指有權(quán)訪(fǎng)問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常用方式是,在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)。 請(qǐng)看以下代碼:我們?cè)赾reateComparisonFunction函數(shù)里創(chuàng)建了一個(gè)閉包 function createComparisonFun...
摘要:閉包與函數(shù)真正的區(qū)別函數(shù)封裝一次多處調(diào)用。閉包只限于本方法使用,耦合度低到忽略。 看過(guò)許多關(guān)于PHP中閉包的講解,每個(gè)文檔想要表達(dá)的意思大體相同,但是理解起來(lái)很費(fèi)勁,我根據(jù)自身理解加以描述,有更好的理解請(qǐng)指出 眾所周知,大家都知道PHP的閉包是function () use (){}; 本文分為3步1:講解閉包的使用2:閉包實(shí)例3:閉包總結(jié) 1、講解閉包的使用1:閉包中的use使用-上...
閱讀 1297·2021-11-23 09:51
閱讀 2693·2021-09-03 10:47
閱讀 2272·2019-08-30 15:53
閱讀 2458·2019-08-30 15:44
閱讀 1405·2019-08-30 15:44
閱讀 1228·2019-08-30 10:57
閱讀 1956·2019-08-29 12:25
閱讀 1119·2019-08-26 11:57