筆者並非大神,只是一個在校的大三學生。開始寫深刻理解系列是爲了給js的一些重難點知識進行梳理,而不是每次面試以前都將這些知識從新理解一遍。有理解的不對的,請賜教!事不宜遲,咱們開始吧。javascript
閉包是函數!(廢話)閉包仍是一個能夠訪問函數中變量的函數。java
function who(){ let name='clong'; function print(){ return name; } return print; } let boy=who(); let myName=boy(); console.log(myName);//'clong'
開始的定義可能會令你感受晦澀難懂,看了上面的例子咱們一塊兒來理解一下。git
下面咱們來分析一下上面的栗子。github
有了上面的栗子,咱們來看看下面這個栗子:面試
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
那麼爲何對一個閉包的變量的改變不會影響到另外一個閉包中的變量呢?我思考了好久,最後這樣解釋給本身聽:this
前一個栗子中,咱們的increment保存的是myFunction的引用和他的閉包(important:函數在建立的時候就會造成本身的做用域鏈)。瞭解過閉包的應該都有保存在內存中這個概念,那麼咱們這裏沒得操做都是直接對閉包的操做,價值做用域的執行順序(閉包=>父級=>...),每次都是從閉包中獲取,而且將修改的值保存在內存中,天然是1,2,3!code
那麼後面一個栗子呢?我先姑且解釋看看(有不對的但願大牛能夠指出),函數中的變量都是私有的(包括函數),第二個栗子返回的是一個對象,而後咱們後面的操做都是基於這個對象的屬性的操做,間接操做了內部的私有方法並獲取了內部的值。那麼,回想一下new一個對象發生了什麼?
這個栗子不是正好跟上面的操做相似嗎?若是仍是不明白,咱們假設makeCounter是一個Array對象,裏面的private和changeBy是length和push內部實現原理,返回一個新對象,而且給了你一些接口,而這個接口正好有push,使你能夠進行push操做,且僅限於該對象的私有屬性的方式。那麼實例與實例的私有屬性或方法共有嗎?固然不!神奇的利用閉包就實現了數據的私有和封裝了
通過了上面的分析,咱們大概能夠了解到閉包的一些用處了吧。
這些也是耳熟能詳了!
MDN/JS/閉包
Understand JavaScript Closures With Ease
I never understood JavaScript closures