ES5+语法重点,持续更新
以下是 ES5 语法重点 及 ES6 至 ES2025 所有核心语法更新 的详细梳理,每个语法点均配可直接理解的代码示例:
ES语法持续更新
一、ES5 语法重点(2009年,现代JS基础)
ES5 奠定了 JavaScript 的工程化基础,以下是开发中必须掌握的核心:
1. 严格模式('use strict')
在文件/函数顶部启用,强制规范语法,减少隐性错误。
1 | |
2. 函数核心:闭包
函数访问外部作用域变量的特性,即使外部函数已执行完毕。
1 | |
3. 数组核心方法(ES5 新增)
ES5 为数组添加了一批实用方法,是日常开发的高频工具:
1 | |
4. 对象属性定义:Object.defineProperty
精确控制对象属性的特性(可写、可枚举、可配置)。
1 | |
5. 原型链继承:Object.create
基于原型实现继承,是 ES5 主流的继承方式。
1 | |
二、ES6(ES2015):里程碑式大版本
ES6 彻底革新了 JavaScript 语法,引入了现代开发的核心特性:
1. 块级作用域:let / const
替代 var,解决变量提升和作用域混乱问题。
1 | |
2. 箭头函数
简写语法,this 绑定外层作用域,解决传统函数 this 指向混乱问题。
1 | |
3. 模板字符串
反引号包裹,支持变量插值和多行字符串。
1 | |
4. 解构赋值
快速从数组/对象中提取值并赋值给变量。
1 | |
5. 函数参数增强
默认参数 + 剩余参数(...)。
1 | |
6. 扩展运算符(...)
展开数组/对象,用于复制、合并。
1 | |
7. 类(Class)
基于原型的语法糖,让面向对象编程更清晰。
1 | |
8. Promise
异步编程的核心,解决“回调地狱”问题。
1 | |
9. 模块化(import / export)
静态模块化语法,替代 CommonJS,是现代前端工程化的基础。
1 | |
10. Set / Map
新的数据结构,补充数组和对象的不足。
1 | |
三、ES2016(ES7)至 ES2025:年度小版本迭代
ES6 之后每年更新小版本,持续补充语法糖和实用特性:
ES2016(ES7)
指数运算符(
**):替代Math.pow()1
console.log(2 ** 3); // 8(等价于 Math.pow(2, 3))**
Array.prototype.includes()**:检查数组是否包含值(可识别NaN)1
2const arr = [1, 2, NaN];
console.log(arr.includes(NaN)); // true(indexOf 无法识别 NaN)
ES2017(ES8)
**
async/await**:Promise 语法糖,异步代码同步写(现代异步开发首选)1
2
3
4
5
6
7
8
9
10
11async function fetchData() {
try {
const data = await new Promise(resolve =>
setTimeout(() => resolve('Data'), 1000)
);
console.log(data); // Data(1秒后)
} catch (err) {
console.error(err);
}
}
fetchData();**
Object.values()/Object.entries()**:获取对象值/键值对数组1
2
3const obj = { a: 1, b: 2 };
console.log(Object.values(obj)); // [1, 2]
console.log(Object.entries(obj)); // [['a', 1], ['b', 2]]字符串补全:
padStart()/padEnd()1
2console.log('5'.padStart(2, '0')); // '05'(补全到2位,前面补0)
console.log('Hello'.padEnd(10, '!')); // 'Hello!!!!!'
ES2018(ES9)
对象扩展运算符:ES6 只支持数组,ES2018 支持对象
1
2
3const obj1 = { a: 1 };
const obj2 = { ...obj1, b: 2 };
console.log(obj2); // { a: 1, b: 2 }**
Promise.prototype.finally()**:Promise 结束时无论成功失败都执行1
2
3
4fetchData()
.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => console.log('Request finished')); // 总是执行
ES2019(ES10)
可选链操作符(
?.):安全访问深层属性,避免“Cannot read property of undefined”1
2
3const user = { address: { city: 'New York' } };
console.log(user?.address?.city); // 'New York'
console.log(user?.profile?.age); // undefined(不抛错)空值合并操作符(
??):仅当左边为null/undefined时返回右边(区别于||)1
2
3const count = 0;
console.log(count ?? 10); // 0(0 不是 null/undefined,保留原值)
console.log(count || 10); // 10(|| 会把 0 当作 falsy 值)**
Array.prototype.flat()/flatMap()**:扁平化数组1
2
3
4
5const arr = [1, [2, [3, 4]]];
console.log(arr.flat(2)); // [1, 2, 3, 4](flat(深度))
const mapped = [1, 2].flatMap(x => [x, x * 2]);
console.log(mapped); // [1, 2, 2, 4](map 后 flat(1))
ES2020(ES11)
**
BigInt**:大整数类型,支持超过Number.MAX_SAFE_INTEGER的计算1
2const bigNum = 123456789012345678901234567890n;
console.log(bigNum + 1n); // 123456789012345678901234567891n**
Promise.allSettled()**:等待所有 Promise 状态改变(无论成功失败)1
2
3
4
5
6const promises = [
Promise.resolve('Success'),
Promise.reject('Error')
];
Promise.allSettled(promises).then(results => console.log(results));
// 输出:[{ status: 'fulfilled', value: 'Success' }, { status: 'rejected', reason: 'Error' }]**动态
import()**:按需导入模块,返回 Promise1
2
3
4
5// 按需加载模块(比如点击按钮时)
button.addEventListener('click', async () => {
const module = await import('./heavy-module.js');
module.doSomething();
});
ES2021(ES12)
逻辑赋值运算符:
&&=、||=、??=(简化逻辑赋值)1
2
3
4
5let a = 1;
a &&= 2; // 等价于 a = a && 2 → 2
let b = null;
b ??= 10; // 等价于 b = b ?? 10 → 10at()方法:字符串/数组支持负数索引(从末尾取)1
2
3
4
5const str = 'Hello';
console.log(str.at(-1)); // 'o'(最后一个字符)
const arr = [1, 2, 3];
console.log(arr.at(-2)); // 2(倒数第二个元素)**
Promise.any()**:只要一个 Promise 成功就返回(区别于Promise.race())1
2
3
4
5
6const promises = [
Promise.reject('Error 1'),
Promise.resolve('Success'),
Promise.reject('Error 2')
];
Promise.any(promises).then(data => console.log(data)); // 'Success'
ES2022(ES13)
类私有字段/方法:
#前缀,仅类内部可访问1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Person {
#name = 'Alice'; // 私有字段
#sayHi() { // 私有方法
console.log(`Hi, I'm ${this.#name}`);
}
greet() {
this.#sayHi(); // 类内部可访问
}
}
const alice = new Person();
alice.greet(); // Hi, I'm Alice
console.log(alice.#name); // SyntaxError: Private field '#name' must be declared**
Object.hasOwn()**:替代Object.prototype.hasOwnProperty,更安全1
2
3const obj = { a: 1 };
console.log(Object.hasOwn(obj, 'a')); // true
console.log(Object.hasOwn(obj, 'toString')); // false(不检查原型链)
ES2023(ES14)
- 数组从末尾查找:
findLast()/findLastIndex()1
2
3const arr = [1, 2, 3, 4, 3];
console.log(arr.findLast(x => x === 3)); // 3(最后一个匹配的元素)
console.log(arr.findLastIndex(x => x === 3)); // 4(最后一个匹配的索引)
ES2024(ES15)
Well-Formed Unicode Strings:
isWellFormed()/toWellFormed()1
2
3const badStr = 'a\uD800'; // 不完整的 Unicode 代理对
console.log(badStr.isWellFormed()); // false
console.log(badStr.toWellFormed()); // 'a�'(替换为有效字符)**
ArrayBuffer.prototype.resize()**:动态调整 ArrayBuffer 大小1
2
3
4const buffer = new ArrayBuffer(8);
console.log(buffer.byteLength); // 8
buffer.resize(16);
console.log(buffer.byteLength); // 16
ES2025(预计)
目前处于提案阶段的核心特性(可能调整):
- 装饰器(Decorators):为类/方法添加元数据和增强逻辑(Stage 3)
1
2
3
4
5@log
class Person {
@readonly
name = 'Alice';
}
补充
详细补充和深度解析,结合代码示例和痛点对比,让内容更透彻。
一、Promise 的三个状态(超详细解析)
Promise 是 ES6 引入的异步编程解决方案,其核心是状态管理。Promise 有且仅有以下三个状态,且状态转换是单向不可逆的:
1. 状态定义
| 状态 | 含义 | 触发条件 | 后续操作 |
|---|---|---|---|
| pending | 等待中(初始状态) | Promise 实例刚创建时 | 可转换为 fulfilled 或 rejected |
| fulfilled | 已成功(resolved) | 调用 resolve() 时 |
触发 .then() 回调 |
| rejected | 已失败 | 调用 reject() 或抛出错误时 |
触发 .catch() 回调 |
2. 状态转换规则(核心!不可逆)
Promise 状态一旦从 pending 转换为 fulfilled 或 rejected,就永远不会再改变。
代码示例:状态不可逆
1 | |
3. 状态与回调的对应关系
.then()接收两个参数:onFulfilled(成功回调)和onRejected(失败回调,可选)。.catch()是.then(null, onRejected)的语法糖,专门捕获失败状态。.finally()无论成功失败都会执行(ES2018 新增)。
代码示例:完整状态流程
1 | |
二、Promise.race() 详细解析(与 all/any 对比)
Promise.race() 的核心是:“谁先改变状态,就用谁的结果”(不管成功还是失败)。
1. 语法与行为
1 | |
- 返回值:一个新的 Promise。
- 行为:
- 只要
iterable中任意一个 Promise 先改变状态(无论成功/失败),返回的 Promise 就会立即跟随改变状态。 - 其他未完成的 Promise 仍会继续执行,但结果会被忽略。
- 只要
2. 代码示例:快速响应
1 | |
3. 与 Promise.all() / Promise.any() 的核心区别
| 方法 | 成功条件 | 失败条件 | 适用场景 |
|---|---|---|---|
Promise.all() |
所有 Promise 都成功 | 任意一个 Promise 失败 | 并行请求,依赖所有结果 |
Promise.race() |
任意一个 Promise 先成功/失败 | 任意一个 Promise 先失败 | 超时控制、快速响应 |
Promise.any() |
任意一个 Promise 成功 | 所有 Promise 都失败 | 容错性高的并行请求(只要一个成) |
代码示例:race 用于超时控制
1 | |
三、ES6 替代 ES5 解决的核心痛点(深度对比)
ES5 时代的 JavaScript 存在诸多工程化和开发体验问题,ES6 的诞生就是为了系统性解决这些痛点。以下是核心痛点及 ES6 的解决方案:
痛点 1:var 的变量提升与作用域混乱
ES5 问题:
var存在变量提升(声明被提前到作用域顶部,但赋值不提前),导致逻辑难以预测。- 只有函数作用域,没有块级作用域,
if/for等块内变量会污染全局。
1 | |
ES6 解决方案:let / const
let/const没有变量提升,必须先声明后使用。- 支持块级作用域(
{}内生效),避免变量污染。
1 | |
痛点 2:回调地狱(Callback Hell)
ES5 问题:
异步操作依赖回调函数,多个异步操作串行时会导致嵌套层级过深,代码可读性和可维护性极差。
1 | |
ES6 解决方案:Promise + ES2017 async/await
Promise用链式调用(.then())替代嵌套,逻辑更清晰。async/await进一步让异步代码看起来像同步代码,彻底解决回调地狱。
1 | |
痛点 3:this 指向混乱
ES5 问题:
传统函数的 this 指向取决于调用方式(全局调用、对象方法调用、new 调用等),极易出错,常需用 var self = this 或 .bind(this) hack。
1 | |
ES6 解决方案:箭头函数
箭头函数没有自己的 this,**继承外层作用域的 this**,彻底解决 this 指向混乱问题。
1 | |
痛点 4:缺乏官方模块化方案
ES5 问题:
ES5 没有原生模块化语法,社区只能用第三方方案(CommonJS、AMD、CMD),导致代码不统一,浏览器端需额外打包工具(如 Browserify)。
1 | |
ES6 解决方案:import / export
ES6 提供官方静态模块化语法,浏览器和 Node.js(v12+)原生支持,是现代前端工程化的基础。
1 | |
痛点 5:对象/数组操作繁琐
ES5 问题:
- 合并对象/数组需用
Object.assign()或concat(),代码冗长。 - 访问深层对象属性需层层判断,否则容易报错
Cannot read property of undefined。
1 | |
ES6+ 解决方案:扩展运算符、可选链、空值合并
- 扩展运算符(
...):简洁合并数组/对象。 - 可选链(
?.):安全访问深层属性,无需层层判断。 - 空值合并(
??):精准处理null/undefined,避免误判0/''。
1 | |
问题汇总
一、浮点型精度问题
1. 问题现象
1 | |
2. 原因
JavaScript 采用 IEEE 754 双精度浮点数(64位)存储数字,二进制无法精确表示部分十进制小数(如 0.1、0.2),计算时会产生舍入误差。
3. 解决方案
方案 1:转整数计算(推荐)
1 | |
方案 2:使用 toFixed() 保留小数(注意返回字符串)
1 | |
方案 3:第三方库(生产环境推荐)
- decimal.js:高精度计算库
- big.js:轻量级精确计算库
1
2import Decimal from 'decimal.js';
console.log(new Decimal(0.1).plus(0.2).toNumber()); // 0.3
二、原型链与继承(核心难点)
1. 核心概念
- **
__proto__**:对象的隐式原型,指向构造函数的prototype。 - **
prototype**:构造函数的原型对象,包含共享属性和方法。 - **
constructor**:原型对象的构造函数指针,指回构造函数本身。
2. 原型链查找规则
当访问对象属性时:
- 先在对象自身查找;
- 找不到则通过
__proto__向上查找原型对象; - 直到
Object.prototype.__proto__(即null),若还找不到则返回undefined。
代码示例
1 | |
3. 常见考点:new 操作符的原理
1 | |
三、闭包(高频应用+难点)
1. 定义
函数能够访问其外部作用域变量的特性,即使外部函数已执行完毕。
2. 核心用途
用途 1:封装私有变量
1 | |
用途 2:函数柯里化
1 | |
3. 常见坑:循环中的闭包
1 | |
四、事件循环(Event Loop)(异步核心)
1. 核心概念
JavaScript 是单线程语言,通过事件循环实现异步编程:
- 调用栈:同步任务执行栈。
- 任务队列:存放异步任务的回调,分为:
- 宏任务(Macro Task):
setTimeout/setInterval、I/O、DOM 事件、requestAnimationFrame。 - 微任务(Micro Task):
Promise.then/catch/finally、async/await(底层是 Promise)、process.nextTick(Node.js)。
- 宏任务(Macro Task):
2. 执行顺序(必背!)
- 执行同步代码(调用栈清空);
- 执行所有微任务(清空微任务队列);
- 执行一个宏任务(从宏任务队列取一个);
- 再次执行所有微任务;
- 重复步骤 3-4。
代码示例(经典面试题)
1 | |
解析
- 同步执行
1、4; - 清空微任务,执行
3; - 执行一个宏任务,执行
2。
五、类型转换(隐式转换坑多)
1. 显式转换
1 | |
2. 隐式转换(高频考点)
规则 1:== 比较(尽量用 === 避免隐式转换)
1 | |
规则 2:+ 号运算
- 若有一个操作数是字符串,另一个转字符串拼接;
- 否则转数字相加。
1
2
3
4console.log(1 + '2'); // '12'(字符串拼接)
console.log(1 + true); // 2(true 转 1)
console.log(1 + null); // 1(null 转 0)
console.log(1 + undefined); // NaN(undefined 转 NaN)
规则 3:逻辑运算符(&&、||、!)
&&:返回第一个 falsy 值,否则返回最后一个值;||:返回第一个 truthy 值,否则返回最后一个值;!:转布尔后取反。1
2
3
4
5
6console.log(0 && 1); // 0
console.log(1 && 2); // 2
console.log(0 || 1); // 1
console.log(1 || 2); // 1
console.log(!0); // true
console.log(![]); // false([] 是 truthy)
六、this 指向(5种情况全覆盖)
1. 全局调用(非严格模式)
1 | |
2. 对象方法调用
1 | |
3. new 调用
1 | |
4. call/apply/bind 绑定
1 | |
5. 箭头函数
箭头函数没有自己的 this,**继承外层作用域的 this**,且绑定后不可改变。
1 | |
七、深拷贝与浅拷贝
1. 浅拷贝(只复制一层,嵌套对象仍共享引用)
1 | |
2. 深拷贝(完全复制,嵌套对象不共享引用)
方法1:JSON.parse(JSON.stringify())(简单但有局限)
1 | |
局限:无法复制函数、正则、Symbol、Date、循环引用对象等。
方法2:structuredClone()(现代浏览器/Node.js 17+ 支持)
1 | |
局限:仍无法复制函数。
方法3:递归实现(完整版)
1 | |
补充
JavaScript 异步编程
异步编程是 JavaScript 的核心特性,用于处理网络请求、文件读写、定时器等耗时操作,避免阻塞主线程。
1. Promise / async-await
这是目前处理异步操作的主流标准方案,async-await 是 Promise 的语法糖,让异步代码看起来像同步代码。
Promise 核心
1 | |
async-await 语法糖
1 | |
2. Generator
Generator 是 ES6 引入的一种特殊的迭代器函数,可以暂停和恢复执行,在 async-await 普及前,常被用于实现异步流程控制(如 co 库)。
核心语法与特性
1 | |