初始化作用域链 创建变量对象: 创建参数对象,检查上下文中的参数,初始化参数名称和值并创建引用副本 浏览上下文中的函数声明: 每找到一个函数,就在变量对象中添加一个新的属性,该属性命名为当前函数名,指向函数在内存中的引用 如果函数名已经存在,所对应的属性值将被重写,指向新的函数引用 浏览上下文中的变量声明: 每找到一个变量声明,在变量对象中添加一个新的属性,该属性命名为当前变量名,并给该属性赋值为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':{...}}复制代码
推荐阅读
-
「中新网」土耳其将因在叙军事行动被赶出北约?美媒:不太可能
-
每日搭配指南|简约优雅不失高级感,值得收藏!,30+女人秋季气质穿搭示范
-
网红白冰直播首秀被骂哭!大量网友质疑他搞分裂,曝更多细节内幕
-
-
肖战|肖战新剧为何广受欢迎?看戏里戏外,都是跨越年龄的吸引力!
-
【新华社】看美丽乡村 庆70华诞丨江苏省苏州市吴中区临湖镇灵湖
-
[萨拉赫]利物浦巨星不得了!年收入暴涨7300万 从第12飙升到第4
-
游龙战神|76.39 亿!高通担心的终于成真!联发科正成为市场“新宠”
-
-
证券日报|天山生物12天飙涨494.51% 谁能驱赶“疯牛”心中的“魔”?
-
银行|超预期!6月份结构性存款压降1.01万亿,产品"量价齐跌",套利空间还有多大?
-
背心|潮流的针织背心裙又火了,时髦经典,内搭衬衫就足够好看
-
枣庄在线|九斤超重宝宝在枣矿中心医院成功顺产!为妈妈和宝宝鼓掌
-
金融期货|中国金融期货交易所招聘2023年应届毕业生、博士后,12月18日前报名
-
次元快讯|为自己摘果庆生,网友:只可远观,不可细看!,杨丽萍59岁大寿
-
运势|八月中旬,事业红火,运势走高,赚足财帛,苦难熬完的属相
-
上观新闻|推动“夜游”和“沉浸式”文旅消费,南京路步行街又有新玩法
-
体育大亨▲美容养颜、滋润肌肤,身体棒棒哒!,适合女性吃的3种食物
-
张继科|张继科事件继续升级,牵扯借钱明星达到5位,张继科本人终于回应
-
#美味跳动餐料调料#夏季太热无心下厨房?幸亏屯了这些方便食品