前言
前面已經看過變數與判斷式了,這一篇讓我們來看看迴圈在 V8 中是如何被實現的,看一下 for loop 與 while loop 有沒有區別。對於初學者來說,其實觀看迴圈的 bytecode 也能更理解迴圈的運作。
for loop
一樣先從最簡單的開始:
function find_me_test() {
for(var i=1; i<=10; i++) {
console.log(i)
}
}
find_me_test()
產生的 bytecode:
[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 24
21 E> 0xd70b3a1dcb2 @ 0 : a5 StackCheck
38 S> 0xd70b3a1dcb3 @ 1 : 0c 01 LdaSmi [1]
0xd70b3a1dcb5 @ 3 : 26 fb Star r0
42 S> 0xd70b3a1dcb7 @ 5 : 0c 0a LdaSmi [10]
42 E> 0xd70b3a1dcb9 @ 7 : 6b fb 00 TestLessThanOrEqual r0, [0]
0xd70b3a1dcbc @ 10 : 99 1c JumpIfFalse [28] (0xd70b3a1dcd8 @ 38)
28 E> 0xd70b3a1dcbe @ 12 : a5 StackCheck
59 S> 0xd70b3a1dcbf @ 13 : 13 00 01 console.log(i)
49 S> 0xd70b3a1dccf @ 29 : 25 fb Ldar r0
0xd70b3a1dcd1 @ 31 : 4c 07 Inc [7]
0xd70b3a1dcd3 @ 33 : 26 fb Star r0
0xd70b3a1dcd5 @ 35 : 8a 1e 00 JumpLoop [30], [0] (0xd70b3a1dcb7 @ 5)
0xd70b3a1dcd8 @ 38 : 0d LdaUndefined
78 S> 0xd70b3a1dcd9 @ 39 : a9 Return
Constant pool (size = 2)
0xd70b3a1dc31: [FixedArray] in OldSpace
- map: 0x0d70563007b1 <Map>
- length: 2
0: 0x0d70930900e9 <String[#7]: console>
1: 0x0d709308fbe9 <String[#3]: log>
Handler Table (size = 0)
1
2
3
4
5
6
7
8
9
10
在 V8 裡面用了 JumpLoop
來實作迴圈,而這個結構也是常見的組合語言實作迴圈的結構,所以看習慣組合語言的人看這個應該不陌生。而且比起組合語言,bytecode 還貼心的直接用 JumpLoop
而不是一般的 Jump
來標明這是一個迴圈。
另外一個值得注意的地方是除了第一圈以外,永遠都會先 i++ 再進行條件檢查,因此當這個迴圈跑完以後,i 的值會是 11,這是有些初學者不會注意到的事情。從上面 bytecode 就可以看出來,迴圈的執行順序是:
- 初始條件(var i = 1)
- 判斷式(if i <= 10)
- 如果結果為 false,結束
- 執行迴圈裡面要做的事(console.log(i))
- 跑完一圈要做的事(i++)
- 跳到第 2 行
接著我們來試試看巢狀迴圈:
function find_me_test() {
for(var i=1; i<=3; i++) {
for(var j=1; j<=3; j++) {
console.log(i, j)
}
}
}
find_me_test()
產生的 bytecode:
[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 32
21 E> 0xb35cd59dcda @ 0 : a5 StackCheck
38 S> 0xb35cd59dcdb @ 1 : 0c 01 LdaSmi [1]
0xb35cd59dcdd @ 3 : 26 fb Star r0
42 S> 0xb35cd59dcdf @ 5 : 0c 03 LdaSmi [3]
42 E> 0xb35cd59dce1 @ 7 : 6b fb 00 TestLessThanOrEqual r0, [0]
0xb35cd59dce4 @ 10 : 99 32 JumpIfFalse [50] (0xb35cd59dd16 @ 60)
28 E> 0xb35cd59dce6 @ 12 : a5 StackCheck
68 S> 0xb35cd59dce7 @ 13 : 0c 01 LdaSmi [1]
0xb35cd59dce9 @ 15 : 26 fa Star r1
72 S> 0xb35cd59dceb @ 17 : 0c 03 LdaSmi [3]
72 E> 0xb35cd59dced @ 19 : 6b fa 01 TestLessThanOrEqual r1, [1]
0xb35cd59dcf0 @ 22 : 99 1d JumpIfFalse [29] (0xb35cd59dd0d @ 51)
58 E> 0xb35cd59dcf2 @ 24 : a5 StackCheck
90 S> 0xb35cd59dcf3 @ 25 : 13 00 02 console.log(i, j)
78 S> 0xb35cd59dd04 @ 42 : 25 fa Ldar r1
0xb35cd59dd06 @ 44 : 4c 08 Inc [8]
0xb35cd59dd08 @ 46 : 26 fa Star r1
0xb35cd59dd0a @ 48 : 8a 1f 01 JumpLoop [31], [1] (0xb35cd59dceb @ 17)
48 S> 0xb35cd59dd0d @ 51 : 25 fb Ldar r0
0xb35cd59dd0f @ 53 : 4c 09 Inc [9]
0xb35cd59dd11 @ 55 : 26 fb Star r0
0xb35cd59dd13 @ 57 : 8a 34 00 JumpLoop [52], [0] (0xb35cd59dcdf @ 5)
0xb35cd59dd16 @ 60 : 0d LdaUndefined
118 S> 0xb35cd59dd17 @ 61 : a9 Return
Constant pool (size = 2)
0xb35cd59dc49: [FixedArray] in OldSpace
- map: 0x0b3588b807b1 <Map>
- length: 2
0: 0x0b359ac100e9 <String[#7]: console>
1: 0x0b359ac0fbe9 <String[#3]: log>
Handler Table (size = 0)
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
值得注意的地方是跳轉時候的參數:
JumpLoop [31], [1] (0xb35cd59dceb @ 17)
JumpLoop [52], [0] (0xb35cd59dcdf @ 5)
可以從第二個參數 [1]
或是 [0]
來辨別是第幾層迴圈的跳轉,詳情可以從 V8 source code 上面看到:
// JumpLoop <imm> <loop_depth>
//
// Jump by the number of bytes represented by the immediate operand |imm|. Also
// performs a loop nesting check and potentially triggers OSR in case the
// current OSR level matches (or exceeds) the specified |loop_depth|.
這還滿方便的,若是沒有這個參數的話,很難從 bytecode 直接看出是哪一個迴圈的跳轉。
那如果是無窮迴圈呢?會產生怎樣的程式碼?
function find_me_test() {
for(;;) {
console.log(1)
}
}
find_me_test()
結果:
[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 24
21 E> 0x13b785a1dc92 @ 0 : a5 StackCheck
28 E> 0x13b785a1dc93 @ 1 : a5 StackCheck
42 S> 0x13b785a1dc94 @ 2 : 13 00 00 console.log(1)
0x13b785a1dca8 @ 22 : 8a 15 00 JumpLoop [21], [0] (0x13b785a1dc93 @ 1)
0x13b785a1dcab @ 25 : 0d LdaUndefined
61 S> 0x13b785a1dcac @ 26 : a9 Return
Constant pool (size = 2)
0x13b785a1dc19: [FixedArray] in OldSpace
- map: 0x13b7e71807b1 <Map>
- length: 2
0: 0x13b73d7100e9 <String[#7]: console>
1: 0x13b73d70fbe9 <String[#3]: log>
Handler Table (size = 0)
其實結構跟一般迴圈一樣,只是少了條件判斷的地方而已,會一直不斷的 JumpLoop。
while loop
接著我們來試試看 while loop,一樣列印出 1~10,跟之前的 for 迴圈功能一模一樣:
function find_me_test() {
var i = 1
while(i <= 10) {
console.log(i)
i++
}
}
find_me_test()
結果:
[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 24
21 E> 0x30eb76b9dcb2 @ 0 : a5 StackCheck
36 S> 0x30eb76b9dcb3 @ 1 : 0c 01 LdaSmi [1]
0x30eb76b9dcb5 @ 3 : 26 fb Star r0
48 S> 0x30eb76b9dcb7 @ 5 : 0c 0a LdaSmi [10]
48 E> 0x30eb76b9dcb9 @ 7 : 6b fb 00 TestLessThanOrEqual r0, [0]
0x30eb76b9dcbc @ 10 : 99 1c JumpIfFalse [28] (0x30eb76b9dcd8 @ 38)
40 E> 0x30eb76b9dcbe @ 12 : a5 StackCheck
61 S> 0x30eb76b9dcbf @ 13 : 13 00 01 console.log(i)
80 S> 0x30eb76b9dccf @ 29 : 25 fb Ldar r0
0x30eb76b9dcd1 @ 31 : 4c 07 Inc [7]
0x30eb76b9dcd3 @ 33 : 26 fb Star r0
0x30eb76b9dcd5 @ 35 : 8a 1e 00 JumpLoop [30], [0] (0x30eb76b9dcb7 @ 5)
0x30eb76b9dcd8 @ 38 : 0d LdaUndefined
88 S> 0x30eb76b9dcd9 @ 39 : a9 Return
Constant pool (size = 2)
0x30eb76b9dc31: [FixedArray] in OldSpace
- map: 0x30eb9c6807b1 <Map>
- length: 2
0: 0x30eb10f100e9 <String[#7]: console>
1: 0x30eb10f0fbe9 <String[#3]: log>
Handler Table (size = 0)
1
2
3
4
5
6
7
8
9
10
可以看到產生出來的 bytecode 與 for loop 產生出來的一模一樣,完全沒有差異。
那 do while 呢?
function find_me_test() {
var i = 1
do {
console.log(i)
i++
} while(i <= 10)
}
find_me_test()
結果:
[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 24
21 E> 0x25ff5eb1dcb2 @ 0 : a5 StackCheck
36 S> 0x25ff5eb1dcb3 @ 1 : 0c 01 LdaSmi [1]
0x25ff5eb1dcb5 @ 3 : 26 fb Star r0
40 E> 0x25ff5eb1dcb7 @ 5 : a5 StackCheck
49 S> 0x25ff5eb1dcb8 @ 6 : 13 00 00 console.log(i)
68 S> 0x25ff5eb1dcc8 @ 22 : 25 fb Ldar r0
0x25ff5eb1dcca @ 24 : 4c 06 Inc [6]
0x25ff5eb1dccc @ 26 : 26 fb Star r0
84 S> 0x25ff5eb1dcce @ 28 : 0c 0a LdaSmi [10]
84 E> 0x25ff5eb1dcd0 @ 30 : 6b fb 07 TestLessThanOrEqual r0, [7]
0x25ff5eb1dcd3 @ 33 : 99 05 JumpIfFalse [5] (0x25ff5eb1dcd8 @ 38)
0x25ff5eb1dcd5 @ 35 : 8a 1e 00 JumpLoop [30], [0] (0x25ff5eb1dcb7 @ 5)
0x25ff5eb1dcd8 @ 38 : 0d LdaUndefined
91 S> 0x25ff5eb1dcd9 @ 39 : a9 Return
Constant pool (size = 2)
0x25ff5eb1dc31: [FixedArray] in OldSpace
- map: 0x25ff05f007b1 <Map>
- length: 2
0: 0x25ff939100e9 <String[#7]: console>
1: 0x25ff9390fbe9 <String[#3]: log>
Handler Table (size = 0)
1
2
3
4
5
6
7
8
9
10
do while 的結構簡單一點,先做事情,然後把條件判斷以及 jump 放在最後面,bytecode 讀起來會簡單很多。
for in
既然都研究了迴圈,那可以連 for in 也順便研究一下:
function find_me_test() {
var obj = {
a: 1,
b: 2,
c: 3
}
for(var element in obj) {
console.log(element)
}
}
find_me_test()
結果:
[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 80
21 E> 0x29fa8f81dd4a @ 0 : a5 StackCheck
38 S> 0x29fa8f81dd4b @ 1 : 7d 00 00 29 CreateObjectLiteral [0], [0], #41
0x29fa8f81dd4f @ 5 : 26 fb Star r0
94 S> 0x29fa8f81dd51 @ 7 : 9c 36 JumpIfUndefined [54] (0x29fa8f81dd87 @ 61)
0x29fa8f81dd53 @ 9 : 9a 34 JumpIfNull [52] (0x29fa8f81dd87 @ 61)
0x29fa8f81dd55 @ 11 : 77 f8 ToObject r3
0x29fa8f81dd57 @ 13 : a0 f8 ForInEnumerate r3
0x29fa8f81dd59 @ 15 : a1 f7 01 ForInPrepare r4-r6, [1]
0x29fa8f81dd5c @ 18 : 0b LdaZero
0x29fa8f81dd5d @ 19 : 26 f4 Star r7
83 S> 0x29fa8f81dd5f @ 21 : a2 f4 f5 ForInContinue r7, r6
0x29fa8f81dd62 @ 24 : 99 25 JumpIfFalse [37] (0x29fa8f81dd87 @ 61)
0x29fa8f81dd64 @ 26 : a3 f8 f4 f7 01 ForInNext r3, r7, r4-r5, [1]
0x29fa8f81dd69 @ 31 : 9c 17 JumpIfUndefined [23] (0x29fa8f81dd80 @ 54)
0x29fa8f81dd6b @ 33 : 26 f9 Star r2
75 E> 0x29fa8f81dd6d @ 35 : a5 StackCheck
83 S> 0x29fa8f81dd6e @ 36 : 26 fa Star r1
105 S> 0x29fa8f81dd70 @ 38 : 13 01 02 console.log(r2)
0x29fa8f81dd80 @ 54 : a4 f4 ForInStep r7
0x29fa8f81dd82 @ 56 : 26 f4 Star r7
0x29fa8f81dd84 @ 58 : 8a 25 00 JumpLoop [37], [0] (0x29fa8f81dd5f @ 21)
0x29fa8f81dd87 @ 61 : 0d LdaUndefined
130 S> 0x29fa8f81dd88 @ 62 : a9 Return
Constant pool (size = 3)
0x29fa8f81dcc1: [FixedArray] in OldSpace
- map: 0x29fa1ea807b1 <Map>
- length: 3
0: 0x29fa8f81dc69 <ObjectBoilerplateDescription[7]>
1: 0x29fad31900e9 <String[#7]: console>
2: 0x29fad318fbe9 <String[#3]: log>
Handler Table (size = 0)
a
b
c
可以看到程式碼的部分滿特別的,出現了幾個與 for in 相關的指令:
ForInEnumerate
ForInPrepare
ForInContinue
ForInNext
ForInStep
雖然說指令不太一樣,不過基本上流程還是滿好懂的,就跟一般迴圈差不多,只是指令換了。
for of
最後讓我們來看一下 for of 會產生出什麼樣的結果,一樣準備一個很簡單的資料:
function find_me_test() {
var arr = [1, 2, 3]
for(var element of arr) {
console.log(element)
}
}
find_me_test()
結果:
[generated bytecode for function: find_me_test]
Parameter count 1
Frame size 120
21 E> 0x1b024c31dd22 @ 0 : a5 StackCheck
38 S> 0x1b024c31dd23 @ 1 : 7a 00 00 25 CreateArrayLiteral [0], [0], #37
0x1b024c31dd27 @ 5 : 26 fb Star r0
69 S> 0x1b024c31dd29 @ 7 : 28 fb 01 01 LdaNamedProperty r0, [1], [1]
0x1b024c31dd2d @ 11 : 26 f5 Star r6
0x1b024c31dd2f @ 13 : 58 f5 fb 03 CallProperty0 r6, r0, [3]
0x1b024c31dd33 @ 17 : 27 fb f6 Mov r0, r5
0x1b024c31dd36 @ 20 : 9e 07 JumpIfJSReceiver [7] (0x1b024c31dd3d @ 27)
0x1b024c31dd38 @ 22 : 61 b6 00 fb 00 CallRuntime [ThrowSymbolIteratorInvalid], r0-r0
0x1b024c31dd3d @ 27 : 26 f7 Star r4
0x1b024c31dd3f @ 29 : 28 f7 02 05 LdaNamedProperty r4, [2], [5]
0x1b024c31dd43 @ 33 : 26 f8 Star r3
0x1b024c31dd45 @ 35 : 11 LdaFalse
0x1b024c31dd46 @ 36 : 26 f4 Star r7
0x1b024c31dd48 @ 38 : 27 ff f1 Mov <context>, r10
0x1b024c31dd4b @ 41 : 10 LdaTrue
0x1b024c31dd4c @ 42 : 26 f4 Star r7
58 S> 0x1b024c31dd4e @ 44 : 58 f8 f7 07 CallProperty0 r3, r4, [7]
0x1b024c31dd52 @ 48 : 26 f0 Star r11
0x1b024c31dd54 @ 50 : 9e 07 JumpIfJSReceiver [7] (0x1b024c31dd5b @ 57)
0x1b024c31dd56 @ 52 : 61 af 00 f0 01 CallRuntime [ThrowIteratorResultNotAnObject], r11-r11
0x1b024c31dd5b @ 57 : 28 f0 03 09 LdaNamedProperty r11, [3], [9]
0x1b024c31dd5f @ 61 : 96 28 JumpIfToBooleanTrue [40] (0x1b024c31dd87 @ 101)
0x1b024c31dd61 @ 63 : 28 f0 04 0b LdaNamedProperty r11, [4], [11]
0x1b024c31dd65 @ 67 : 26 f0 Star r11
0x1b024c31dd67 @ 69 : 11 LdaFalse
0x1b024c31dd68 @ 70 : 26 f4 Star r7
0x1b024c31dd6a @ 72 : 27 f0 f9 Mov r11, r2
50 E> 0x1b024c31dd6d @ 75 : a5 StackCheck
58 S> 0x1b024c31dd6e @ 76 : 27 f9 fa Mov r2, r1
80 S> 0x1b024c31dd71 @ 79 : 13 05 0d LdaGlobal [5], [13]
0x1b024c31dd74 @ 82 : 26 ee Star r13
88 E> 0x1b024c31dd76 @ 84 : 28 ee 06 0f LdaNamedProperty r13, [6], [15]
0x1b024c31dd7a @ 88 : 26 ef Star r12
88 E> 0x1b024c31dd7c @ 90 : 59 ef ee f9 11 CallProperty1 r12, r13, r2, [17]
0x1b024c31dd81 @ 95 : 27 fa f0 Mov r1, r11
0x1b024c31dd84 @ 98 : 8a 39 00 JumpLoop [57], [0] (0x1b024c31dd4b @ 41)
0x1b024c31dd87 @ 101 : 0c ff LdaSmi [-1]
0x1b024c31dd89 @ 103 : 26 f2 Star r9
0x1b024c31dd8b @ 105 : 26 f3 Star r8
0x1b024c31dd8d @ 107 : 8b 07 Jump [7] (0x1b024c31dd94 @ 114)
0x1b024c31dd8f @ 109 : 26 f2 Star r9
0x1b024c31dd91 @ 111 : 0b LdaZero
0x1b024c31dd92 @ 112 : 26 f3 Star r8
0x1b024c31dd94 @ 114 : 0f LdaTheHole
0x1b024c31dd95 @ 115 : a6 SetPendingMessage
0x1b024c31dd96 @ 116 : 26 f1 Star r10
0x1b024c31dd98 @ 118 : 25 f4 Ldar r7
0x1b024c31dd9a @ 120 : 96 3c JumpIfToBooleanTrue [60] (0x1b024c31ddd6 @ 180)
0x1b024c31dd9c @ 122 : 28 f7 07 13 LdaNamedProperty r4, [7], [19]
0x1b024c31dda0 @ 126 : 26 ef Star r12
0x1b024c31dda2 @ 128 : 9c 34 JumpIfUndefined [52] (0x1b024c31ddd6 @ 180)
0x1b024c31dda4 @ 130 : 9a 32 JumpIfNull [50] (0x1b024c31ddd6 @ 180)
0x1b024c31dda6 @ 132 : 73 06 TestTypeOf #6
0x1b024c31dda8 @ 134 : 98 12 JumpIfTrue [18] (0x1b024c31ddba @ 152)
0x1b024c31ddaa @ 136 : 00 0c 9a 00 LdaSmi.Wide [154]
0x1b024c31ddae @ 140 : 26 ee Star r13
0x1b024c31ddb0 @ 142 : 12 08 LdaConstant [8]
0x1b024c31ddb2 @ 144 : 26 ed Star r14
0x1b024c31ddb4 @ 146 : 61 9f 00 ee 02 CallRuntime [NewTypeError], r13-r14
0x1b024c31ddb9 @ 151 : a7 Throw
0x1b024c31ddba @ 152 : 27 ff ee Mov <context>, r13
0x1b024c31ddbd @ 155 : 58 ef f7 15 CallProperty0 r12, r4, [21]
0x1b024c31ddc1 @ 159 : 9e 15 JumpIfJSReceiver [21] (0x1b024c31ddd6 @ 180)
0x1b024c31ddc3 @ 161 : 26 ed Star r14
0x1b024c31ddc5 @ 163 : 61 af 00 ed 01 CallRuntime [ThrowIteratorResultNotAnObject], r14-r14
0x1b024c31ddca @ 168 : 8b 0c Jump [12] (0x1b024c31ddd6 @ 180)
0x1b024c31ddcc @ 170 : 26 ee Star r13
0x1b024c31ddce @ 172 : 0b LdaZero
0x1b024c31ddcf @ 173 : 6d f3 TestReferenceEqual r8
0x1b024c31ddd1 @ 175 : 98 05 JumpIfTrue [5] (0x1b024c31ddd6 @ 180)
0x1b024c31ddd3 @ 177 : 25 ee Ldar r13
0x1b024c31ddd5 @ 179 : a8 ReThrow
0x1b024c31ddd6 @ 180 : 25 f1 Ldar r10
0x1b024c31ddd8 @ 182 : a6 SetPendingMessage
0x1b024c31ddd9 @ 183 : 0b LdaZero
0x1b024c31ddda @ 184 : 6d f3 TestReferenceEqual r8
0x1b024c31dddc @ 186 : 99 05 JumpIfFalse [5] (0x1b024c31dde1 @ 191)
0x1b024c31ddde @ 188 : 25 f2 Ldar r9
0x1b024c31dde0 @ 190 : a8 ReThrow
0x1b024c31dde1 @ 191 : 0d LdaUndefined
105 S> 0x1b024c31dde2 @ 192 : a9 Return
Constant pool (size = 9)
0x1b024c31dc69: [FixedArray] in OldSpace
- map: 0x1b02631007b1 <Map>
- length: 9
0: 0x1b024c31dc21 <ArrayBoilerplateDescription 0, 0x1b025400af19 <FixedArray[3]>>
1: 0x1b0263104d79 <Symbol: Symbol.iterator>
2: 0x1b02631040d9 <String[#4]: next>
3: 0x1b0263103979 <String[#4]: done>
4: 0x1b0263104959 <String[#5]: value>
5: 0x1b02e45100e9 <String[#7]: console>
6: 0x1b02e450fbe9 <String[#3]: log>
7: 0x1b02631044c1 <String[#6]: return>
8: 0x1b0263100751 <String[#0]: >
Handler Table (size = 32)
from to hdlr (prediction, data)
( 41, 101) -> 109 (prediction=0, data=10)
( 155, 168) -> 170 (prediction=0, data=13)
1
2
3
一眼就可以發現程式碼比想像中長很多,不過從 constant pool 裡面的值可以立刻推測發生了什麼事:
- length: 9
0: 0x1b024c31dc21 <ArrayBoilerplateDescription 0, 0x1b025400af19 <FixedArray[3]>>
1: 0x1b0263104d79 <Symbol: Symbol.iterator>
2: 0x1b02631040d9 <String[#4]: next>
3: 0x1b0263103979 <String[#4]: done>
4: 0x1b0263104959 <String[#5]: value>
5: 0x1b02e45100e9 <String[#7]: console>
6: 0x1b02e450fbe9 <String[#3]: log>
7: 0x1b02631044c1 <String[#6]: return>
8: 0x1b0263100751 <String[#0]: >
for of 底層實作是透過 iterator 來做的,在 MDN 上面可以看見有一個自己實作的小範例:
const iterable = {
[Symbol.iterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return { value: this.i++, done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const value of iterable) {
console.log(value);
}
// 0
// 1
// 2
所以 for of 基本上就是先透過 iterable[Symbol.iterator]()
拿到 iterator,然後不斷呼叫 iterator.next()
,如果返回的物件 done 是 true 代表結束,否則的話 value 就是拿到的值,這個 pattern 其實就跟 generator 是一樣的。
理解了背後的原理以後,就可以把這一段 for of:
var arr = [1, 2, 3]
for(var element of arr) {
console.log(element)
}
看作是這樣:
var arr = [1, 2, 3]
var it = arr[Symbol.iterator]()
var result = it.next()
while(!result.done) {
console.log(result.value)
result = it.next()
}
這也是為什麼 for of 產生的 bytecode 會那麼長的緣故。而且裡面有許多指令都在做檢查,例如說:CallRuntime [ThrowSymbolIteratorInvalid]
、CallRuntime [ThrowIteratorResultNotAnObject]
以及 CallRuntime [NewTypeError]
。