摘要:閉包在我的前端學習中一直也是盲點,之前很多次看到別人提到我都是完全聽不懂。閉包導致的問題因為閉包會使得函數中的變量都保存在內存中,如不能及時釋放會對性能造成影響。
閉包在我的前端學習中一直也是盲點,之前很多次看到別人提到我都是完全聽不懂。最近一直看書和寫demo,對閉包也逐漸有所理解了,在這里寫下這篇博客。
從作用域鏈講起首先明確幾個概念:
1.JavaScript有函數級作用域,但沒有塊級作用域。
2.當要使用一個變量時,會沿著作用域鏈一步一步向上查找。
這里有一個demo:
var a = 1 function foo () { var a = 2 } foo() console.log(a) // a = 1
結果當然是a = 1,雖然在foo函數中重新聲明了a并且賦給它一個新的值,但是var聲明的變量只在foo()函數中有效,函數執行完畢就會銷毀,因此全局作用域中a的值沒有變化。
接下來再看這個demo:
for (var i = 0; i < 10; i++) { // code } console.log(i) // i = 10
這里在for循環結束之后仍然能在外部訪問到i,就是因為JavaScript沒有塊級作用域造成的。
閉包的特性閉包的主要特性就是可以從外部訪問函數內部的屬性和方法。先看一個demo:
function foo () { var a = 1 } foo() console.log(a) // 出錯
正常情況下,定義在函數內部的局部變量在函數執行完之后就會被銷毀,因此在外部是無法訪問局部變量的。那應該怎么做才能訪問呢?請繼續看:
function foo () { var a = 1 function bar () { console.log(a) } return bar } var baz = foo() baz() // 1
在這個demo中,我們在函數foo()內部又定義了一個函數bar(),并把它的函數名返回,這樣便能在外部實現對內部變量a的訪問。
有人問了:不是說函數執行結束之后內部變量會被銷毀嗎?你這不科學啊。
是的,原來函數執行結束之后內部變量的確會被銷毀,但是這里內部函數bar()在foo()執行時被返回并保存到了外部的baz中。這時候foo()執行完后,baz中依舊保存著對函數bar()的引用,因此bar()的作用域并沒有被釋放,根據之前提到的變量查找方式,在bar()函數的外層作用域中找到了a。
以上就是使用閉包能從外部訪問內部屬性的原理。
閉包的用途利用閉包強大的特性,最方便的用途就是實現私有變量和私有方法;另外,因為使用閉包會在內存中保存函數作用域,因此也能保存變量的值。
此話怎解?請看下面的demo:
for (var i = 1; i <= 5; i++) { setTimeout(function timer () { console.log(i) }, i * 1000) } // 6 6 6 6 6
這里的結果并不是我們預想中的每隔一秒依次輸出12345,而是每隔一秒輸出一個6,為什么會這樣呢?
首先,這個6是變量i最終退出循環時的值,其次var聲明的變量沒有塊級作用域,因此i實際上在全局作用域中都訪問得到。到這里不難理解了,setTimeout在循環結束后執行timer()函數時,訪問的i實際上在全局作用域中,此時i=6,因此后面的每個timer()函數執行訪問的i都是6。那么問題又來了,怎樣才能讓閉包訪問的i是我們想要的呢?
其實很簡單,讓i變成局部變量,在循環結束之后銷毀,這樣每個閉包訪問的就是保存在作用域中的局部變量i,也就是對應的12345。請看下面的demo:
for (var i = 1; i <= 5; i++) { (function (j) { setTimeout(function timer () { console.log(j) }, j * 1000) })(i) } // 1 2 3 4 5
這里用到了立即執行函數(IIFE),可能有些難以理解。那我一步一步解釋:立即執行函數其實就是JavaScript模仿塊級作用域的方法,這里你可以把它簡單看成一個函數調用。i就是傳給調用函數的參數,內部匿名函數的形參j接收到i的值開始執行setTimeout。此時j就是函數級作用域中的局部變量,在循環結束后,timer()函數開始執行,因為它是一個閉包,所以能訪問保存在作用域中的變量j,也就輸出了我們想要的結果。
當然更簡單的方式是用ES6的let來聲明i,這樣也是使得i變成局部變量,其他關于ES6這里就不多提,有興趣的可以自行看let基本用法。
閉包導致的問題1.因為閉包會使得函數中的變量都保存在內存中,如不能及時釋放會對性能造成影響。
2.在IE9以下的瀏覽器會有內存泄漏的問題。(關于這塊我后續會寫文章詳細說明)
本人經驗尚淺,目前對于前端仍在不斷摸索和學習,文章如有錯誤,歡迎各位指正。最后附上本人博客地址和原文鏈接,希望能向各位多多學習。
lbj的前端之路
原文鏈接:學習JavaScript之原型鏈
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/83125.html
摘要:接上回我寫了一篇關于閉包的博客學習之閉包,最后談到閉包導致的問題時留了一個尾在以下的瀏覽器中會有內存泄漏的問題。今天的博客就繼續探索一下內存泄漏的問題。博客地址的前端之路原文鏈接學習之內存泄漏 接上回我寫了一篇關于閉包的博客《學習JavaScript之閉包》, 最后談到閉包導致的問題時留了一個尾: 在IE9以下的瀏覽器中會有內存泄漏的問題。 今天的博客就繼續探索一下內存泄漏的問題。 淺...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。異步編程入門的全稱是前端經典面試題從輸入到頁面加載發生了什么這是一篇開發的科普類文章,涉及到優化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結思考,循序漸進的理解 TypeScript。 網絡基礎知識之 HTTP 協議 詳細介紹 HTT...
摘要:在一個閉包環境內修改變量值,不會影響另一個閉包中的變量。直到看到函數閉包閉包這篇文章的代碼一部分,終于明白其中的邏輯了。 閉包 閉包定義:指擁有多個變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。函數內部可以直接讀取全局變量。函數內部變量無法在函數外部訪問。函數內部聲明要用var或者let聲明,不然會變成全局變量鏈式作用域:子對象會一級級向上尋找...
摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進擊之詞法作用域與作用域鏈什么是閉包閉包的含義就是閉合,包起來,簡單的來說,就是一個具有封閉功能與包裹功能的結構。在中函數構成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來,簡單的來說,就是一個具有封閉功能與包裹功能的結構。所謂的閉包就是...
摘要:今天同學去面試,做了兩道面試題全部做錯了,發過來給道典型的面試題前端掘金在界中,開發人員的需求量一直居高不下。 排序算法 -- JavaScript 標準參考教程(alpha) - 前端 - 掘金來自《JavaScript 標準參考教程(alpha)》,by 阮一峰 目錄 冒泡排序 簡介 算法實現 選擇排序 簡介 算法實現 ... 圖例詳解那道 setTimeout 與循環閉包的經典面...
閱讀 1573·2021-11-24 09:39
閱讀 1063·2021-11-22 15:11
閱讀 2210·2021-11-19 11:35
閱讀 1639·2021-09-13 10:37
閱讀 2475·2021-09-03 10:47
閱讀 2162·2021-08-30 09:47
閱讀 1643·2021-08-20 09:39
閱讀 2922·2019-08-30 14:13