看了这一篇你还不理解什么是闭包——我直播吃翔!
看了這一篇你還不理解什么是閉包——我直播吃翔!
一. 前言
許多剛剛接觸JavaScript的小伙伴可能對閉包一臉懵逼,難于理解。對于那些有一點JavaScript使用經驗但未真正理解閉包的人來說,理解閉包會使你功力大增。恰巧這篇文章正是為你準備的。
有些小伙伴對閉包有了一定理解,但是并不清楚在JavaScript具體有哪些應用,現在我告訴你。在JavaScript中閉包無處不在,只是你還沒有發現它。閉包是基于詞法作用域書寫代碼時產生的自然結果,你不需要為了利用它而有意識的創建閉包,因為閉包已經在你的代碼中無處不在。
相信我,看完這篇文章,你一定會恍然大悟,原來我的代碼中已經到處都是閉包了。
二.準備知識
為了理解閉包,你必須對詞法作用域,作用域鏈有一定的理解。
三.閉包
閑話少說,直接上干貨
對于閉包直接了當的定義:
當函數可以記住并訪問所在的詞法作用域時,就產生了閉包 (你不知道的JavaScript)
閉包是指有權訪問另一個函數作用域中的變量的函數(JavaScript高級程序設計)
直接上代碼
function foo() {var a = 2;return function fun1() {console.log(a)} } var fun2 = foo() fun2() // 2在上面的例子中,fun1能夠訪問foo的內部作用域,我們把fun1作為一個值返回。在foo()執行后,把foo()的返回值 fun1 賦值給fun2并調用fun2。打印出了結果2.
此時,我們可以說fun1記住并訪問了所在的詞法作用域 或者說 fun2訪問了另一個函數作用域中的變量(fun2在全局作用域中聲明,訪問了foo的內部作用域)
由于引擎有自動的垃圾回收機制,在foo()執行后(不再使用),通常foo的整個內部作用域會被銷毀,對內存進行回收。閉包的神奇之處正是可以阻止這件事情的發生,因為fun1依然持有對該作用域的引用,這個引用就叫做閉包。
無論使用何種方式對函數類型的值進行傳遞,當函數在別處調用時,都可以看到閉包。
直接傳遞
或者間接的傳遞
var fn; function foo(){var a = 2;function baz() {console.log(a);}fn = baz; //對函數類型進行值得傳遞 } function bar(fn) {fn(); //閉包產生了 } foo() bar()無論通過什么手段將內部函數傳遞到所在的詞法作用域之外,它都會保持對定義時作用域的引用,這個函數無論在何處執行,都產生了閉包。
現在你理解閉包了嗎?思考一下你的代碼中產生了哪些閉包?
四. 無處不在的閉包
1.定時器
function wait(message) {setTimeout(function timer() {console.log(message)}, 1000) } wait('hello 閉包')將函數timer傳遞給setTimeout(), 內部函數timer 保持了對wait的作用域的引用,1000毫秒后打印出‘hello 閉包’。
2. 或者你使用過jQuery
function doSomeing(selector,doWhat) {$(selector).onClick(function(){console.log(doWhat)}) } doSomeing('#dom1','dowhat') doSomeing('#dom2','dowhat2')jquery的click事件處理函數保持了對doSomeing的作用域的引用。當click事件發生時,閉包也產生了。
3.ajax
function foo(someData){$.ajax({data:data,url:url,success:function(result){console.log(someData)}}) } foo('我也是閉包')ajax的success回調函數保持了對foo的作用域的引用,這也是閉包。
現在你理解了嗎?在定時器,事件監聽器,Ajax請求,跨窗口通信,Web Workers 或者其他任何的異步(或同步)任務中,只要使用了回調函數,實際上都是在應用閉包。
還有一個IIFE模式(立即執行函數)需要單獨說一下
var a = 2; (function(){console.log(a) //2 })()雖然它能夠正確的執行,但是嚴格意義上來說,它并不是一個閉包,因為它不是在本身的詞法作用域之外執行的。我認為,因為它是在原始定義時的詞法作用域中立即執行,也是保存了對原始定義時的詞法作用域的引用。它確確實實產生了閉包的效果。
五. 循環和閉包
思考下面的代碼
for (var i=1;i<=5;i++){setTimeout(function timer() {console.log(i)},i*1000) }正常情況下,我們對這段代碼的預期是每隔一秒輸出一個數字,1-5。但是實際上并不會這樣,這段代碼會每間隔一秒輸出一個數字6。這是為什么?
首先6從哪里來,當timer執行時,for循環早已結束,終止條件是i=6。
有些小伙伴可能會不明白了,timer不是創建了閉包,保持了對i的引用嗎?
沒錯。for循環了5次,創建了5個閉包,但是都是保持了對同一個i的引用(i是全局變量)。所以當timer執行時,i = 6,輸出6,沒毛病。
那么如何達到我們想要的效果,我們需要更多的閉包。循環過程中每個迭代都需要一個閉包
立即執行函數創建了一個新的作用域,使得延遲函數的的回調可以將新的作用域封閉在每個迭代內部。問題解決。
現在你理解閉包了嗎? 大聲的告訴我。你的代碼中還有其他閉包的應用嗎?現在停下你手中的事情,查看你最近的寫的代碼,看看有沒有閉包的應用吧?
如果這篇文章有幫到你,點個贊再走唄!
總結
以上是生活随笔為你收集整理的看了这一篇你还不理解什么是闭包——我直播吃翔!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 香港科大詹华强发明头发检测吸毒新技术
- 下一篇: 松下AJ-HPX298摄像机MXF视频删