讓我們從個小測驗著手吧
quiz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var a = 1
function test() {
console.log('1.', a)
var a = 7
console.log('2.', a)
a++
var a
inner()
console.log('4.', a)
function inner(){
console.log('3.', a)
a = 30
b = 200
}
}
test()
console.log('5.', a)
a = 70
console.log('6.', a)
console.log('7.', b)
answer :
以 hoisting 試想
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24var a = 1
function test() {
var a
console.log('1.', a) // '1. undefined'
a = 7
console.log('2.', a) // '2. 7'
a++
var a
function inner() {
console.log('3.', a) // '3. 8'
a = 30
b = 200
}
inner()
console.log('4.', a) // '4. 30'
}
test()
console.log('5.', a) // '5. 1'
a = 70
console.log('6.', a) // '6. 70'
console.log('7.', b) // '7. 200'
ECMAScript
Execution Contexts
When control is transferred to ECMAScript executable code, control is entering an execution context. Active execution contexts logically form a stack. The top execution context on this logical stack is the running execution context.
Variable Instantiation
Every execution context has associated with it a variable object. Variables and functions declared in the source text are added as properties of the variable object. For function code, parameters are added as properties of the variable object.
1
2
3
4
5
6
7VO: {
a:1
}
function test() {
var a = 1
}On entering an execution context, the properties are bound to the variable object in the following order:
For function code: for each formal parameter, as defined in the FormalParameterList, create a property of the variable object whose name is the Identifier and whose attributes are determined by the type of code. The values of the parameters are supplied by the caller as arguments to [[Call]].
1
2
3
4
5
6
7
8
9
10VO: {
a: 123,
b: undefined
}
function test(a, b) {
}
test(123) // 會使參數初始化For each FunctionDeclaration in the code, in source text order, create a property of the variable object.
If the variable object already has a property with this name, replace its value and attributes.
1
2
3
4
5
6
7
8
9
10
11
12VO: {
a: 123,
b: function
}
function test(a, b) {
function b() {
} // 若有同名的,則會被後面的所覆蓋
}
test(123)For each VariableDeclaration or VariableDeclarationNoIn in the code, create a property of the variable object whose value is undefined.
If there is already a property of the variable object with the name of a declared variable, the value of the property and its attributes are not changed.
1
2
3
4
5
6
7
8
9
10
11
12
13VO: {
a: 123,
b: function,
c: undefined
}
function test(a, b) {
function b() {
}
var c = 30
}
test(123)
了解原理後,回到上述的 quiz
quiz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var a = 1
function test(){
console.log('1.', a)
var a = 7
console.log('2.', a)
a++
var a
inner()
console.log('4.', a)
function inner(){
console.log('3.', a)
a = 30
b = 200
}
}
test()
console.log('5.', a)
a = 70
console.log('6.', a)
console.log('7.', b)
answer :
體驗在 JS 引擎中此段程式碼如何被執行
產生 global Execution Context,並初始化 global VO
1
2
3global EC
global VO {
}首先尋找參數,但因為此段並不是 function 所以沒有參數。
接著尋找是否有 function 的宣告 -> 找到
test()
的宣告。
1
2
3
4global EC
global VO {
test: func
}尋找變數宣告
1
2
3
4
5global EC
global VO {
test: func,
a: undefined
}開始執行程式
Line 1 :
var a = 1;
1
2
3
4
5global EC
global VO {
test: func,
a: 1
}Line 2~15 : function 宣告跳過
Line 16 : 當呼叫一個新的 function
test()
,就進入一個新的execution context
。
產生新的 test Execution Context ,並初始化 test VO :
1
2
3
4
5
6
7
8
9
10test EC
test VO {
}
global EC
global VO {
test: func,
a: 1
}首先尋找參數 -> 無
接著尋找是否有 function 的宣告 -> 找到
inner()
的宣告。1
2
3
4
5
6
7
8
9
10test EC
test VO {
inner: func
}
global EC
global VO {
test: func,
a: 1
}尋找變數宣告
1
2
3
4
5
6
7
8
9
10
11test EC
test VO {
inner: func,
a: undefined
}
global EC
global VO {
test: func,
a: 1
}開始執行程式
Line 3 :
console.log('1.', a) // undefined
Line 4 :
var a = 7
1
2
3
4
5
6
7
8
9
10
11test EC
test VO {
inner: func,
a: 7
}
global EC
global VO {
test: func,
a: 1
}Line 5 :
console.log('2.', a) // 7
Line 6 :
a++
1
2
3
4
5
6
7
8
9
10
11test EC
test VO {
inner: func,
a: 8
}
global EC
global VO {
test: func,
a: 1
}Line 8 :
inner()
產生新的 inner Execution Context ,並初始化 inner VO :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16inner EC
inner VO {
}
test EC
test VO {
inner: func,
a: 8
}
global EC
global VO {
test: func,
a: 1
}function inner() 中沒有傳入任何參數,裡面也沒有 function 以及變數。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16inner EC
inner VO {
}
test EC
test VO {
inner: func,
a: 8
}
global EC
global VO {
test: func,
a: 1
}開始執行程式
Line 11 :
console.log('3.', a) // 8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16inner EC
inner VO {
}
test EC
test VO {
inner: func,
a: 8
}
global EC
global VO {
test: func,
a: 1
}因為 inner VO 裡面是空的,因此往上找至 test VO -> a: 8 。
Line 12 :
a = 30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16inner EC
inner VO {
}
test EC
test VO {
inner: func,
a: 30
}
global EC
global VO {
test: func,
a: 1
}Line 13 :
b = 200
因為 inner VO 裡面沒有 b 的資訊,因此往上找至 test VO ,發現裡面也沒有 b 的資訊,繼續往上至 global VO 找,仍沒有 b 的資訊,最後在 global VO 建立 b 的資訊 ( 讓 b 變成全域變數 )。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17inner EC
inner VO {
}
test EC
test VO {
inner: func,
a: 30
}
global EC
global VO {
test: func,
a: 1,
b: 200
}
執行完 inner(),將 inner EC pop 出去。
1
2
3
4
5
6
7
8
9
10
11
12test EC
test VO {
inner: func,
a: 30
}
global EC
global VO {
test: func,
a: 1,
b: 200
}回到 function test() {} 內部
- Line 9 :
console.log('4.', a) // 30
- Line 9 :
執行完 test(),將 test EC pop 出去。
1
2
3
4
5
6global EC
global VO {
test: func,
a: 1,
b: 200
}回到 global EC
Line 17 :
console.log('5.', a) // 1
Line 18 :
a = 70
1
2
3
4
5
6global EC
global VO {
test: func,
a: 70,
b: 200
}Line 19 :
console.log('6.', a) // 70
Line 20 :
console.log('7.', a) // 200
主程式執行完畢
Comments