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