作用域闭包
再看閉包定義
閉包的概念看了幾十回不止了,但是讓我描述一遍,我應(yīng)該還是說(shuō)不出個(gè)什么東西。下面就再來(lái)看看閉包的概念:
當(dāng)函數(shù)可以記住并訪問(wèn)所在的詞法作用域時(shí),就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行。
下面配合一個(gè)例子來(lái)更好的理解
function foo() {var a = 2;function bar() {console.log(a) // 2}bar() } foo() 復(fù)制代碼看了這段代碼,很容易有個(gè)疑惑,這就是閉包嗎?這不應(yīng)該是作用域的例子嗎?對(duì),這就是詞法作用域的查找規(guī)則。但是,這些規(guī)則就是閉包的一部分。
通過(guò)這段代碼,我們不能清晰的理解閉包的意義,卻可以很容易的理解詞法作用域。下面我們來(lái)稍加修改一下,來(lái)看看到底閉包應(yīng)該什么樣子,和詞法作用域有什么聯(lián)系?
function foo() {var a = 2function bar() {console.log(a)}return bar } var baz = foo() baz() // 2 復(fù)制代碼再結(jié)合定義來(lái)看,就很容易理解了。
bar函數(shù)可以訪問(wèn)所在函數(shù)foo的詞法作用域。這就產(chǎn)生了閉包。而bar函數(shù)本身作為返回值進(jìn)行傳遞,并且賦值給了baz,在foo的詞法作用域外,我們執(zhí)行baz,依然可以執(zhí)行。
下面我們來(lái)進(jìn)一步的理解閉包。
依據(jù)JavaScript的垃圾回收機(jī)制,當(dāng)var baz = foo()執(zhí)行之后,理論上來(lái)說(shuō)foo的內(nèi)容不會(huì)再被使用,所以應(yīng)該釋放所在的內(nèi)存空間,但是這時(shí)候卻不會(huì)被回收,這就是閉包的神奇的地方。
bar()依然持有該作用域的引用。這個(gè)引用就是閉包。
所以當(dāng)我們執(zhí)行baz時(shí),以然可以訪問(wèn)到a。
下面再來(lái)幾個(gè)例子,加深我們對(duì)閉包的理解
function foo() {var a = 2function baz() {console.log(a)}bar(baz) } function bar(fn) {fn() } 復(fù)制代碼這個(gè)例子和之前的區(qū)別在于,前面一個(gè)是返回一個(gè)函數(shù)作為調(diào)用的值,而這一個(gè)是直接傳遞函數(shù)給另一個(gè)函數(shù)作為變量的值。這都達(dá)到了閉包的效果。
另外,傳遞也可以是間接的,我們可以直接賦值給一個(gè)全局變量。并在其他地方調(diào)用。
var fn function foo() {var a = 2function baz() {console.log(a)}fn = baz } function bar() {fn() } foo() bar() 復(fù)制代碼循環(huán)和閉包
我第一次接觸到閉包的這個(gè)概念是在這樣一次使用之后:
for(var i = 0; i < 5;i++){setTimerout(function timer(){console.log(i)}, i * 1000) } 復(fù)制代碼天真的我等待這每秒輸出一個(gè)0-4的數(shù)字,等結(jié)果出現(xiàn)的時(shí)候,我直接蒙了,這是JavaScript出bug了啊。
現(xiàn)在看來(lái)但是的自己是多么的無(wú)知。。。
解決辦法對(duì)現(xiàn)在的自己來(lái)說(shuō)很簡(jiǎn)單了
for (var i=1; i<=5; i++) {(function() {var j = i;setTimeout( function timer() {console.log( j );}, j * 1000 );})(); } 復(fù)制代碼或這樣
for (var i=1; i<=5; i++) {(function(j) {setTimeout( function timer() {console.log( j );}, j * 1000 );})( i ); } 復(fù)制代碼原因也是很簡(jiǎn)單的,
塊作用域
es6的出現(xiàn)讓我們有了解決上面問(wèn)題的更簡(jiǎn)單的方法。那就是利用塊作用域。
for (let i=1; i<=5; i++) {setTimeout( function timer() {console.log( i );}, i*1000 ); } 復(fù)制代碼模塊
在大規(guī)模使用es6的模塊機(jī)制的現(xiàn)在,我們對(duì)模塊已經(jīng)有了也許詳細(xì)的認(rèn)識(shí)。但是,具體的模塊是怎么實(shí)現(xiàn)的呢?我們來(lái)看一下。
function Foo() {var something = "cool";var another = [1, 2, 3];function doSomething() {console.log( something );}function doAnother() {console.log( another.join( " ! " ) );}return {doSomething: doSomething,doAnother: doAnother} } var foo = Foo() foo.doSomething() foo.doAnother() 復(fù)制代碼上面的這個(gè)模式就是最基本的模塊了,很顯然,我們利用了閉包的知識(shí)。
Foo函數(shù)對(duì)外暴漏了一個(gè)對(duì)象,這個(gè)對(duì)象包含了doSomething和doAnother兩個(gè)函數(shù)。
模塊模式需要具備的兩個(gè)必要條件:
上面的模塊我們可以調(diào)用很多次,每次都會(huì)創(chuàng)建一個(gè)新的模塊實(shí)例。如果我們只需要使用一次時(shí),我們可以這么做
var foo = (function CoolModule() {var something = "cool";var another = [1, 2, 3];function doSomething() {console.log( something );}function doAnother() {console.log( another.join( " ! " ) );}return {doSomething: doSomething,doAnother: doAnother}; })(); foo.doSomething(); // cool foo.doAnother(); // 1 ! 2 ! 3 復(fù)制代碼看,閉包是不是無(wú)處不在。
當(dāng)然,模塊還可以接受參數(shù),根據(jù)參數(shù)來(lái)設(shè)置一些模塊內(nèi)的內(nèi)容,這都是很簡(jiǎn)單的應(yīng)用,很容易理解。
es6的使用,使我們對(duì)模塊的使用更加的方便。es6會(huì)將文件作為獨(dú)立的模塊來(lái)處理,每一個(gè)暴露的函數(shù)都可以用關(guān)鍵詞export來(lái)導(dǎo)出。使用模塊暴露出來(lái)的函數(shù)時(shí),我們用import來(lái)引入。
結(jié)
通過(guò)這一次學(xué)習(xí),對(duì)閉包有個(gè)更加深入的認(rèn)識(shí),我么再來(lái)看一下閉包的定義:
當(dāng)函數(shù)可以記住并訪問(wèn)所在的詞法作用域, 即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行, 這時(shí) 就產(chǎn)生了閉包
另一個(gè)重要的概念就是模塊:
閉包無(wú)處不在,以后的工作學(xué)習(xí)中能熟練的運(yùn)用閉包,一眼認(rèn)出來(lái)是閉包就是真正的掌握了
轉(zhuǎn)載于:https://juejin.im/post/5be15446f265da6174644e36
總結(jié)
- 上一篇: 非常便捷的本地Mock
- 下一篇: 兄弟连区块链教程Fabric1.0源代码