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、"函数声明过程中,变量对象中如果已存在同名的属性,则替换它的值"这句话如何理解?以下述代码为例:
- 2、"变量声明过程中,变量对象中如果已存在同名的属性,则不进行任何操作"这句话如何理解?以下述代码为例:
推荐阅读
- mysql中四种存储引擎的区别和选择
- 比较:mysql中的truncate、delete与drop
- 中国文化的精髓--茶具
- 不可不知的 5 种 JavaScript 代码编辑器
- 探讨紫砂茶具中的紫砂文化
- 如何在小程序中盈利?
- 封神演义中的元始天尊的师傅是谁 封神榜元始天尊和通天教主
- Redis的链表结构
- 3名初中生女生从18楼坠下 18米高楼女孩意外坠楼
- MySQL使用WHERE子句来过滤结果集中的行记录