5、ES+新特性

24 阅读3分钟

1. let 和 const

let 和 const 是用来声明变量的新方式,它们提供了块级作用域。

// let
let x = 10;
if (true) {
  let x = 20;
  console.log(x); // 20
}
console.log(x); // 10

// const
const y = 30;
// y = 40; // TypeError: Assignment to constant variable.

const obj = { a: 1 };
obj.a = 2; // 允许修改对象的属性
console.log(obj.a); // 2

2. 箭头函数

箭头函数提供了一种更简洁的函数表达方式,并且不会绑定自己的 this

const add = (a, b) => a + b;
console.log(add(2, 3)); // 5

const numbers = [1, 2, 3, 4];
const squares = numbers.map(n => n * n);
console.log(squares); // [1, 4, 9, 16]

// 使用箭头函数避免 this 问题
function Timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++;
    console.log(this.seconds);
  }, 1000);
}

new Timer();

3. 模板字符串

模板字符串提供了嵌入表达式和多行字符串的功能。

const name = 'John';
const greeting = `Hello, ${name}!`;
console.log(greeting); // Hello, John!

const multiline = `This is a
multiline string.`;
console.log(multiline);
// This is a
// multiline string.

// 嵌入表达式
const a = 5;
const b = 10;
console.log(`The sum of ${a} and ${b} is ${a + b}.`); // The sum of 5 and 10 is 15.

4. 解构赋值

解构赋值允许从数组或对象中提取数据,并将其赋值给变量。

// 数组解构
const [a, b] = [1, 2];
console.log(a, b); // 1 2

const [c, d, e = 5] = [3, 4];
console.log(c, d, e); // 3 4 5

// 对象解构
const { name, age } = { name: 'Alice', age: 25 };
console.log(name, age); // Alice 25

const { x = 10, y = 20 } = { x: 5 };
console.log(x, y); // 5 20

// 解构赋值中的嵌套对象
const user = {
  name: 'John',
  address: {
    street: '123 Main St',
    city: 'New York'
  }
};

const { name: userName, address: { street, city } } = user;
console.log(userName); // John
console.log(street); // 123 Main St
console.log(city); // New York

5. 默认参数

函数参数可以有默认值。

function greet(name = 'Guest') {
  return `Hello, ${name}!`;
}
console.log(greet()); // Hello, Guest!
console.log(greet('Alice')); // Hello, Alice!

// 使用默认参数计算
function multiply(a, b = 1) {
  return a * b;
}
console.log(multiply(5)); // 5
console.log(multiply(5, 2)); // 10

6. 展开运算符

展开运算符用于展开数组或对象。

// 数组展开
const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4];
console.log(arr2); // [1, 2, 3, 4]

// 对象展开
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };
console.log(obj2); // { a: 1, b: 2, c: 3 }

// 函数参数展开
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10

// 数组拷贝和合并
const fruits = ['apple', 'banana'];
const moreFruits = ['orange', 'pear'];
const combinedFruits = [...fruits, ...moreFruits];
console.log(combinedFruits); // ['apple', 'banana', 'orange', 'pear']

7. Promise

Promise 是用于处理异步操作的新方式。它代表一个未来完成(或失败)的操作以及它的结果值。

创建 Promise

创建一个 Promise 实例,并用 resolve 和 reject 控制成功和失败。

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Success!');
  }, 1000);
});

promise.then(result => {
  console.log(result); // Success!
}).catch(error => {
  console.error(error);
});

链式调用

Promise 可以通过 then 链式调用多个处理器。

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
});

promise
  .then(result => {
    console.log(result); // 1
    return result + 1;
  })
  .then(result => {
    console.log(result); // 2
    return result + 1;
  })
  .then(result => {
    console.log(result); // 3
  })
  .catch(error => {
    console.error(error);
  });

错误处理

可以通过 catch 来捕获 Promise 中的错误。

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('Something went wrong!');
  }, 1000);
});

promise
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error(error); // Something went wrong!
  });

Promise.all

Promise.all 方法接收一组 Promise,并返回一个新的 Promise,当所有 Promise完成时,新的 Promise 将以一个数组的形式返回结果。

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log(results); // [1, 2, 3]
  })
  .catch(error => {
    console.error(error);
  });

Promise.race

Promise.race 方法接收一组 Promise,并返回一个新的 Promise,只要一个 Promise 完成或失败,新 Promise 就会以第一个完成或失败的结果值来结束。

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('First promise resolved');
  }, 1000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Second promise resolved');
  }, 500);
});

Promise.race([promise1, promise2])
  .then(result => {
    console.log(result); // Second promise resolved
  })
  .catch(error => {
    console.error(error);
  });

8. async 和 await

async 和 await 使处理异步代码更加简洁。

基本使用

一个 async 函数会返回一个 Promiseawait 关键字只能在 async 函数内部使用,用于等待一个 Promise 完成。

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}

fetchData();

串行执行

await 关键字可以串行执行多个异步操作,保证代码按顺序执行。

async function fetchMultipleData() {
  try {
    const response1 = await fetch('https://api.example.com/data1');
    const data1 = await response1.json();
    console.log(data1);

    const response2 = await fetch('https://api.example.com/data2');
    const data2 = await response2.json();
    console.log(data2);
  } catch (error) {
    console.error('Error:', error);
  }
}

fetchMultipleData();

