大白話解釋作用域和閉包是個啥 作用域的分類
常見的變量作用域就是 靜態作用域(詞法作用域) 與 動態作用域 。詞法作用域注重的是 write-time ,即 編程時的上下文 ,而 動態作用域 則注重的是 run-time ,即 運行時上下文 。詞法作用域中我們需要知道一個函數 在什么地方被定義 ,而動態作用域中我們需要關心的是函數 在什么地方被調用
而 javascript 使用的則是詞法作用域
let value = 1 function foo() { console.log(value) } function bar() { let value = 2 foo() } bar() // 1
在 javascript 解析模式中,當 foo 被調用的時候:
檢查 foo 函數內是否存在 value
存在則使用這個 value
不存在則根據書寫代碼的位置查找上一層代碼(這里的 window),找到 value 為 1
在動態作用域的解析模式中,當 foo 被調用的時候:
檢查 foo 函數內是否存在 value
存在則使用這個 value
不存在則根據調用該函數的作用域中去尋找也就是這里的 bar 函數,找到 value 為 2
在從內層到外層的變量搜索過程中,當前作用域到外層作用域再到更外層作用域直到最外層的全局作用域,整個搜尋軌跡就是 作用域鏈
變量的兩種查找類型一種是 rhs 一種是 lhs
假設有這么一段代碼:
console.log(a) // 輸出 undefined console.log(a2) // 報錯 a2 is not defined var a = 1
上述代碼實際上在變量提升的作用下應該是下面這個順序:
var a console.log(a) // 輸出 undefined console.log(a2) // 報錯 a2 is not defined a = 1
第一個 console 輸出 undefined 因為還未執行賦值操作,查詢過程是 rhs 也就是 right-hand-side
第二個 console 報錯,是因為 rhs 查詢 a2 變量不存在因此報錯
a = 1 則是賦值操作,也就是 lhs,英文 left-hand-side
閉包閉包是啥?閉包就是從函數外部訪問函數內部的變量,函數內部的變量可以持續存在的一種實現。
在了解了詞法作用域和變量的查詢方式之后,我們看看一個簡單的閉包的實現邏輯:
function f() { let num = 1 // 里面的變量 function add() { num += 1 } function log() { console.log(num) } return { add, log } // 我要到外面去了 } const { add, log } = f() log() // 1 我從里面來,我在外面被調用,還是可以獲得里面的變量 add() log() // 2
首先定義一個 f 函數,函數內部維護一個變量 num,然后定義兩個函數 add 和 log
add 函數每次調用會增加 num 的值
log 函數每次調用會打印 num 的值
然后我們將兩個函數通過 return 方法返回
緊接著先調用外部的 log 方法打印 f 方法維護的 num,此時為 1
然后調用外部的 add 方法增加 num 的值
最后再次調用 log 方法打印 num,此時則為 2
為什么外部定義的 add 函數可以訪問 f 函數內部的變量呢。正常情況下外部作用域不可訪問內部作用域的變量,但我們將內部訪問其內部變量的方法“導出”出去,以至于可以從外部直接調用函數內部的方法,這樣我們就可以從函數的外部訪問函數內部的變量了。
經典的 for 循環問題arr = [] for (var i = 0; i < 10; i ++) { arr[i] = function() { console.log(i) } } arr[2]() // 10
首先我們知道 for 循環體內的 i 實際上會被定義在全局作用域中
每次循環我們都將 function 推送到一個 arr 中,for 循環執行完畢后,arr 中張這樣:
隨后我們執行代碼 arr[2]() 此時 arr[2] 對應的函數 function(){ console.log(i) } 會被觸發
函數嘗試搜索函數局部作用域中的 i 變量,搜索不到則會繼續向外層搜索,i 被定義到了外層,因此會直接采用外層的 i,就是這里的全局作用域中的 i,等到這個時候調用這個函數,i 早已變成 10 了
那么有什么方法能夠避免出現這種情況嗎?
ES6 之前的解決方案:
了解了閉包我們就知道了閉包內的變量可以持續存在,所以修改代碼將 arr 中的每一項改為指向一個閉包:
arr = [] for (var i = 0; i < 10; i ++) { arr[i] = (function() { // 這是一個閉包 var temp = i // 閉包內部維護一個變量,這個變量可以持續存在 return function() { console.log(temp) } })() }
這樣程序就能按照我們的想法運行了
ES6 之后的解決方案:
ES6 之后我們就有了塊級作用域因此代碼可以改為這樣:
arr = [] for (let i = 0; i < 10; i ++) { // 使用 let arr[i] = function() { console.log(i) } }
在使用 let 之后,我們每次定義 i 都是通過 let i 的方法定義的,這個時候 i 不再是被定義到全局作用域中了,而是被綁定在了 for 循環的塊級作用域中
因為是塊級作用域所以對應 i 的 arr 每一項都變成了一個閉包,arr 每一項都在不同的塊級作用域中因此不會相互影響
參考:
https://github.com/mqyqingfen...
https://www.datchley.name/bas...
https://segmentfault.com/a/11...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102467.html
摘要:在實際開發項目中,有時我們會用到自定義按鈕因為一個項目中,眾多的頁面,為了統一風格,我們會重復用到很多相同或相似的按鈕,這時候,自定義按鈕組件就派上了大用場,我們把定義好的按鈕組件導出,在全局引用,就可以在其他組件隨意使用啦,這樣可以大幅度 在實際開發項目中,有時我們會用到自定義按鈕;因為一個項目中,眾多的頁面,為了統一風格,我們會重復用到很多相同或相似的按鈕,這時候,自定義按鈕組件就...
摘要:代碼整潔之道整潔的代碼不僅僅是讓人看起來舒服,更重要的是遵循一些規范能夠讓你的代碼更容易維護,同時降低幾率。另外這不是強制的代碼規范,就像原文中說的,。里式替換原則父類和子類應該可以被交換使用而不會出錯。注釋好的代碼是自解釋的。 JavaScript代碼整潔之道 整潔的代碼不僅僅是讓人看起來舒服,更重要的是遵循一些規范能夠讓你的代碼更容易維護,同時降低bug幾率。 原文clean-c...
對比內容UCloudStackZStackVMwareQingCloud騰訊TStack華為云Stack優勢總結?基于公有云自主可控?公有云架構私有化部署?輕量化/輕運維/易用性好?政府行業可復制案例輕量化 IaaS 虛擬化平臺?輕量化、產品成熟度高?業內好評度高?功能豐富、交付部署快?中小企業案例多全套虛擬產品及云平臺產品?完整生態鏈、技術成熟?比較全面且健全的渠道?產品成熟度被市場認可,市場占...
摘要:能跨平臺地設置及使用環境變量讓這一切變得簡單,不同平臺使用唯一指令,無需擔心跨平臺問題安裝方式改寫使用了環境變量的常見如在腳本多是里這么配置運行,這樣便設置成功,無需擔心跨平臺問題關于跨平臺兼容,有幾點注意 cross-env能跨平臺地設置及使用環境變量, cross-env讓這一切變得簡單,不同平臺使用唯一指令,無需擔心跨平臺問題 1、npm安裝方式 npm i --save-de...
摘要:引入的模塊引入的使用將打包打包的拆分將一部分抽離出來物理地址拼接優化打包速度壓縮代碼,這里使用的是,同樣在的里面添加 const path = require(path); //引入node的path模塊const webpack = require(webpack); //引入的webpack,使用lodashconst HtmlWebpackPlugin = require(ht...
閱讀 652·2021-11-24 09:39
閱讀 3012·2021-11-23 10:06
閱讀 981·2021-10-08 10:05
閱讀 754·2019-08-30 10:49
閱讀 1719·2019-08-29 14:08
閱讀 1325·2019-08-29 12:48
閱讀 3320·2019-08-26 14:04
閱讀 3613·2019-08-26 13:50