ES6+ 新特性
概括
概括一下就是这些:
let const
解构赋值
新增运算符 ... ?. ??
模板字符串
内置对象新增了方法
Symbol 和 BigInt
Map Set 数据结构
Proxy Reflect
箭头函数
generator
promise
async await
class
ESModules
在项目中使用
let const
-
let 和 const 都具有块级作用域,因此在循环或条件语句中使用时,要注意变量的作用域。
-
let 和 const 都 存在“暂时性死区”(Temporal Dead Zone, TDZ)的问题。在声明之前,尝试访问这些变量会导致错误。
-
使用 let 和 const 声明的变量不能被重复声明。这避免了变量名冲突。
-
const 用于声明常量,表示变量的绑定不可更改,但对象的属性仍然可以修改,因此要注意深浅拷贝问题。
-
const 在声明时必须初始化,而 let 可以先声明后赋值。
-
尽量避免在全局作用域中使用 let 和 const,推荐在函数或模块中使用,以减少命名冲突。
让一个 const 声明的对象或数组不可变的方法:
- 使用 Object.freeze()
const obj = Object.freeze({ key: "value" });
obj.key = "newValue"; // 不会改变原对象
// 冻结对象
const arr = Object.freeze([
Object.freeze({ key: "value" }),
Object.freeze({ key: "anotherValue" }),
]);
- 深度冻结
如果对象包含嵌套对象,需要使用递归冻结。
function deepFreeze(obj) {
Object.freeze(obj);
Object.keys(obj).forEach((key) => {
if (obj[key] && typeof obj[key] === "object") {
deepFreeze(obj[key]);
}
});
}
const nestedObj = { key: { subKey: "value" } };
deepFreeze(nestedObj);
- 使用 Immutable.js 或类似库
使用库如 Immutable.js 创建不可变的数据结构。
const { List, Map } = require("immutable");
const immutableList = List([Map({ key: "value" })]);
- 使用 ES6 的展开运算符:
对于数组,可以使用展开运算符创建新的数组副本,而不直接修改原数组。
const arr = [1, 2, 3];
const newArr = [...arr]; // 创建新数组
解构赋值
- 数组的结构赋值
const arr = [1, 2, 3];
// 基本用法
const [a, b] = arr; // a = 1, b = 2
// 跳过元素
const [first, , third] = arr; // first = 1, third = 3
// 剩余参数
const [x, ...rest] = arr; // x = 1, rest = [2, 3]
- 对象的结构赋值
const obj = { name: "Alice", age: 25 };
// 基本用法
const { name, age } = obj; // name = 'Alice', age = 25
// 重命名变量
const { name: fullName } = obj; // fullName = 'Alice'
// 默认值
const { height = 180 } = obj; // height = 180, 因为 obj 中没有 height
- 嵌套结构赋值
const nested = { a: 1, b: { c: 2 } };
// 对象嵌套
const {
b: { c },
} = nested; // c = 2
const arrNested = [
[1, 2],
[3, 4],
];
const [[one, two], [three, four]] = arrNested; // one = 1, two = 2
- 解构赋值与函数参数
function display({ name, age }) {
console.log(`Name: ${name}, Age: ${age}`);
}
const person = { name: "Bob", age: 30 };
display(person); // 输出: Name: Bob, Age: 30
- 交换变量值
let a = 1;
let b = 2;
[a, b] = [b, a]; // a = 2, b = 1
- 结构赋值重命名
const person = { name: "Alice", age: 25 };
// 重命名
const { name: fullName, age: years } = person;
新增的运算符
- 扩展运算符 (
...
)
作用:
- 在数组或对象中展开元素。
使用场景:
- 合并数组或对象。
- 复制数组或对象。
- 函数参数收集。
// 数组
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // 合并数组
// 对象
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // 合并对象
// 函数参数收集
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 6
- 可选链运算符 (
?.
)
作用:
- 允许安全地访问对象的深层属性,避免因访问未定义的属性而导致的错误。
使用场景:
- 当不确定某个对象的某个属性是否存在时,可以避免手动检查每个层级。
const user = {
profile: {
name: "Alice",
},
};
// 安全访问
const userName = user.profile?.name; // 'Alice'
const userAge = user.profile?.age; // undefined
- 空值合并运算符 (
??
)
作用:
-
返回其左侧操作数,如果其为 null 或 undefined,则返回右侧操作数。 使用场景:
-
在赋值时提供默认值,特别是处理可能为空的值。
const value1 = null;
const value2 = 5;
const result = value1 ?? value2; // 5
const value3 = 0;
const result2 = value3 ?? 10; // 0,因为 0 不是 null 或 undefined
- 逻辑赋值运算符
// 或赋值运算符
x ||= y;
// 等同于
x || (x = y);
// 与赋值运算符
x &&= y;
// 等同于
x && (x = y);
// Null 赋值运算符
x ??= y;
// 等同于
x ?? (x = y);
模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。
模板字符串使用反引号(
)包裹,可以跨多行书写字符串。
如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
模板字符串中嵌入变量,需要将变量名写在$之中。大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
模板字符串之中还能调用函数。
let name = "Bob",
time = "today";
console.log(`Hello ${name}, how are you ${time}?`);
function fn() {
return "Hello World";
}
console.log(`foo ${fn()} bar`);
内置对象新增了方法
字符串扩展
正则扩展
数值扩展
函数扩展
数组扩展
对象扩展
Symbol 和 BigInt
Symbol:
用于创建唯一的标识符,避免命名冲突,适用于对象属性。
Symbol 使用场景 对象属性:可以使用 Symbol 作为对象的属性键,以避免属性冲突。
const uniqueKey = Symbol("key");
const obj = {
[uniqueKey]: "value",
};
console.log(obj[uniqueKey]); // 'value'
Symbol 还可以用于定义自定义的迭代器,例如实现 Symbol.iterator 方法。
const myIterable = {
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
return step <= 3 ? { value: step, done: false } : { done: true };
},
};
},
};
for (const value of myIterable) {
console.log(value); // 1, 2, 3
}
BigInt:
用于表示任意精度的整数,适用于大数计算,特别是在需要超出安全整数范围时。
const bigInt1 = BigInt(123456789012345678901234567890);
const bigInt2 = 123456789012345678901234567890n;
console.log(bigInt1); // 123456789012345678901234567890n
console.log(bigInt1 === bigInt2); // true
注意事项 BigInt 不能与 Number 直接进行运算,必须进行类型转换。
const num = 5;
const bigNum = 10n;
// 下面的代码会抛出错误
// console.log(num + bigNum);
// 正确方式
console.log(num + Number(bigNum)); // 15
Map Set 数据结构
Map 数据结构
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。
Set 数据结构
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Proxy Reflect
Proxy(代理) 是 ES6 中新增的一个特性。Proxy 让我们能够以简洁易懂的方式控制外部对对象的访问。其功能非常类似于设计模式中的代理模式。 Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。
与大多数全局对象不同 Reflect 并非一个构造函数,所以不能通过 new 运算符对其进行调用,或者将 Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的(就像 Math 对象)。
箭头函数
箭头函数的 this 指向声明时所在作用域下的 this 值。
箭头函数和普通函数的区别
语法: 箭头函数使用 ()=> ,普通函数使用 function 关键字来定义函数。
箭头函数没有自己的 this,它会继承其所在作用域的 this 值。而普通函数的 this 则由函数调用时的上下文决定,可以通过 call、apply、bind 方法来改变。
箭头函数没有自己的 arguments 对象,它可以通过 rest 参数语法来接收不定数量的参数。而普通函数则有自己的 arguments 对象,它可以接收任意数量的参数。
箭头函数不能作为构造函数使用,不能使用 new 来实例化,因为它没有自己的 this,而普通函数可以用 new 来创建新的对象。
箭头函数不 能使用 yield 关键字来定义生成器函数,而普通函数可以。
箭头函数不支持 call()/apply()函数特性
箭头函数没有 prototype 属性
原型函数不能定义成箭头函数
generator
生成器是一种特殊的函数,可以暂停执行并在后续时间继续执行。生成器在调用时不会立即执行,而是返回一个迭代器对象,可以通过该对象逐步执行生成器中的代码。
生成器使用 function*
语法定义,内部可以使用 yield
关键字来暂停执行并返回值。
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = myGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
生成器的特点
可暂停执行:生成器可以在执行过程中暂停和恢复。
迭代器协议:生成器自动实现了迭代器接口,可以与 for...of 循环一起使用。
生成器的应用场景
异步编程:生成器可以与 Promise 结合使用,简化异步代码的写法。
状态机:生成器可以用来构建状态机,通过 yield 暂停和恢复状态。
生成器是 JavaScript 中一个强大的特性,提供了一种优雅的方式来处理迭代和异步编程。它们通过暂停和恢复执行,使得代码更加灵活和可读。
promise
async await
class
ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return "(" + this.x + ", " + this.y + ")";
};
var p = new Point(1, 2);
// es6 写法
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return "(" + this.x + ", " + this.y + ")";
}
}
继承
class Point {
/* ... */
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + " " + super.toString(); // 调用父类的toString()
}
}
ESModules
ES Modules(ECMAScript 模块)是 JavaScript 的模块化机制,允许将代码分割成小的、可重用的模块。它提供了一种标准化的方法来导入和导出代码,从而实现模块之间的依赖管理。
导出(Export)
使用 export
关键字来导出模块中的变量、函数或类。
// module.js
export const name = "Alice";
export function greet() {
console.log("Hello, " + name);
}
导入(Import)
使用 import 关键字来导入其他模块的导出。
// main.js
import { name, greet } from "./module.js";
console.log(name); // 'Alice'
greet(); // 'Hello, Alice'