作用域鏈(scope chain)
當(dāng)js碰到可執(zhí)行代碼時(shí)抬驴,首先必然會(huì)先創(chuàng)建執(zhí)行上下文翩概,其中包括了
- 變量對(duì)象
- 作用域鏈
- this對(duì)象
定義
The scope chain property of each execution context is simply a collection of the current context's [VO] + all parent’s lexical [VO]s.
Scope = VO + All Parent VOs
Eg: scopeChain = [ [VO] + [VO1] + [VO2] + [VO n+1] ];
簡(jiǎn)而言之界拦, 作用域鏈?zhǔn)钱?dāng)前執(zhí)行上下文VO以及其父級(jí)詞法VO的集合。
函數(shù)的生命周期
創(chuàng)建階段
就是當(dāng)在預(yù)處理的時(shí)候碰到了一個(gè)函數(shù)扔字,當(dāng)前此函數(shù)并沒(méi)有被執(zhí)行旋奢,此時(shí)它的作用域已經(jīng)被確定了插勤。因?yàn)閖s使用的是詞法作用域沽瘦。
執(zhí)行階段
當(dāng)函數(shù)即將進(jìn)入執(zhí)行階段之前,它會(huì)先生成函數(shù)執(zhí)行上下文农尖,即上下文的創(chuàng)建階段析恋。上下文對(duì)象中的作用域鏈屬性也就在此刻被創(chuàng)建了。當(dāng)執(zhí)行上下文創(chuàng)建完畢盛卡,便進(jìn)入函數(shù)的執(zhí)行階段助隧。
內(nèi)容
作用域鏈既然是一個(gè)集合,它存儲(chǔ)在哪里滑沧?集合里面的內(nèi)容是如何被放進(jìn)去的呢并村?
存儲(chǔ)在哪里?
函數(shù)有一個(gè)內(nèi)部屬性叫做[[scope]
滓技,當(dāng)在函數(shù)的創(chuàng)建階段時(shí)哩牍,就會(huì)保存所有父級(jí)的變量對(duì)象到[[scope]]
中。它現(xiàn)在已經(jīng)有了除了自己本身執(zhí)行上下文中的VO以外的所有VO了令漂!
自己的VO
當(dāng)創(chuàng)建函數(shù)執(zhí)行上下文時(shí)膝昆,會(huì)先創(chuàng)建VO,然后將這個(gè)VO填充到作用域鏈的前端叠必。
舉個(gè)例子
var bar = 1
function foo() {
var baz = 1
console.log(baz)
}
foo()
詳細(xì)步驟如下:
1.js開(kāi)始執(zhí)行全局執(zhí)行上下文(globalContext)入棧
stack=[[globalContext]
]
foo函數(shù)被創(chuàng)建荚孵,確定作用域。保存作用域鏈到內(nèi)置屬性
[[scope]]
中
foo.[[scope]] = [globalContext.VO
]-
foo函數(shù)將要被執(zhí)行纬朝,首先要生成foo函數(shù)的執(zhí)行上下文(fooContext)并將其壓入執(zhí)行棧
stack=[[fooContext]
,[globalContext]
]收叶,此時(shí)fooContext中屬性還沒(méi)有完成賦值。
3.1 先復(fù)制foo.[[scope]]給fooContext
此時(shí)fooContext = {scopeChain: [foo.[[scope]]]}
3.2 確定VO
此時(shí)fooContext = { VO: { arguments: {length: 0}, baz: undefined }, scopeChain: [foo.[[scope]]] }
3.3 將VO對(duì)象放置于作用域鏈的前端
此時(shí)fooContext = { VO: { arguments: {length: 0}, baz: undefined }, scopeChain: [VO, foo.[[scope]]] }
函數(shù)開(kāi)始執(zhí)行
將VO依此賦值共苛,并執(zhí)行函數(shù)判没,foo執(zhí)行完畢蜓萄,fooContext從執(zhí)行棧中彈出
此時(shí)stack=[globalContext]