初始化作用域链 创建变量对象: 创建参数对象,检查上下文中的参数,初始化参数名称和值并创建引用副本 浏览上下文中的函数声明: 每找到一个函数,就在变量对象中添加一个新的属性,该属性命名为当前函数名,指向函数在内存中的引用 如果函数名已经存在,所对应的属性值将被重写,指向新的函数引用 浏览上下文中的变量声明: 每找到一个变量声明,在变量对象中添加一个新的属性,该属性命名为当前变量名,并给该属性赋值为undefined 如果变量名已经在变量对象中存在,将不进行任何操作,继续浏览当前上下文 确定上下文中this的指向 代码执行阶段: 分配变量值并且逐行执行当前上下文中的代码下面看一个例子:
function foo(i){ var a = 'hello', var b = function privateB(){}, function c(){}}foo(22);复制代码当调用函数foo的时候,创建阶段如下所示:
fooExecutionContext = { 'scopeChain': {...}, 'variableObject':{ arguments:{ 0:22, length:1 }, i:22, c:pointer to function c(){}, a:undefined, b:undefined }, 'this':{...}}复制代码正如所示,创建阶段确定了属性的名称,除了实参和形参以外并没有给他们赋值 。一旦创建阶段完成,执行流进入函数内部并且激活/执行代码阶段,执行后的代码如下所示:
fooExecutionContext = { 'scopeChain': {...}, 'variableObject':{ arguments:{ 0:22, length:1 }, i:22, c:pointer to function c(){}, a:'hello', b:pointer to function privateB(){} }, 'this':{...}}复制代码变量提升
网上很多关于JavaScript中变量提升的定义,定义中指出变量和函数的声明会被提升至当前函数作用域的顶部 。但是,并没有解释为什么会存在变量提升以及解释器如何创建激活对象,其实原因很简单,以下面的代码为例:
(function() { console.log(typeof foo); // function pointer console.log(typeof bar); // undefiendvar foo = 'hello', bar = function (){ return 'world'; };function foo(){ return 'hello'; };}())复制代码对于疑问和解答如下:
- 为什么我们可以在声明foo前访问它?
- 回顾创建阶段,变量在函数执行前已经被创建 。因此在函数执行前,foo已经在激活对象中创建 。
- foo被声明了两次,为什么foo的类型是function而不是undefined或者string?
- 尽管foo被声明两次,在创建阶段中,函数先于变量在激活对象中创建,并且如果激活对象中已经存在属性名,则不会影响已经存在的属性 。
- 所以,对于函数foo的引用首先在激活对象中已经创建,并且当解释器到达var foo语句,解释器发现在变量对象中foo已经被创建,因此就会跳过然后继续后续操作 。
- 为什么bar的值是undefined?
- bar实际上是一个值为函数的变量,在创建阶段变量会被初始化为undefined。
注:以上部分译自此文,如有侵权请告知;如有翻译不妥,还请各位读者指正 。以下是我对本文知识点的简要总结 。
简要总结
- 每个函数被调用的时候,都会创建一个新的执行上下文,并将当前执行上下文压入栈顶
- 每个执行上下文可以看作是具有以下3个属性的对象:
- 作用域链
- 变量对象/激活对象(VO/AO)
- this
- 每个执行上下文的建立分为两个阶段:创建阶段和执行阶段
- 执行上下文创建阶段,变量对象VO初始化的先后顺序:函数参数、函数声明、变量声明 。关于此部分两个常见问题的解答如下:
- 1、"函数声明过程中,变量对象中如果已存在同名的属性,则替换它的值"这句话如何理解?以下述代码为例:
function foo(i){ console.log(i); // function pointer var i = function (){}}foo(2);复制代码变量对象初始化第一步:函数参数复制代码executionContextObj = { 'scopeChain':{...}, 'variableObject':{ arguments:{ 0:2, length: 1, }, i:2 }}复制代码变量对象初始化第二步:函数声明 函数声明过程中,变量对象中已存在同名的属性i,将其值由"1"替换为新值"function"复制代码executionContextObj = { 'scopeChain':{...}, 'variableObject':{ arguments:{ 0:2, length: 1, }, i: function (){} }}复制代码
- 2、"变量声明过程中,变量对象中如果已存在同名的属性,则不进行任何操作"这句话如何理解?以下述代码为例:
function foo(i){ console.log(i); // function pointer var i = function (){}, var i = 9; } foo(2);复制代码 变量对象初始化第一步:函数参数复制代码executionContextObj = { 'scopeChain':{...}, 'variableObject':{ arguments:{ 0:2, length: 1, }, i:2 }}复制代码 变量对象初始化第二步:函数声明 函数声明过程中,变量对象中已存在同名的属性i,将其值由‘1’替换为新值‘function’复制代码executionContextObj = { 'scopeChain':{...}, 'variableObject':{ arguments:{ 0:2, length: 1, }, i: function (){} }}复制代码 变量对象初始化第三步:变量声明 变量声明过程中,变量对象中已存在同名的属性i,不进行任何操作 。复制代码executionContextObj = { 'scopeChain':{...}, 'variableObject':{ arguments:{ 0:2, length: 1, }, i: function (){} }, 'this':{...}}复制代码
推荐阅读
-
冬笋炒腊肉怎么做好吃,干笋炒腊肉的做法大全家常窍门-
-
-
晨娱秀场|看到他上脚效果,网友:又学会一招,华晨宇的“增高鞋”火了
-
魔方|火影:魔方挤牙膏式更新,夏日照美冥部分技能曝光,还有特殊机制
-
悠家娱乐@iPhone8大米价提前砸场!,iPhone12系列再次改回方正设计
-
[突击晓分队]16年来头一回见,美军嗅到危险味道?大批轰炸机紧急撤回本土
-
医世象|T细胞会让肿瘤预后更好吗?《BMJ》:喉癌患者的T细胞水平可提高生存率
-
史书中大量年幼或年盛便染疾而亡的帝王妃嫔是因为何种疾病为何这种情况如此频繁
-
娱乐胖姑娘|靳东和妻子看比赛,老干部气息荡然无存,笑得像个200斤的孩子!
-
去展网|「俄罗斯线材展」当地电缆产品年均增长潜力超过35%
-
特朗普@北京时间5月21日6时,美国传来绝望消息,特朗普:我对中国非常失望!
-
-
回来|唐太宗根本不希望唐僧早点回来,他游地府时就发现了玄机
-
美国|中国富婆给美国捐款96亿,在国内却负债200亿,如今报应来了!
-
白栀说|女儿重病,三个细节体现生活的不易,《我们永不言弃》打架坐牢
-
[盆栽]此款盆栽花,号称“爆盆神器”,全年开花,养护简单,很值得栽培
-
幽默搞笑段子集锦|你的胆子够大啊,我的女人你竟然也敢打?,幽默笑话:哥们
-
阿全吃美食|关键原因在于此战术,巴黎圣日耳曼赢下蒙彼利埃
-
券商|调整就是机会!节前主力跑步进场,芯片、半导体、券商更是被资金爆买,份额创出历史新高
-
|脚踏几条船?48岁男星自曝和小20岁女性约会,暧昧对象还有好几个