JS
declaration
为什么建议使用 let 而不是 var
- 主要原因是作用域不同,let 是块级作用域,var 是函数级作用域。
1 2 3 4 5 6 7
| if (true) { var x = 10; } console.log(x);
for (var i = 0; i < 3; i++) {} console.log(i);
|
1 2 3 4 5 6 7
| if (true) { let y = 10; } console.log(y);
for (let j = 0; j < 3; j++) {} console.log(j);
|
- var 会进行变量提升
1 2
| console.log(a); var a = 5;
|
等价于
1 2 3
| var a; console.log(a); a = 5;
|
let 也有提升,但存在暂时性死区(Temporal Dead Zone, TDZ),在声明前访问会直接报错:
1 2
| console.log(b); let b = 5;
|
- 重复声明
var 允许在同一作用域内重复声明同名变量,后面的会覆盖前面的,且不会报错:
1 2
| var name = "Alice"; var name = "Bob";
|
let 在同一作用域内重复声明会直接报错:
1 2
| let name = "Alice"; let name = "Bob";
|
- 全局对象污染
在浏览器中,全局作用域下用 var 声明的变量会成为 window 对象的属性:
1 2 3 4 5
| var foo = 1; console.log(window.foo);
let bar = 2; console.log(window.bar);
|
- 循环中的闭包陷阱
1 2 3 4
| for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); }
|
不只是 js 有这种情况,golang 在 GO 1.22 之前存在闭包陷阱,具体原因是引用了同一块地址的值,后续修复的办法是每次循环都会重新创建一块地址,这样就不会三个地方都引用同一个地址,然后导致输出同一个值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package main
import ( "fmt" "time" )
func main() { for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() } time.Sleep(time.Second) }
|
为什么要有 null 和 undefined
undefined 是表示存在有这个变量,但是还没有开始赋值。
null 代表是直接没有这个值
JS 的 WAT 故事
1 2 3 4
| [] + [] // "" [] + {} // "[object Object]" {} + [] // 0 ({}被解析为代码块) {} + {} // NaN (同上)
|
1 2 3 4 5 6 7 8
| [1,2,3] + [4,5,6] // "1,2,34,5,6" (转字符串后拼接) [1,2,3] * 2 // NaN [10] - [5] // 5 ([10]→"10"→10, [5]→"5"→5) [10,20] - [5] // NaN ("10,20" 无法转数字)
[] == ![] // true // [] 转布尔是 true, ![] 是 false // [] == false → "" == false → 0 == 0 → true
|
这部分主要是在警告 js 的隐式转化规则中存在的问题,因此其复杂的隐式转化规则,经常导致出现一些意料之外的错误,比如使用 == 的时候是一种相对宽松的比较,如果两边的类型不一致,那么就会出现隐式类型转化的情况,"" == 0 成立就是因为 "" 被转化为数字 0,从而使得条件成立。
除此之外,还有运算顺序引发的错误,下面的运算顺序是从左往右进行运算的,也就是说,上述代码等价于:
但是,typescript 对上述进行了处理,使用静态检查检测上述的错误。
TypeScript 是 JavaScript 的 超集。TypeScript 不会将任何 JavaScript 代码视为错误。这意味着你可以将任何有效的 JavaScript 代码放在 TypeScript 文件中,而不必担心它的确切编写方式。
for
for…in… 和 for…of…
for...in 和 for...of 都可以用于遍历,但遍历的对象和结果不同。
for...in:遍历对象的可枚举属性名(key)
for...of:遍历可迭代对象(Iterable)产生的值(value)
这和 Python 中使用 for...in 遍历有所区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| let array = [1, 2, 3, 4, 5];
array.name = "MyArray";
console.log("Using for...of loop:");
for (let x of array) { console.log(x); }
console.log("Using for...in loop:");
for (let x in array) { console.log(x); }
|
typeof
null 类型为 object
1 2 3 4 5 6 7 8 9
| function printAll(strs: string | string[] | null) { if (typeof strs === "object") { for (const s of strs) { console.log(s); } } }
|
当你写 typeof strs === "object" 时,你原本以为它能过滤出数组(Array),但实际上它同时把 null 也放进来了。
1 2 3
| "use strict"; let strs = null; console.log(typeof strs);
|