對 作用域施无、作用域鏈、執(zhí)行上下文對象(GO | AO)喘沿、閉包 的個人理解:
- JS分為全局和局部作用域闸度,都屬于詞法作用域(與之相對是動態(tài)作用域)。
- 作用域本身是一個抽象概念蚜印,代表JS代碼可執(zhí)行范圍及查找變量的規(guī)則莺禁。
- 每個函數(shù)都有一個[[scope]]隱式屬性。
- [[scope]]是GO或者GO和AO的集合窄赋,而集合呈鏈式分布被稱為作用域鏈哟冬。
- 在JS代碼執(zhí)行之前或者說進入新的作用域之前,都會有一個短暫的預編譯過程忆绰。
- 預編譯的現(xiàn)象是浩峡,變量聲明提前和函數(shù)聲明提前。
- 預編譯過程中较木,創(chuàng)建了一個完整的執(zhí)行上下文對象:
- 創(chuàng)建GO(Global Object)或是AO(Activated Object)對象红符;
- 聲明的變量和函數(shù)的形參作為屬性名,undefined作為屬性值伐债,存入AO對象预侯;
- 將傳入的實參與形參統(tǒng)一,確定arguments對象峰锁;
- 將函數(shù)存入AO對象(感覺函數(shù)應該比變量更提前萎馅,變量遇到同名將忽略)。
- 創(chuàng)建GO/AO對象后虹蒋,開始確定作用域鏈糜芳,確定this
- 將其加入當前[[scope]]頂部,形成完整作用域鏈魄衅,供當前代碼執(zhí)行所用峭竣;
- 如遇函數(shù)聲明,將當前[[scope]]復制鏈式引用到該函數(shù)[[scope]]中晃虫。
- 代碼解釋執(zhí)行時皆撩,按作用域鏈順序查詢變量。
- 所以說哲银,作用域是預編譯時確定的扛吞,作用域鏈是代碼執(zhí)行時確定的。
- 當前作用域所有代碼執(zhí)行完畢后荆责,AO銷毀滥比。
- 只要函數(shù)一直被引用,[[scope]]中的函數(shù)聲明時所保存的鏈式關系就一直存在做院,鏈式節(jié)點中所儲存的數(shù)據(jù)對象就不會被銷毀盲泛,就可以一直被函數(shù)使用濒持,這就是閉包。
作用域查乒、作用域鏈精解
[[scope]]:函數(shù)對象中隱式屬性(供js引擎存让趾怼)郁竟,存儲了運行期上下文的集合玛迄。
作用域鏈:[[scope]]中所存儲的執(zhí)行期上下文對象的集合,這個集合呈鏈式鏈接棚亩,我們把這種鏈式鏈接叫做作用域鏈蓖议。
運行期上下文:當函數(shù)執(zhí)行時,會創(chuàng)建一個稱為執(zhí)行期上下文的內(nèi)部對象讥蟆。一個執(zhí)行期上下文定義了一個函數(shù)執(zhí)行時的環(huán)境勒虾,函數(shù)每次執(zhí)行時的執(zhí)行上下文都是獨一無二的,所以多次調(diào)用一個函數(shù)會導致創(chuàng)建多個執(zhí)行上下文瘸彤,函數(shù)每次執(zhí)行時修然,都會把新生成的執(zhí)行期上下文,填充到作用域鏈的最頂端质况。當函數(shù)執(zhí)行完畢愕宋,它所產(chǎn)生的執(zhí)行上下文被銷毀。
執(zhí)行期上下文:函數(shù)執(zhí)行時會創(chuàng)建一個稱為執(zhí)行期上下文的內(nèi)部對象结榄。一個執(zhí)行期上下文定義了一個函數(shù)執(zhí)行時的環(huán)境中贝,函數(shù)每次執(zhí)行時對應的執(zhí)行上下文都是獨一無二的,所以多次調(diào)用一個函數(shù)會導致創(chuàng)建多個執(zhí)行上下文臼朗,當函數(shù)執(zhí)行完畢邻寿,它所產(chǎn)生的執(zhí)行上下文被銷毀。
查找變量:從作用域鏈的頂端依次向下查找视哑。
立即執(zhí)行函數(shù)
此類函數(shù)沒有聲明绣否,在一次執(zhí)行后即釋放。適合做初始化工作挡毅。
// (function () {}()) // w3c建議
// (function () {})()
// 只有表達式才能被執(zhí)行符號執(zhí)行
// 報錯
function test () {...}()
// 不報錯蒜撮,不執(zhí)行
function test (a) {...}(1)
// 轉(zhuǎn)換為
function (a) {...}
(1)
閉包
當內(nèi)部函數(shù)保存到外部時,將會生成閉包慷嗜。
閉包會導致原有的作用域鏈不釋放淀弹,造成內(nèi)存泄漏(內(nèi)存不足,仿佛泄露)庆械。
閉包作用
- 實現(xiàn)公有變量
- 可以做緩存(存儲結構)
- 可以實現(xiàn)封裝薇溃,屬性私有化
- 模塊化開發(fā)
閉包精細版
// 考題
var x = 1;
if (function f () {}) {
x += typeof f;
}
console.log(x); // 1undefined