JS解释器执行代码的过程:
- 定位到调用函数的code处;.
-
执行
function
code前, 创建
execution context
. -
进入创建阶段:
-
初始化
Scope Chain
. -
创建
variable object
:-
创建
arguments object
, check the context for parameters, initialize the name and value and create a reference copy. -
函数声明(Scan the context for function declarations):
-
For each function found, create a property in the
variable object
that is the exact function name, which has a reference pointer to the function in memory. - 如果函数名已经存在,引用指针值将被重写(If the function name exists already, the reference pointer value will be overwritten.)
-
For each function found, create a property in the
-
(变量声明)Scan the context for variable declarations:
-
For each variable declaration found, create a property in the
variable object
that is the variable name, and initialize the value as
undefined
. -
如果变量名已经存在,什么都不做,不影响已存在的变量声明。(If the variable name already exists in the
variable object
, do nothing and continue scanning).
-
For each variable declaration found, create a property in the
-
创建
-
确定context中
"this"
的值。
-
初始化
-
Activation / Code Execution Stage:
-
AO是在进入函数的执行上下文时创建的,为上下文中的变量赋值。(
Run / interpret the function code in the context and assign variable values as the code is executed line by line).
-
example:
function foo(i) {
var a = 'hello';
var b = function privateB() {
};
function c() {
}
}
foo(22);
On calling
foo(22)
, the
creation stage
looks as follows:
fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: undefined,
b: undefined
},
this: { ... }
}
As you can see, the
creation stage
handles defining the names of the properties, not assigning a value to them, with the exception of formal arguments / parameters. Once the
creation stage
has finished, the flow of execution enters the function and the
activation
/ code
execution stage
looks like this after the function has finished execution:
fooExecutionContext = {
scopeChain: { ... },
variableObject: { //此处应该叫做AO
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: 'hello',
b: pointer to function privateB()
},
this: { ... }
}
A Word On Hoisting
You can find many resources online defining the term
hoisting
in JavaScript, explaining that variable and function declarations are
hoisted
to the top of their function scope. However, none explain in detail why this happens, and armed with your new knowledge about how the interpreter creates the
activation object
, it is easy to see why. Take the following code example:
(function() {
console.log(typeof foo); // function pointer
console.log(typeof bar); // undefined
var foo = 'hello',
bar = function() {
return 'world';
};
function foo() {
return 'hello';
}
console.log(typeof foo);//string 这里可以测试出在AO对象中 foo的值==hello
console.log( foo());//报错,foo is not a function. (变量声明是在函数声明和函数形参之后,所以执行后foo就是字符变量了,根据结果,个人理解)
}());
The questions we can now answer are:
-
Why can we access foo before we have declared it?
-
If we follow the
creation stage
, we know the variables have already been created before the
activation / code execution stage
. So as the function flow started executing,
foo
had already been defined in the
activation object
.
-
If we follow the
-
Foo is declared twice, why is foo shown to be
function
and not
undefined
or
string
?
-
Even though
foo
is declared twice, we know from the
creation stage
that functions are created on the
activation object
before variables, and if the property name already exists on the
activation object
, we simply bypass the decleration. -
Therefore, a reference to
function foo()
is first created on the
activation object
, and when we get interpreter gets to
var foo
, we already see the property name
foo
exists so the code does nothing and proceeds.
-
Even though
-
bar的值为什么是
undefined
?
-
bar
is actually a variable that has a function assignment, and we know the variables are created in the
creation stage
but they are initialized with the value of
undefined
.
-
这篇文章写得很赞,下班了,有空继续翻译。哈哈!
变量对象:如上
活动对象:感觉是进入执行上下文后,变量对象中的变量声明被赋值了、