文章来源:http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/
明明全局变量定义一个值,为什么函数里不能用?java里函数完全可以使用全局变量值,这里有点糊涂了?
且提示值显示undefine呢! 蒙了,哪么这篇文章将回答这个问题?
What is the Execution Context?
当代码在JavaScript着运行时,它的执行环境非常重要,主要有下面3种代码:
1、Global code –代码第一次执行默认的环境。
2、Function code –每次执行流进入函数体时的环境
3、Eval code – 在eval 函数里面被执行的文本,
这篇文章目的在于理解作用域,让我们想想execution context 关键字,正在解析当前代码的作用域,
例子,包含了全局和函数/本地 context 解析代码 两个。
global context
由紫线边框表示 且有3个不同 function contexts
由绿,蓝,橘边界表示,只有一个global context
, 程序里任何其它的context 能访问global context .function contexts
, 且调用每一个函数 时,建立一个新的context, 也就是建立一个私有的作用域,当前函数作用域的外面不能直接访问当前函数里面声明的任何东西 。上例, 一个fuction能访问声明在当前context外面的一个变量,但是一个外面的context不能访问里面的变量/函数, Why does this happen? How exactly is this code evaluated?Execution Context Stack
浏览器解释JavaScript是作为一个单线程实现的。浏览器在一个时间里仅仅一个事情执行,其它的动作和事件在所谓的 Execution Stack 排队
. 下面的图是单一线程堆栈的抽象视图。
正如我们已经知道,一个浏览器什么时候第一次装载你的脚本,首先默认进入the global execution context
. 如果, 在你的global 代码,你调用一个函数,你程序的顺序流进入调用函数里面,建立一个新的 execution context
和把那个 context压入到 execution 堆栈顶部。
如果在当前函数里你调用另一个函数,相同的事情发生。代码执行流 进入内部函数里面, 建立 一个新的 execution context
,并压入到一个已经存在堆栈的顶部.浏览器将总是执行当前 execution context
,它存在堆栈顶部,且一旦函数完成执行当前的 execution context
, 它将从堆栈中弹出,返回对下一个context对象进行控制。
下面的例子显示一个递归函数和一个程序的执行堆栈, 这个代码简单的调用自己3次, i从0开始自增。每次函数 foo
被调用, 一个新的 execution context被建立. 一旦一个 context 已经完成了执行, 从堆栈中弹出且控制返回到下面的context,一直执行到再一次到达global context
.
(function foo(i) {
if (i === 3) {
return;
}
else {
foo(++i);
}
}(0));
关于
execution stack
有5点需要记住:
- 单线程.
- 异步执行.
- 全局context
- 无限个 function contexts.
- 调用每一个函数建立一个新
execution context
, even a call to itself
executionContextObj = { 'scopeChain': { /* variableObject + all parent execution context's variableObject */ }, 'variableObject': { /* function arguments / parameters, inner variable and function declarations */ }, 'this': {} } |
2、在执行一个函数的代码之前,建立execution context.
3、进入execution context.建立阶段:
3.1 初始化作用域链
3.2 建立变量对象
3.2.1 建立参数对象,检查参数的context, 初始化名字和值且建里一个引用复制
3.2.2 扫描函数声明的context:
3.2.2.1 对每一个发现的函数,建立变量对象( variable object)的一个属性 ,即该函数的函数名。
variable object
,也就是变量名字,且undefined.作为初始化值。
variable object里
, 什么也不做且继续扫描。
- 运行 / 解释函数代码在 context 里 且给变量赋值,代码是一行一行的执行。
function foo(i) {
var a = 'hello';
var b = function privateB() {
};
function c() {
}
}
foo(22);
foo(22)上
, 建立阶段 creation stage
,主要是execution context.建立阶段如下:
fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {// 建立参数对象
0: 22,
length: 1
},
i: 22,//参数变量
c: pointer to function c()//扫描函数声明的context:
a: undefined,// 扫描变量声明的context:
b: undefined // 扫描变量声明的context:
},
this: { ... }
}
creation stage
处理变量属性名字的定义为undefined,并没有对他们赋值, with the exception of formal arguments / parameters. 一旦 creation stage
已经完成, 下面的执行进入函数和代码的 execution stage
看起来像这样。
fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: 'hello',
b: pointer to function privateB()
},
this: { ... }
}
hoisting
,解释变量和函数的声明是提升到他们函数作用域的顶部,然而,没有一个人详细的解释发生理由,现在你可以用刚学的知识解释如何建立一个 activation object
,
(function() {
console.log(typeof foo); // function pointer
console.log(typeof bar); // undefined
var foo = 'hello',
bar = function() {
return 'world';
};
function foo() {
return 'hello';
}
}());
- 为什么我们在什么foo之前能访问 foo ?
- 如果我们观察建立阶段
creation stage
, 我们知道多个变量已经在activation / code execution stagez
之前建立,因此foo作为执行的函数首次已经定义在activation object
- 如果我们观察建立阶段
- Foo是声明两次,为什么foo显示的
function
而不是undefined
或string
?- 即使
foo
是声明两次, 我们从creation stage
已经知道,activation object
函数建立在变量之前, 且如果属性名字已经在activation object
里面,我们简单忽略后面的声明. - 因此, 在
activation object
上,一个对象function foo()
的引用是第一次建立, 当解释器得到var foo
时,我们已经看属性名字foo
存在,所以代码什么也没做,然后继续。
- 即使
- 为什么 bar
undefined
?-
bar
是一个函数赋值的一个变量, 我么知道所有变量是在creation stage
建立的,但是他们的初始化值是undefined
.
-