JavaScript 语言基础 (2021)
JavaScript 语言基础 (2021)
# 写在前面
# 新语法 TLDR
如果你已经比较熟悉 JavaScript 了,这里有一些最近新标准中新增的语法,以防你看到类似的语法不知道该怎么搜它们…
- 空值合并 (opens new window)
a ?? b
- 可选链 (opens new window)
a?.b
,a?.[0]
,fn?.(a, b)
- 新的赋值运算符 (opens new window)
a ||= b
,a ??= b
... - BigInt (opens new window)
10n
,BigInt(10)
,BigInt("10")
- Generator (opens new window)
function* fn() { yield 1; }
- class 私有属性 (opens new window)
class A { #val = 2; }
# 很少用到或建议别用
历史遗毒、糟粕,或有其他实现方式,看到尽量绕行。
var
(opens new window)eval
(opens new window)new Function
(opens new window)with
(opens new window)label
(opens new window)==
(opens new window) 非严格相等
# 教材
- 视频
- Node.js Ultimate Beginner’s Guide in 7 Easy Steps (opens new window) 16min:是什么和如何使用 Node.js 和运行 JavaScript,模块化,包
- JavaScript Tutorial for Beginners: Learn JavaScript in 1 Hour (opens new window) 1h:在浏览器中运行 JS,基本语法
- JavaScript Crash Course For Beginners (opens new window) 2h:JS 简介,JS(ES6) 语法、基本类型、API,类,简单 DOM 操作
- Learn JavaScript - Full Course for Beginners (opens new window) 3h:运行 JS 的多种方式,JS(ES6) 语法、API、执行细节、代码技巧、模块化
- The JavaScript Survival Guide (opens new window) 15min:一些 JS 的坑和技巧
- 教程
- MDN 文档
- 文档
- 索引
- 书
- JavaScript 权威指南 (opens new window):犀牛书
- JavaScript 高级程序设计 (opens new window):红宝书、高程(第 4 版)
- 扩展阅读:JavaScript 历史
# 概况
# JavaScript 生态
- Node.js (opens new window):JS 的命令行运行环境
– 包管理
- npm (opens new window):自带的包管理
- pnpm (opens new window)(我用这个)
- 版本管理
- Node 版本管理(如果你需要的话)
- nrm (opens new window):npm 源管理
- VS Code 插件
- 杂项
# 术语表
- JavaScript
- JS (opens new window):JavaScript,ECMAScript 的具体实现
- ES (opens new window):ECMAScript,JavaScript 的语言标准
- ES5:5 指版本号。几乎所有环境(如 IE 浏览器)都支持运行,兼容性高,但是缺少了很多便捷的新语法
- ES6/ES2015:6 指版本号,2015 指年份。是一次重大的更新,新增了很多新语法,部分老浏览器(如 IE)无法解析运行
- ESNext (opens new window):表示最新的 ES 版本
- Web API (opens new window):只有浏览器有的操纵页面的 API
- DOM (opens new window):Document Object Model,文档对象模型。Web API 中和 HTML 节点有关的部分
- ES (opens new window):ECMAScript,JavaScript 的语言标准
- JSON (opens new window):JavaScript Object Notation,基于 JS 语法的数据格式
- 方言
- TS (opens new window):TypeScript,带类型的 JavaScript
- JSX (opens new window):React 框架提供的 HTML 模板语法糖,在 JS 中写 HTML
- TSX (opens new window):TS + JSX
- Vanilla JS (opens new window):指原生 JS,Vanilla 表示 Plain
- JS (opens new window):JavaScript,ECMAScript 的具体实现
- ECMA
- ECMA (opens new window):European Computer Manufacturers Association,欧洲计算机制造商协会
- ECMA-262 (opens new window):ECMAScript 语言标准的文档编号
- ECMA TC39 (opens new window):制定和发展 ES 语言标准的业界团体
- 编程语言
- Statement (opens new window):语句,一行任务指令
- Expression (opens new window):表达式,一个表达式的结果总是某个值(或 undefined)
- API (opens new window):Application Programming Interface,应用程序接口,复杂逻辑的封装。比如:
console.log()
是一个用于 log 的 API - argument (opens new window):形参,指定义函数时的参数
- parameter (opens new window):实参,指调用函数时,传入的参数
- builtin function (opens new window):内建函数。比如:数组的
slice
方法是浏览器引擎内建的方法 - DSL (opens new window):Domain-Specific Language,领域特定语言。比如:JSX 可以认为是 JS 的一个 DSL
- Polyfill (opens new window):模拟新标准 API 的代码,让旧浏览器能够运行新的语言功能
- LHS/RHS (opens new window):Left/Right Hand Side,指一个表达式/语句中,操作符左/右测的语句
# Node.js 安装和运行 (Mac)
brew install node
npm -g i pnpm
node -v
npm -v
pnpm -v
node
> 1+1
> .exit
node script.js
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
brew install n
n ls
sudo n 14
sudo n 16
1
2
3
4
5
2
3
4
5
npm install -g nrm
nrm ls
nrm use taobao
1
2
3
4
2
3
4
npm init
# npm
npm i -g @vue/cli # 全局安装
npm init # 新建一个项目
npm i lodash # 在项目中安装一个包
npm un lodash # 在项目中卸载一个包
1
2
3
4
5
6
7
2
3
4
5
6
7
# JavaScript 语言基础知识体系
# 语言特性
- 语言特性
- 动态类型,弱类型
- 基于原型的类继承
- 函数是一等公民
- 函数也是 Object
- 高阶函数
- 函数没有重载
- 单线程,非阻塞(EventLoop 队列)
- 高级语言,JIT(Just-In-Time)编译
# 基本语法
- 语法
- 分号和换行 (opens new window)
;
- 注释 (opens new window)
//
,/* */
- 变量和声明
- 变量声明 (opens new window)
var
,let
,const
- 变量命名 (opens new window) 区分大小写
a-z, A-Z, 0-9, _ $
- 保留字 (opens new window)
- 字面量 (opens new window)
- 变量声明 (opens new window)
- 严格模式 (opens new window)
"use strict";
- 尾逗号 (opens new window)
[1, 2,]
,{ a, b, }
- 扩展、剩余、解构 (opens new window)
[a = 1, b, ...rest] = arr
,{a: val = 1, b, ...rest} = obj
- 正则表达式 (opens new window)
/ab+c/i
- 分号和换行 (opens new window)
- 表达式与运算符 - 指南 (opens new window), 表达式与运算符 - 参考 (opens new window)
- (数值计算)
- 算数 (opens new window)
+
,-
,*
,/
,%
,**
- 自增/自减 (opens new window)
++
,--
- 位运算 (opens new window)
&
,|
,^
,~
,<<
,>>
,>>>
- 算数 (opens new window)
- (逻辑)
- 布尔运算 (opens new window)
!
,&&
,||
- 比较 (opens new window)
<
,<=
,>
,>=
- 字符串比较 (opens new window) 按 Unicode 编码顺序
- 相等性 (opens new window)
==
,===
,!=
,!==
- 布尔运算 (opens new window)
- 赋值 (opens new window)
=
,+=
,-=
,||=
,??=
... - (其他)
- 条件运算符/三元/三目 (opens new window)
a > b ? a : b
typeof
(opens new window)、instanceof
(opens new window)in
(opens new window)、delete
(opens new window)- 空值合并 (opens new window)
??
- 可选链 (opens new window)
?.
,?.[]
,?.()
- 圆括号运算符 (opens new window)
1 * (2 + 3)
- 逗号运算符 (opens new window)
a = (b, c)
void
(opens new window)
- 条件运算符/三元/三目 (opens new window)
- 运算符优先级 (opens new window)
- (数值计算)
- 控制流 (opens new window)
- 条件分支 (opens new window)
if
,else
,else if
- switch (opens new window)
switch
,case
,break
,default
- 条件分支 (opens new window)
- 循环/迭代 (opens new window)
- 循环 (opens new window)
for
,while
,do while
,break
,continue
,label
for in
(opens new window),for of
(opens new window)for await of
(opens new window)
- 循环 (opens new window)
- 错误处理 (opens new window)
try
,cache
,finally
throw
,Error
# Runtime
- 隐式全局变量 (opens new window)
- 变量提升 (opens new window)
- 暂时性死区 (opens new window)
- 短路求值 (opens new window)
- 垃圾回收 (opens new window)
# 原始数据类型
- 原始类型 (opens new window)
- number (opens new window)
0b111
,0o777
,0xfff
,10.5
,1.5e2
NaN
(opens new window),Infinity
(opens new window)
- BigInt (opens new window)
10n
,BigInt(10)
,BigInt("10")
- string (opens new window)
'Hello'
,"Hello"
,"Hello " + "World!"
- 访问字符 (opens new window)
str[0]
- 模板字符串 (opens new window)
`x + y = ${x + y}`
- 标签模板 (opens new window)
alert`hello`
- 转义字符 (opens new window)
\n
,\\
\u00A9
,\u{1f60d}
String.raw()
(opens new window)
- boolean (opens new window)
true
,false
- symbol (opens new window)
Symbol('id')
Symbol.for('id')
,Symbol.keyFor(sym)
.description
- undefined (opens new window)
- null (opens new window)
- number (opens new window)
- 特性
- String 和 Number 相关常见 API
- String (opens new window)
.length
.concat()
,.slice()
,.split()
,.repeat()
.includes()
,.indexOf()
,.lastIndexOf()
.match()
,.matchAll()
,.replace()
,.replaceAll()
,.search()
.startsWith()
,.endsWith()
.trim()
,.trimEnd()
,.trimStart()
,.padEnd()
,.padStart()
.charAt()
,.charCodeAt()
,.codePointAt()
String.fromCharCode()
,String.fromCodePoint()
,String.raw()
- Number (opens new window)
.toFixed()
Number.isFinite()
,Number.isNaN()
,Number.parseInt()
,Number.parseFloat()
Number.EPSILON
,Number.MAX_SAFE_INTEGER
- Math (opens new window)
Math.E
,Math.PI
,Math.SQRT1_2
,Math.SQRT2
Math.abs()
,Math.sign()
Math.max()
,Math.min()
,Math.random()
Math.ceil()
,Math.floor()
,Math.round()
,Math.trunc()
Math.pow()
,Math.sqrt()
,Math.cbrt()
Math.exp()
,Math.log()
,Math.log10()
,Math.log2()
Math.cos()
,Math.sin()
,Math.tan()
,Math.acos()
,Math.asin()
,Math.atan()
...
- String (opens new window)
# 引用类型 对象 数组
- Object (opens new window)
{ a: 1, b: 2, c: 3 }
- 简写 (opens new window)
{ a, b, fn() {} }
- 计算属性 (opens new window)
{ [key]: value }
- 属性读取 (opens new window)
obj.key
,obj['key']
- getter/setter (opens new window)
{ get k() {}, set k(v) {} }
this
(opens new window)- 对象 — 原始值转换 (opens new window)
.toString()
,.valueOf()
,Symbol.toPrimitive
- 引用类型 (opens new window)
- Object 常见 API (opens new window)
Object.keys()
,Object.values()
,Object.entries()
,Object.fromEntries()
Object.create()
,Object.assign()
,Object.is()
,.isPrototypeOf()
Object.hasOwn()
,.hasOwnProperty()
Object.defineProperty()
,Object.getOwnPropertyDescriptor()
...Object.preventExtensions()
,Object.freeze()
,Object.seal()
Object.isExtensible()
,Object.isFrozen()
,Object.isSealed()
- Array (opens new window)
[1, 2, 3, null, {}, [6, 7]]
- 多维数组 (opens new window)
arr[0]
,arr[0][0]
.length
- 数组是基于对象的 (opens new window)
- 类数组 (opens new window)
- Iterable (opens new window)
Symbol.iterator
,for of
- Array 常见 API (opens new window)
Array.from()
,Array.of()
,Array.isArray()
,.fill()
.sort()
,.reverse()
,.flat()
,.flatMap()
.pop()
,.push()
,.shift()
,.unshift()
.map()
,.filter()
,.reduce()
,.reduceRight()
,.forEach()
,.every()
,.some()
.find()
,.findIndex()
,.includes()
,.indexOf()
,.lastIndexOf()
.concat()
,.join()
,.slice()
,.splice()
.keys()
,.values()
,.entries()
# 函数
- Function (opens new window)
- 声明
function fn(a, b) { return a + b }
- 函数表达式 (opens new window)
const fn = function () {}
- 箭头函数 (opens new window)
(a, b) => a + b
- 没有
this
(opens new window) (所以bind
、new
等操作都无效) - 不绑定
arguments
(opens new window)
- 没有
- 参数
- 默认值 (opens new window)
- 解构 (opens new window)
({ max, min = 0 }) => {}
- 按值传递 (opens new window)
return
(opens new window)- 执行
this
(opens new window) (直接调用、方法调用、构造函数、apply/call/bind)- 杂项
- 绑定
- 实用工具
- 声明
# Class
- class (opens new window)
class Cls extends Base {}
,new Cls()
- 基本结构
- getter/setter (opens new window)
- 继承
- 静态方法 (opens new window)
static
- 底层
# 部分其他内置对象
- RegExp (opens new window)
- JSON (opens new window)
- Date (opens new window)
- Map Set (opens new window)
- Proxy Reflect (opens new window)
# 异步 事件循环
- Promise (opens new window)
new Promise((res, rej) => { res(value); }).then((v) => { log(v) })
- Promise 化 (opens new window)
- Promise API (opens new window)
.then()
,.catch()
,.finally()
Promise.all()
,Promise.allSettled()
,Promise.any()
,Promise.race()
Promise.resolve()
,Promise.reject()
- async/await (opens new window)
- Generator (opens new window)
function* fn() {}
,yield
.next()
,.value
,.done
Symbol.asyncIterator
(opens new window)- 事件循环 (opens new window)
# 模块化
- 模块化 (opens new window)
import
,export
- 动态导入 (opens new window)
import()
# JavaScript 代码片段
# ES5 基本语法
// 单行注释
/* 注释 */
/*
多行注释
多行注释
*/
// * ---------------- 变量
var a = '1';
var empty; // => undefiend 表示未赋值
// * ---------------- 字符串、运算、显式类型转换、方法调用
var str = 'hello' + ' ' + 'world!'; // => 'hello world!'
String(233); // => '233'
str.split(' '); // => ["hello", "world!"]
// * ---------------- 数字、运算优先级(括号)、特殊值、位运算
var num = 1 - ((3 + 1.5) * 4) / 2; // => 8
Number('2'); // => 2
(2).toFixed(3); // => "2.000"
var na = 'oh' / 'no'; // => NaN (not a number)
Infinity; // => 无穷大
((1 & 2) | 3) << 2; // => 12 (0b1100)
// * ---------------- 布尔值、逻辑运算、相等性、隐式类型转换
var bo = (true && false) || !'str'; // => false
Boolean(42); // => true
+0 <= -0; // => true
0 == '0'; // => true
0 === '0'; // => false
null === undefined; // => false
// * ---------------- 数组、字面量声明
var arr = [1, 'hello'];
var arr2 = new Array(3); // => [empty * 3]
arr.length; // => 2
var falsy = [0, [], '', false, null, undefined];
// * ---------------- 对象、嵌套对象、成员调用
var obj = {
num: 1024,
str: 'emmm',
f: function (a, b) {
console.log(a, b);
},
mix: { key: 'value', arr: [1, { k: 'v' }] },
};
var another = obj.num; // => 1024
obj.f(1, obj.str); // => 1 "emmm"
// * ---------------- 函数、参数、运行、返回值、上下文绑定
function run(a, b) {
console.log('ahh', a, b); // => arr hello world
return 'complete';
}
var result = run('hello', 'world'); // => 'complete'
run.call(obj, 'hello', 'world');
run.apply(obj, ['hello', 'world']);
var boundRun = run.bind(obj);
boundRun('hello', 'world');
// * ---------------- 逻辑流
if (true) console.log('inline');
if (false) {
console.log('case 1');
} else if (2 > 3) {
console.log('case 2');
} else {
console.log('case 3');
}
// * switch
var score = 'A';
switch (score) {
case 'A':
console.log('Good');
break;
case 'B':
console.log('Nice');
break;
default:
console.log('Nope');
break;
}
// * for
for (var i = 0; i < 3; i++) {
console.log(i); // 打印 3 次
}
// * while
while (Math.random() < 0.7) {
console.log('runned'); // 当条件为 true 时执行
}
// * ---------------- 类、this、prototype
function Legacy(arg) {
this.value = arg;
}
Legacy.prototype = {
log: function () {
console.log('value is:', this.value);
},
};
var inst = new Legacy(2); // => Legacy {value: 2}
inst.value; // => 2
inst.log(); // => value is: 2
// * ---------------- getter、setter
var obj2 = {
get myValue() {
return this._val;
},
set myValue(v) {
this._val = 'hello ' + v;
},
};
console.log(obj2.myValue); // => undefined
obj2.myValue = 'world';
console.log(obj2.myValue); // => hello world
// * ---------------- 基本包装类型、原型链、类型
'hello'.split(); // => ["hello"]
'hello'.split === String.prototype.split; // true
typeof 2; // => 'number'
typeof 'str'; // => 'string'
typeof [1, 2]; // => 'object'
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# ES6+ 新增语法
// * ---------------- 显式的块级作用域
{
// console.log(num);
// let 和 const 会有暂时性死区现象
// 提升了声明,但是没有赋值,取值会报错
// => ReferenceError: Cannot access 'num' before initialization
// * ---------------- 声明
let num = 2;
const str = `hello ${num}`; // => 'hello 2' (模板字符串)
let obj = {
k: 1,
num, // => 2 (对象属性简写,将自动使用上下文中的同名变量)
fn1: function () {},
fn2: () => {},
fn3() {},
};
// * ---------------- 箭头函数、默认值
const fn = (a, b = 666) => console.log(a, b);
fn(233); // => 233 666
// * ---------------- 类
class MyClass extends Object {
constructor(val) {
super(val);
this.val = val;
}
log() {
console.log('really', this.val);
}
}
const inst = new MyClass(666);
inst.log(); // => really 666
// * ---------------- 解构赋值(可嵌套)、剩余参数
const [a, b, ...c] = [1, 2, 3, 4, 5]; // => a=1, b=2, c=[3, 4, 5]
const { toString, valueOf } = Object;
const arr = [...c]; // => [3, 4, 5] (新的数组)
const fn2 = (a, ...rest) => console.log(rest);
fn2(1, 2, 3); // => [2, 3]
}
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# Beyond ES6
// * ---------------- 空值合并 nullish coalescing
{
const val = 0;
const a = val || 1; // => 1
const a = val ?? 1; // => 0
}
{
const val = null;
const a = val || 1; // => 1
const a = val ?? 1; // => 1
}
// * ---------------- 可选链 optional chaining
{
const a = null;
const b = a?.key; // => null
}
{
const a = { key: 1 };
const b = a?.key; // => 1
}
// * ---------------- 赋值运算
{
let a = true;
let b = false;
b &&= a; // => false
}
{
let a = null;
let b = 1;
b ??= a; // => 1
}
// * ---------------- BigInt
{
123456789012345678901234567890n;
1n + 2n; // => 3n
}
// * ---------------- Generator
{
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
generator.next(); // => { value: 1, done: false };
generator.next(); // => { value: 2, done: false };
generator.next(); // => { value: 3, done: true };
generator.next(); // => { value: undefined, done: true };
}
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
上次更新: Jan 29, 2022 6:24 PM