我終于理解了閉包
本文寫于 2020 年 5 月 14 日
閉包這個詞一聽就很高級,令人害怕,
但實際上,閉包非常的強大,JS 的精髓之一就是閉包,
實際上,我們經常在使用閉包,而不自知!
Kyle Simpson 在書中將掌握閉包,比喻為:
不像 Luke 一樣接受訓練才能掌握原力,而是像 Neo 見到矩陣一樣,
我們其實一直都在不自覺地使用著閉包,一旦理解了閉包,就會如同重生一般,鳳凰涅槃,
函式的作用域
function foo() {
let hello = 'hello world'
console.log(hello)
}
這個非常簡單的函式,如果呼叫foo(),那么在短暫的時間過后,hello 變數就會消失的無影無蹤,
仿佛從來沒有出現過一樣,
但是如果我們這么寫:
function foo() {
let hello = 'hello world'
function bar() {
console.log(hello)
}
return bar
}
let hey = foo()
觀察一下,雖然在let hey = foo()的時候,foo 已經執行過了,但是如果我們使用hey(),依然可以使用 hello 變數!
這就是閉包,
foo()在執行之后,正常情況下,整個內部的作用域都會被銷毀,因為 JS 引擎會幫助我們自動回收垃圾,
而閉包神奇的可以阻止這件事情的發生,讓內部作用域依然存在,不被回收,讓bar()來使用,
bar()在foo()執行結束后,依然保持對該作用域的參考,這就叫做閉包!
我們經常都在使用閉包
上面的代碼是不是十分眼熟?
function foo() {
const hello = 'hello world'
setInterval(function bar() {
alert(hello)
}, 1000)
}
這個定時器,是不是閉包?
定時器、事件監聽器、Ajax 請求、跨視窗通信……只要用到了回呼函式,都是閉包!
以前 ES5 的時候,大家一直有個困惑:
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
}, 1000 * i)
}
為什么這個代碼每個一秒會輸出一次 5?
就算輸出也應該是 4 啊,為什么呢?
因為 var 的 i,作用域其實在外面,最后一次結束的時候,i 已經是 5 了,
而異步操作,都是在 for 回圈結束之后才執行的,
也就是說,每次回圈結束,都會記住setTimeout(() => { console.log(i) }, n000),然后在 for 結束之后,統一的把 i 傳入 console,
那自然而然的,會都輸出 5 了,
那這個怎么讓他每一次回圈都能夠實作,console 對當前 i 的參考呢?這也是閉包呀,
把 setTimeout 單獨放到一個作用域里,然后再回圈的時候把當前的 i 傳進去就可以了!非常簡單!
for (var i = 0; i < 5; i++) {
;(function (j) {
setTimeout(() => {
console.log(j)
}, 1000 * j)
})(i)
}
成功了!
但實際上,如果使用 let,完全不會有這種需要“泄露”的情況,
(完)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/90142.html
標籤:JavaScript
