-
Able to spot programming mistakes merely by reading code
-
Stop the try-and-error approach in programming (it works ≠ it is correct)
-
Improve instincts to what is the root cause of bugs
-
Speed up time for code review
Circle the statement that will execute:
a) if (false) { foo(); } else { bar(); }
b) if (true) { foo(); } else { bar(); }
c) if ("false") { foo(); } else { bar(); }
d) if (1) { foo(); } else { bar(); }
e) if (0) { foo(); } else { bar(); }
f) if (-0) { foo(); } else { bar(); }
g) if (Infinity) { foo(); } else { bar(); }
h) if ("1") { foo(); } else { bar(); }
i) if (null) { foo(); } else { bar(); }
j) if ("null") { foo(); } else { bar(); }
k) if ([null]) { foo(); } else { bar(); }
l) if (undefined) { foo(); } else { bar(); }
m) if ("undefined") { foo(); } else { bar(); }
n) if ([1]) { foo(); } else { bar(); }
o) if ("0") { foo(); } else { bar(); }
p) if ([true]) { foo(); } else { bar(); }
q) if ("true") { foo(); } else { bar(); }
r) if (NaN) { foo(); } else { bar(); }
s) if (["0"]) { foo(); } else { bar(); }
t) if ({}) { foo(); } else { bar(); }
u) if ("") { foo(); } else { bar(); }
v) if ([]) { foo(); } else { bar(); }
w) if ([""]) { foo(); } else { bar(); }
console.table([
false,
null,
undefined,
NaN,
0,
-0,
"",
true,
1,
-1,
Infinity,
"true",
"false",
"null",
"undefined",
"NaN",
"0",
"1",
"-1",
"[]",
"{}",
{},
[],
[0],
[1],
[-1],
[true],
[false],
[NaN],
[null],
[undefined],
["true"],
["false"],
["null"],
["undefined"],
["0"],
["1"],
["-1"],
[""],
[[]],
[{}],
].map(obj => [obj, Boolean(obj)]));
- Why sometimes use
isTrue()
sometimes does not? trust? confidence? - Should we come up with a consistent way to check existence of values?
- Should we use lazy evaluation shorthand when possible?
if (!isNullOrUndef(event) && !isNullOrUndef(validEvents[event]) && (!waitForInit || (waitForInit && this._initCompleted))) {}
if (event && !isNullOrUndef(validEvents[event]) && (!waitForInit || (waitForInit && this._initCompleted))) {}
- Should we omit the use of
isTrue()
?
if (isTrue(match) && !this._isPreviousBetInit))
if (match && !this._isPreviousBetInit))
- Do we need isTrue() if we have types?
Circle the statement that will execute:
1. if (1 >= 0) { foo(); } else { bar(); }
2. if (0 >= 0) { foo(); } else { bar(); }
3. if (true >= 0) { foo(); } else { bar(); }
4. if (false >= 0) { foo(); } else { bar(); }
5. if (null >= 0) { foo(); } else { bar(); }
6. if (undefined >= 0) { foo(); } else { bar(); }
7. if (NaN >= 0) { foo(); } else { bar(); }
8. if (Infinity >= 0) { foo(); } else { bar(); }
9. if ("" >= 0) { foo(); } else { bar(); }
10. if ("true" >= 0) { foo(); } else { bar(); }
11. if ("false" >= 0) { foo(); } else { bar(); }
12. if ("null" >= 0) { foo(); } else { bar(); }
13. if ("undefined" >= 0) { foo(); } else { bar(); }
14. if ("0" >= 0) { foo(); } else { bar(); }
15. if ("1" >= 0) { foo(); } else { bar(); }
16. if ("[]" >= 0) { foo(); } else { bar(); }
17. if ("{}" >= 0) { foo(); } else { bar(); }
18. if ({} >= 0) { foo(); } else { bar(); }
19. if ([] >= 0) { foo(); } else { bar(); }
20. if ([0] >= 0) { foo(); } else { bar(); }
21. if ([1] >= 0) { foo(); } else { bar(); }
22. if ([true] >= 0) { foo(); } else { bar(); }
23. if ([false] >= 0) { foo(); } else { bar(); }
24. if ([NaN] >= 0) { foo(); } else { bar(); }
25. if ([null] >= 0) { foo(); } else { bar(); }
26. if ([undefined] >= 0) { foo(); } else { bar(); }
27. if (["true"] >= 0) { foo(); } else { bar(); }
28. if (["false"] >= 0) { foo(); } else { bar(); }
29. if (["null"] >= 0) { foo(); } else { bar(); }
30. if (["0"] >= 0) { foo(); } else { bar(); }
31. if (["1"] >= 0) { foo(); } else { bar(); }
32. if ([""] >= 0) { foo(); } else { bar(); }
33. if ([[]] >= 0) { foo(); } else { bar(); }
34. if ([{}] >= 0) { foo(); } else { bar(); }
35. if ([[""]] >= 0) { foo(); } else { bar(); }
console.table([
1,
1,
0,
true,
false,
null,
undefined,
NaN,
Infinity,
"",
"true",
"false",
"null",
"undefined",
"0",
"1",
"[]",
"{}",
{},
[],
[0],
[1],
[true],
[false],
[NaN],
[null],
[undefined],
["true"],
["false"],
["null"],
["0"],
["1"],
[""],
[[]],
[{}],
[[""]]
].map(obj => [obj, Number(obj), obj >= 0]));
- How do you know what kind of type conversion would happen?
- Even we enforce ourselves to use
===
instead of==
, we cannot avoid implicit type casting - Implicit type casting is hard to remember, too many special, weird cases
- How can you be sure of the behavior if the type of variable is unknown?
- Should we use operators like
>=
?
typeof (1)
typeof ("1")
typeof (1+1)
typeof ("1"+"1")
typeof (1+"1")
typeof ("1"+1)
typeof (1+true)
typeof ("1"+true)
typeof (true+1)
typeof (true+"1")
typeof (1+null)
typeof ("1"+null)
typeof (null+1)
typeof (null+"1")
typeof (1+undefined)
typeof ("1"+undefined)
typeof (undefined+1)
typeof (undefined+"1")
typeof (1+NaN)
typeof ("1"+NaN)
typeof (NaN+1)
typeof (NaN+"1")
typeof (1+"")
typeof (""+1)
typeof (1+[])
typeof ([]+1)
typeof (Symbol() + 1)
console.table([
"---",
1,
"1",
1 + 1,
"1" + "1",
1 + "1",
"1" + 1,
1 + true,
"1" + true,
true + 1,
true + "1",
1 + null,
"1" + null,
null + 1,
null + "1",
1 + undefined,
"1" + undefined,
undefined + 1,
undefined + "1",
1 + NaN,
"1" + NaN,
NaN + 1,
NaN + "1",
1 + "",
"" + 1,
1 + [],
[] + 1,
].map(obj => [obj, typeof obj]));
- NaN is number type
- after any
+
operation, it can only be number / string type - when any side of the expression is string type, cast anything to string
- i.e. before es6 template literal,
foobar+""
was a common way to cast things to string type - For primitive types, when neither side of the expression is string type, cast anything to number
- i.e. 1 + null => 1 + Number(null) => 1 + 0 => 1
Symbol()
is an exceptional case which throw errors+
operations on non-primitive types would be discussed in upcoming exercises
Circle the statement that will execute:
1. if (1 >= 0) { foo(); } else { bar(); }
2. if (true >= 0) { foo(); } else { bar(); }
3. if (false >= 0) { foo(); } else { bar(); }
4. if (null >= 0) { foo(); } else { bar(); }
5. if (undefined >= 0) { foo(); } else { bar(); }
6. if (NaN >= 0) { foo(); } else { bar(); }
7. if ("" >= 0) { foo(); } else { bar(); }
8. if ("null" >= 0) { foo(); } else { bar(); }
9. if ("1" >= 0) { foo(); } else { bar(); }
10. if ({} >= 0) { foo(); } else { bar(); }
11. if ([] >= 0) { foo(); } else { bar(); }
12. if (null) { foo(); } else { bar(); }
13. if ("") { foo(); } else { bar(); }
14. if ([]) { foo(); } else { bar(); }
15. if ({}) { foo(); } else { bar(); }
16. if (NaN) { foo(); } else { bar(); }
17. if ("0") { foo(); } else { bar(); }
18. if (1 + "1" === 2) { foo(); } else { bar(); }
19. if ("1" + 1 === "11") { foo(); } else { bar(); }
20. if (null + 1 === 1) { foo(); } else { bar(); }
21. if (typeof NaN === "NaN") { foo(); } else { bar(); }
console.table([
1,
1 >= 0,
true >= 0,
false >= 0,
null >= 0,
undefined >= 0,
NaN >= 0,
"" >= 0,
"null" >= 0,
"1" >= 0,
{} >= 0,
[] >= 0,
null,
"",
[],
{},
NaN,
"0",
1 + "1" === 2,
"1" + 1 === "11",
null + 1 === 1,
typeof NaN === "NaN"
].map(obj => Boolean(obj)));
Circle the statement that will execute:
1. if (true) { foo(); } else { bar(); }
2. if (false) { foo(); } else { bar(); }
3. if (0) { foo(); } else { bar(); }
4. if (1) { foo(); } else { bar(); }
5. if (null) { foo(); } else { bar(); }
6. if (undefined) { foo(); } else { bar(); }
7. if (Infinity) { foo(); } else { bar(); }
8. if (NaN) { foo(); } else { bar(); }
9. if ("") { foo(); } else { bar(); }
10. if ([]) { foo(); } else { bar(); }
11. if ({}) { foo(); } else { bar(); }
12. if ("true") { foo(); } else { bar(); }
13. if ("false") { foo(); } else { bar(); }
14. if ("0") { foo(); } else { bar(); }
15. if ("1") { foo(); } else { bar(); }
16. if ("null") { foo(); } else { bar(); }
17. if ("undefined") { foo(); } else { bar(); }
18. if ([1]) { foo(); } else { bar(); }
19. if ([null]) { foo(); } else { bar(); }
20. if (["0"]) { foo(); } else { bar(); }
21. if ([""]) { foo(); } else { bar(); }
console.table([
1,
true,
false,
0,
1,
null,
undefined,
Infinity,
NaN,
"",
[],
{},
"true",
"false",
"0",
"1",
"null",
"undefined",
[1],
[null],
["0"],
[""],
].map(obj => Boolean(obj)));
Circle the statement that will execute:
1. if (true) { foo(); } else { bar(); }
2. if (false) { foo(); } else { bar(); }
3. if (0) { foo(); } else { bar(); }
4. if (null) { foo(); } else { bar(); }
5. if (undefined) { foo(); } else { bar(); }
6. if (NaN) { foo(); } else { bar(); }
7. if ("") { foo(); } else { bar(); }
8. if ([]) { foo(); } else { bar(); }
9. if ({}) { foo(); } else { bar(); }
10. if (-1) { foo(); } else { bar(); }
11. if ("false") { foo(); } else { bar(); }
12. if ("0") { foo(); } else { bar(); }
13. if ("null") { foo(); } else { bar(); }
14. if ("undefined") { foo(); } else { bar(); }
15. if ([null]) { foo(); } else { bar(); }
16. if (["0"]) { foo(); } else { bar(); }
17. if ([""]) { foo(); } else { bar(); }
console.table([
1,
true,
false,
0,
null,
undefined,
NaN,
"",
[],
{},
-1,
"false",
"0",
"null",
"undefined",
[null],
["0"],
[""],
].map(obj => Boolean(obj)));
Same as exercise 6
Circle the statement that will execute:
1. if (NaN) { foo(); } else { bar(); }
2. if (NaN === NaN) { foo(); } else { bar(); }
3. if (isNaN(NaN)) { foo(); } else { bar(); }
4. if (Number.NaN) { foo(); } else { bar(); }
5. if (Number.NaN === Number.NaN) { foo(); } else { bar(); }
6. if (Number.isNaN(Number.NaN)) { foo(); } else { bar(); }
7. if (isNaN(Number.NaN)) { foo(); } else { bar(); }
8. if (Number.isNaN(NaN)) { foo(); } else { bar(); }
9. if (Number("1")) { foo(); } else { bar(); }
10. if (isNaN("1")) { foo(); } else { bar(); }
11. if (Number.isNaN("1")) { foo(); } else { bar(); }
12. if (Number("foo")) { foo(); } else { bar(); }
13. if (isNaN("foo")) { foo(); } else { bar(); }
14. if (Number.isNaN("foo")) { foo(); } else { bar(); }
15. if (Number.isNaN(Number("foo")) { foo(); } else { bar(); }
console.table([
[0,0],
["NaN", NaN],
["NaN === NaN", NaN === NaN],
["isNaN(NaN)", isNaN(NaN)],
["Number.NaN", Number.NaN],
["Number.NaN === Number.NaN", Number.NaN === Number.NaN],
["Number.isNaN(Number.NaN)", Number.isNaN(Number.NaN)],
["isNaN(Number.NaN)", isNaN(Number.NaN)],
["Number.isNaN(NaN)", Number.isNaN(NaN)],
[`Number("1")`, Number("1")],
[`isNaN("1")`, isNaN("1")],
[`Number.isNaN("1")`, Number.isNaN("1")],
[`Number("foo")`, Number("foo")],
[`isNaN("foo")`, isNaN("foo")],
[`Number.isNaN("foo")`, Number.isNaN("foo")],
[`Number.isNaN(Number("foo"))`, Number.isNaN(Number("foo"))]
]);
See Explanation in MDN
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NaN#Testing_against_NaN
- isNaN (just like using ==, it does type conversion for you)
- Number.isNaN (just like using ===, it does not convert type)
Circle the statement that will execute:
1. if (true) { foo(); } else { bar(); }
2. if ("false") { foo(); } else { bar(); }
3. if ("0") { foo(); } else { bar(); }
4. if ("0" >= 0) { foo(); } else { bar(); }
5. if ("0" + 1 === 1) { foo(); } else { bar(); }
6. if ("") { foo(); } else { bar(); }
7. if ([]) { foo(); } else { bar(); }
8. if ({}) { foo(); } else { bar(); }
9. if (-1) { foo(); } else { bar(); }
10. if (false >= 0) { foo(); } else { bar(); }
11. if (undefined >= 0) { foo(); } else { bar(); }
12. if (null >= 0) { foo(); } else { bar(); }
13. if (1 + "1" === 2) { foo(); } else { bar(); }
14. if (null + 1 === "null1") { foo(); } else { bar(); }
15. if (NaN) { foo(); } else { bar(); }
16. if (NaN !== NaN) { foo(); } else { bar(); }
17. if (typeof NaN === "NaN") { foo(); } else { bar(); }
console.table([
true,
"false",
"0",
"0" >= 0,
"0" + 1 === 1,
"",
[],
{},
-1,
false >= 0,
undefined >= 0,
null >= 0,
1 + "1" === 2,
null + 1 === "null1",
NaN,
NaN !== NaN,
typeof NaN === "NaN",
].map(obj => Boolean(obj)));
Circle the statement that will execute (if any):
1. if (true) { foo(); } else { bar(); }
2. if (true && false) { foo(); } else { bar(); }
3. if (true || false) { foo(); } else { bar(); }
4. if (null || "0") { foo(); } else { bar(); }
5. if ("" && []) { foo(); } else { bar(); }
6. if (undefined || -1) { foo(); } else { bar(); }
7. if (NaN || {}) { foo(); } else { bar(); }
8. if ([] && {}) { foo(); } else { bar(); }
9. if (false >= 0 || "") { foo(); } else { bar(); }
10. if (baz()) {}
11. if (baz() && false) {}
12. if (false && baz()) {}
13. if (baz() || "") {}
14. if ("" || baz()) {}
15. if (baz() && []) {}
16. if ([] && baz()) {}
const q = [
Boolean(true) ? "foo" : "bar",
Boolean(true) ? "foo" : "bar",
Boolean(true && false) ? "foo" : "bar",
Boolean(true || false) ? "foo" : "bar",
Boolean(null || "0") ? "foo" : "bar",
Boolean("" && []) ? "foo" : "bar",
Boolean(undefined || -1) ? "foo" : "bar",
Boolean(NaN || {}) ? "foo" : "bar",
Boolean([] && {}) ? "foo" : "bar",
Boolean(false >= 0 || "") ? "foo" : "bar",
"baz() does not execute",
"baz() does not execute",
"baz() does not execute",
"baz() does not execute",
"baz() does not execute",
"baz() does not execute",
"baz() does not execute"
];
const baz = (i) => {q[i] = "baz executed"};
baz(10);
baz(11) && false;
false && baz(12);
baz(13) || "";
"" || baz(14);
baz(15) && [];
[] && baz(16);
console.table(q);
Which ECMAScript Version support such function?
The first two has been done for you:
Built-in Objects Methods | ES 5.1 | ES2015 | ES2016 | ES2017 | ES2018 | ES2019 | ES2020 | ESNEXT |
---|---|---|---|---|---|---|---|---|
"".trimLeft / "".trimRight |
✔ | ✔ | ✔ | |||||
[].indexOf |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
[].forEach |
||||||||
"".split / [].join |
||||||||
[].filter / [].map / [].reduce |
||||||||
[].slice / [].splice |
||||||||
[].concat |
||||||||
[].flat |
||||||||
[].flatMap |
||||||||
[].includes |
||||||||
"".includes |
||||||||
"".padStart / "".padEnd |
||||||||
"".trim |
||||||||
"".trimStart / "".trimEnd |
||||||||
"".match |
||||||||
"".matchAll |
||||||||
"".replace |
||||||||
"".replaceAll |
||||||||
"".startsWith / "".endsWith |
||||||||
Object.assign |
||||||||
Object.keys |
||||||||
Object.values |
||||||||
Array.isArray |
||||||||
Promise.all |
||||||||
Promise.catch |
||||||||
Promise.finally |
||||||||
Promise.allSettled |
Built-in Objects Methods | ES 5.1 | ES2015 | ES2016 | ES2017 | ES2018 | ES2019 | ES2020 | ESNEXT |
---|---|---|---|---|---|---|---|---|
"".trimLeft / "".trimRight |
✔ | ✔ | ✔ | |||||
[].indexOf |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
[].forEach |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
"".split / [].join |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
[].filter / [].map / [].reduce |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
[].slice / [].splice |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
[].concat |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
[].flat |
✔ | ✔ | ✔ | |||||
[].flatMap |
✔ | ✔ | ✔ | |||||
[].includes |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ||
"".includes |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |
"".padStart / "".padEnd |
✔ | ✔ | ✔ | ✔ | ✔ | |||
"".trim |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
"".trimStart / "".trimEnd |
✔ | ✔ | ✔ | |||||
"".match |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
"".matchAll |
✔ | ✔ | ||||||
"".replace |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
"".replaceAll |
✔ | |||||||
"".startsWith / "".endsWith |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |
Object.assign |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |
Object.keys |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
Object.values |
✔ | ✔ | ✔ | ✔ | ✔ | |||
Array.isArray |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
Promise.all |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |
Promise.catch |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |
Promise.finally |
✔ | ✔ | ✔ | ✔ | ||||
Promise.allSettled |
✔ | ✔ |
No need to recite which function is supported in which ECMAScript version.
Unless you are 100% certain that function is supported in all browsers, you should check if it requires a polyfill.
https://kangax.github.io/compat-table/es5/
Which ECMAScript Version support such syntax?
The first two has been done for you:
Syntax | ES 5.1 | ES2015 | ES2016 | ES2017 | ES2018 |
---|---|---|---|---|---|
var a = 1; |
✔ | ✔ | ✔ | ✔ | ✔ |
let a = 1; |
✔ | ✔ | ✔ | ✔ | |
const a = 1; |
|||||
function(a) {} |
|||||
function(a = 1) {} |
|||||
function(...b) {} |
|||||
function([a, b, c]) {} |
|||||
function([a, ...bc]) {} |
|||||
function* generator(){ yield 1; } |
|||||
async function a() { await b; } |
|||||
async function* generator(){ yield 1; } |
|||||
() => {} |
|||||
`${0}123` |
|||||
[0, ...[1, 2, 3]] |
|||||
[0, ..."123"] |
|||||
{a: 1, ...bc} |
|||||
{ [a]: 1 } |
|||||
{ a } |
|||||
{ a() { return 1 }} |
|||||
{ [a]() { return 1 }} |
|||||
var [a, b, c] = [1, 2, 3]; |
|||||
var [a, b, c] = "123" |
|||||
var {a, b, c} = {a: 1, b: 2, c: 3} |
|||||
var [a ...bc] = [1, 2, 3]; |
|||||
var {a, ...bc} = {a: 1, b: 2, c: 3} |
|||||
class A {} |
|||||
2 ** 3 |
|||||
for(var i=0; i<3; i++) {} |
|||||
for(var item in [1, 2, 3]) {} |
|||||
for(var item in "123") {} |
|||||
for (var item of [1, 2, 3]) {} |
|||||
for (var item of "123") {} |
|||||
for await(var item of [1, 2, 3]) {} |
Syntax | ES 5.1 | ES2015 | ES2016 | ES2017 | ES2018 |
---|---|---|---|---|---|
var a = 1; |
✔ | ✔ | ✔ | ✔ | ✔ |
let a = 1; |
✔ | ✔ | ✔ | ✔ | |
const a = 1; |
✔ | ✔ | ✔ | ✔ | |
function(a) {} |
✔ | ✔ | ✔ | ✔ | ✔ |
function(a = 1) {} |
✔ | ✔ | ✔ | ✔ | |
function(...b) {} |
✔ | ✔ | ✔ | ✔ | |
function([a, b, c]) {} |
✔ | ✔ | ✔ | ✔ | |
function([a, ...bc]) {} |
✔ | ✔ | ✔ | ✔ | |
function* generator(){ yield 1; } |
✔ | ✔ | ✔ | ✔ | |
async function a() { await b; } |
✔ | ✔ | |||
async function* generator(){ yield 1; } |
✔ | ||||
() => {} |
✔ | ✔ | ✔ | ✔ | |
`${0}123` |
✔ | ✔ | ✔ | ✔ | |
[0, ...[1, 2, 3]] |
✔ | ✔ | ✔ | ✔ | |
[0, ..."123"] |
✔ | ✔ | ✔ | ✔ | |
{a: 1, ...bc} |
✔ | ||||
{ [a]: 1 } |
✔ | ✔ | ✔ | ✔ | |
{ a } |
✔ | ✔ | ✔ | ✔ | |
{ a() { return 1 }} |
✔ | ✔ | ✔ | ✔ | |
{ [a]() { return 1 }} |
✔ | ✔ | ✔ | ✔ | |
var [a, b, c] = [1, 2, 3]; |
✔ | ✔ | ✔ | ✔ | |
var [a, b, c] = "123" |
✔ | ✔ | ✔ | ✔ | |
var {a, b, c} = {a: 1, b: 2, c: 3} |
✔ | ✔ | ✔ | ✔ | |
var [a ...bc] = [1, 2, 3]; |
✔ | ✔ | ✔ | ✔ | |
var {a, ...bc} = {a: 1, b: 2, c: 3} |
✔ | ||||
class A {} |
✔ | ✔ | ✔ | ✔ | |
2 ** 3 |
✔ | ✔ | ✔ | ||
for(var i=0; i<3; i++) {} |
✔ | ✔ | ✔ | ✔ | ✔ |
for(var item in [1, 2, 3]) {} |
✔ | ✔ | ✔ | ✔ | ✔ |
for(var item in "123") {} |
✔ | ✔ | ✔ | ✔ | ✔ |
for (var item of [1, 2, 3]) {} |
✔ | ✔ | ✔ | ✔ | |
for (var item of "123") {} |
✔ | ✔ | ✔ | ✔ | |
for await(var item of [1, 2, 3]) {} |
✔ |