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