關於Javascript中執行上下文的理解
JS直譯器執行程式碼的過程:
- 定位到呼叫函式的code處;.
-
執行
function
code前, 建立execution context
. -
進入建立階段:
-
建立
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
- 如果函式名已經存在,引用指標值將被重寫(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
-
如果變數名已經存在,什麼都不做,不影響已存在的變數宣告。(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 theactivation / 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 theactivation / code execution stage
. So as the function flow started executing,foo
had already been defined in theactivation object
.
-
If we follow the
-
Foo is declared twice, why is foo shown to be
function
and notundefined
orstring
?-
Even though
foo
is declared twice, we know from thecreation stage
that functions are created on theactivation object
before variables, and if the property name already exists on theactivation object
, we simply bypass the decleration. -
Therefore, a reference to
function foo()
is first created on theactivation object
, and when we get interpreter gets tovar foo
, we already see the property namefoo
exists so the code does nothing and proceeds.
-
Even though
-
bar的值為什麼是 ?
-
bar
is actually a variable that has a function assignment, and we know the variables are created in thecreation stage
but they are initialized with the value of .
-
變數物件:如上
活動物件:感覺是進入執行上下文後,變數物件中的變數宣告被賦值了、