并行执行

可以使用 Promise.all 并行执行多个异步操作,提高性能。

async function fetchParallelData() {
  try {
    const [response1, response2] = await Promise.all([
      fetch('https://api.example.com/data1'),
      fetch('https://api.example.com/data2')
    ]);

    const data1 = await response1.json();
    const data2 = await response2.json();

    console.log(data1);
    console.log(data2);
  } catch (error) {
    console.error('Error:', error);
  }
}

fetchParallelData();

9. 类(Class)

class 提供了更简洁的面向对象编程语法。

定义类

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
  }
}

const john = new Person('John', 30);
console.log(john.greet()); // Hello, my name is John and I am 30 years old.

继承

使用 extends 关键字实现类的继承,子类可以继承父类的属性和方法。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
  }
}

class Student extends Person {
  constructor(name, age, grade) {
    super(name, age); // 调用父类的构造函数
    this.grade = grade;
  }

  study() {
    return `${this.name} is studying in grade ${this.grade}.`;
  }
}

const alice = new Student('Alice', 20, 'A');
console.log(alice.greet()); // Hello, my name is Alice and I am 20 years old.
console.log(alice.study()); // Alice is studying in grade A.

静态方法

使用 static 关键字定义静态方法,静态方法可以直接通过类调用,而不是通过类实例。

class MathHelper {
  static add(a, b) {
    return a + b;
  }

  static subtract(a, b) {
    return a - b;
  }
}

console.log(MathHelper.add(2, 3)); // 5
console.log(MathHelper.subtract(5, 2)); // 3

10. 模块化

ES6 引入了模块化语法,允许将代码分割成更小的文件。

导出和导入

// math.js
export function add(a, b) {
  return a + b;
}

export const PI = 3.14;

// main.js
import { add, PI } from './math.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14

默认导出

模块可以有一个默认导出,使用 export default 语法。

// math.js
export default function multiply(a, b) {
  return a * b;
}

// main.js
import multiply from './math.js';
console.log(multiply(2, 3)); // 6

动态导入

动态导入允许在运行时按需加载模块。

// main.js
async function loadModule() {
  const { add } = await import('./math.js');
  console.log(add(2, 3)); // 5
}

loadModule();

11. 可选链操作符

可选链操作符 ?. 允许在链式调用时更安全地访问深层次的对象属性。

const user = {
  address: {
    street: '123 Main St'
  }
};

console.log(user?.address?.street); // 123 Main St
console.log(user?.contact?.phone); // undefined

12. 空值合并操作符

空值合并操作符 ?? 提供了一种更简洁的方式来处理 null 或 undefined

const name = null;
const defaultName = 'Guest';
console.log(name ?? defaultName); // Guest

const age = 0;
const defaultAge = 18;
console.log(age ?? defaultAge); // 0

13. 私有字段

私有字段以 # 开头,只能在类的内部访问。

class Person {
  #name;

  constructor(name) {
    this.#name = name;
  }

  greet() {
    return `Hello, my name is ${this.#name}.`;
  }
}

const alice = new Person('Alice');
console.log(alice.greet()); // Hello, my name is Alice.
// console.log(alice.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class

14. BigInt

BigInt 是一种新的原始数据类型,用于表示任意精度的整数。

const bigInt = 1234567890123456789012345678901234567890n;
console.log(bigInt); // 1234567890123456789012345678901234567890n

const sum = bigInt + 10n;
console.log(sum); // 1234567890123456789012345678901234567900n

// 将现有的Number转换为BigInt
const num = 42;
const bigIntNum = BigInt(num);
console.log(bigIntNum); // 42n

15. 动态导入

动态导入允许在运行时按需加载模块。

// main.js
async function loadModule() {
  const { add } = await import('./math.js');
  console.log(add(2, 3)); // 5
}

loadModule();

16. Symbol

Symbol 是一种新的原始数据类型,表示独一无二的值。

const sym1 = Symbol('description');
const sym2 = Symbol('description');
console.log(sym1 === sym2); // false

const obj = {
  [sym1]: 'value1'
};

console.log(obj[sym1]); // value1

17. Set 和 Map

Set 是一种新的集合类型,Map 是一种新的键值对集合类型。

// Set
const set = new Set([1, 2, 3, 3]);
console.log(set.size); // 3

set.add(4);
set.delete(2);
console.log(set.has(2)); // false

for (const value of set) {
  console.log(value); // 1 3 4
}

// Map
const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(map.get('key1')); // value1

map.set('key3', 'value3');
map.delete('key2');
console.log(map.has('key2')); // false

for (const [key, value] of map) {
  console.log(key, value); // key1 value1, key3 value3
}

18. WeakSet 和 WeakMap

WeakSet 和 WeakMap 是特殊的集合和键值对集合,不会阻止其包含的对象被垃圾回收。

// WeakSet
const weakSet = new WeakSet();
let obj = { foo: 'bar' };
weakSet.add(obj);
console.log(weakSet.has(obj)); // true

obj = null; // 对象被垃圾回收
console.log(weakSet.has(obj)); // false

// WeakMap
const weakMap = new WeakMap();
let keyObj = { key: 'value' };
weakMap.set(keyObj, 'some value');
console.log(weakMap.get(keyObj)); // some value

keyObj = null; // 对象被垃圾回收
console.log(weakMap.get(keyObj)); // undefined