您好,登錄后才能下訂單哦!
這篇文章主要講解了“JS閉包到底是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“JS閉包到底是什么”吧!
在JavaScript這門語言中,閉包是它的核心基礎之一,可以說是一個特色了,但是很多從事前端工作的程序員并沒有真正的理解它!
閉包有多重要?如果你是初入前端的朋友,我可以肯定得告訴你,前端面試,必問閉包。面試官們常常用對閉包的了解程度來判定面試者的基礎水平,保守估計,10個前端面試者,至少5個都死在閉包上。
通過本文講解,希望你可以重新認識一下閉包!
函數調用時發生了什么?
為了理解閉包,首先我們需要完全理解 JavaScript 到底是如何工作的!
那么函數調用是會發生什么呢?
當瀏覽器在解析 JS代碼的時候,會進行一個預解析的操作,會有一個js解析器,里面會執行其中的兩步操作:
1、預解析,找一些東西(var function 參數);
2、逐行去解讀代碼。
當解析器解讀函數調用時,會將整個函數執行一個入棧操作,并為函數創建一個新的執行上下文。函數內部可以看作是一個小的區域,它有它自己的作用域和執行線程,也要逐行解讀。當函數顯式返回(到達return語句)或隱式返回(默認情況下函數返回undefined)時,函數將出棧,其執行上下文也將被銷毀。
閉包是什么鬼?
我們先來看下這段代碼:
let name = "John" function greet() { const greeting = "Hi" function printHi() { console.log(greeting + ' ' + name) } printHi() } name = "Jane" greet() // "Hi Jane"
我們發現,子函數 printHi 可以訪問全局作用域和其父函數 greet 的局部作用域。
注意,我們實際上可以訪問函數執行期間可用的“新”數據,而不是聲明。這就是詞法作用域在 JavaScript 中的工作方式。
但是如果我們返回一個函數,而不是僅僅在外部函數體中調用它,會發生什么呢?
看好了,奇跡出現了!
從一個函數中返回的函數不僅僅是一個簡單的函數定義,它是這個定義加上它可以訪問并需要執行的變量,這些變量存儲在它附帶的詞法作用域中。
我們剛剛描述的就是閉包。從形式上講,閉包是一個「即使在詞法范圍之外調用,仍可以記住它的詞法范圍」的函數。
function creator(num) { return function() { num = num * 2 console.log(num) } } const double = creator(5) double() //10 double() //20 const double2 = creator(7) double2() // 14 double2() //28 double() // 40
正如我們在上面的代碼片段中看到的,每當我們調用 double 時,它都會更新存儲在其詞法作用域中的同一個變量(來自其父函數的num),從技術上講,這是函數所具有的隱藏 [[scope]] 屬性。
如果你想知道閉包到底有什么用,請繼續看下面的示例。
模塊封裝
閉包允許我們保護或隱藏某些信息。[[scope]] 是一個隱藏的屬性,所以我們不能像使用標準對象那樣訪問和更新它。還有一點很重要,我們可以返回一組存儲在對象上的函數,它們都是閉包。
在下面的代碼片段中,我們利用了所謂的IIFE(立即執行函數),它允許我們消除調用外部函數的中間步驟,就像我們在賦值時直接調用它一樣。
const myModule = (function(){ const apiKey = "123456789" return { displayKey() { console.log(apiKey) } } })() myModule.displayKey() // "123456789"
如果我們將這個模塊 export 出去, 提供給其他人使用,我們為他準備的 API 不允許他更改 apiKey,這就做到了只讀屬性,除了在源代碼中重寫它之外,調用方不可能更改它。
緩存和記憶化
假設您想創建一個簡單的ID生成器。為了確保總是返回比上一個高的數字,也可以使用閉包。我們將緩存當前變量中最高的 ID 值。
const newID = (function() { let current = 0 return function() { return ++current } })() newID() // 1 newID() // 2
當我們的算法時間復雜度很高時,這種緩存方式就非常有用,我們可以將部分結果存儲在緩存中,當我們使用更高的數字進行計算時,我們可以使用緩存中的數據作為基礎。這個過程叫做記憶化。一個最好的例子就是處理處理遞歸問題,比如斐波那契序列。
const factorialMemo = (function() { const cache = {} return function factorial(n) { if(n === 1 || n === 0) { return 1 } else if (cache[n]) { return cache[n] } else { cache[n] = n * factorial(n-1) return cache[n] } } })() factorialMemo(5) //120 // cache object looks like {'2': 2, '3' : 6, '4' : 24, '5' : 120} factorialMemo(6) // 6 * cached 120
感謝各位的閱讀,以上就是“JS閉包到底是什么”的內容了,經過本文的學習后,相信大家對JS閉包到底是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。