JavaScript的现代进阶:从ES6到ES15
自从
ES6(ECMAScript 2015)
以来,JavaScript
作为一门语言经历了前所未有的变革,每年的新版本都带来了令人振奋的新特性和优化,极大地提升了开发者的生产力和代码的可维护性。本文将深入探讨从ES6
到ES15(ECMAScript 2024)
期间JavaScript
的演变历程,旨在为开发者提供一份全面的指南,涵盖语言的关键更新和实用示例。
1. ES6 (ECMAScript 2015): 现代JavaScript的起点
1.1. let 和 const
- let 用于声明块级作用域的变量,解决了变量提升的问题。
- const 用于声明不可重新赋值的常量,提高了代码的可预测性。
let count = 0;
const PI = 3.14;
1.2. 解构赋值
对象解构和数组解构提供了从复杂数据结构中快速提取数据的便捷方式。
const {firstName, lastName} = {firstName: 'John', lastName: 'Doe'};
console.log(firstName, lastName); // John Doe
const [first, second] = [1, 2];
console.log(first, second); // 1 2
测试:
1.3. 模板字符串
允许在字符串中嵌入表达式,极大地提高了字符串拼接的可读性和效率。
const name = 'Alice';
console.log(`Hello, ${name}!`); // Hello, Alice!
测试:
1.4. 箭头函数
提供了一种更简洁的函数定义方式,同时自动绑定this
,避免了闭包中的this
问题。
const add = (a, b) => a + b;
add(1, 2); // 3
测试:
1.5. 类
虽然JavaScript
本质上是基于原型的,但类语法提供了面向对象编程的语法糖,使代码更易于理解和维护。
class Person {constructor(name) {this.name = name;}greet() {console.log(`Hello, ${this.name}`);}
}const person = new Person('John');
person.greet()
测试:
1.6. 模块系统
引入了import
和export
关键字,实现了真正的模块化编程,提高了代码的组织性和复用性。
// myModule.js
export const PI = 3.14;// main.js
import {PI} from './myModule.js';
1.7. Promises
为异步编程提供了一个更优雅的解决方案,替代了回调地狱,使异步代码更易于理解和调试。
const promise = new Promise((resolve, reject) => {setTimeout(() => resolve('Done!'), 1000);
});
console.log(promise)
测试:
1.8. Symbols
用于创建唯一属性键,避免了命名冲突,特别适用于私有属性的实现。
const uniqueId = Symbol('id');
console.log(uniqueId);
测试:
1.9. Array.from()
- 从类数组对象或可迭代对象创建新的数组实例。
const arrayLike = {length: 3, 0: 'a', 1: 'b', 2: 'c'};
console.log(arrayLike)
const arr = Array.from(arrayLike);
console.log(arr)
测试:
1.10. Map 和 Set
Map
是一种容器,用于存储键值对。与普通的JavaScript
对象不同,Map
的键可以是任意类型的值,包括函数、对象、基本类型等。Map
的一些主要特性包括:
- 键值对的顺序是基于插入顺序的。
- 可以通过键来快速检索值。
- 键和值都可以是任意类型的值。
- 提供了
get, set, delete, has, clear
等方法来操作数据。 Map
的大小可以通过size
属性获取。
const myMap = new Map();
myMap.set('key1', 'value1');
myMap.set(123, 'another value');
console.log(myMap.get('key1')); // 输出: value1
console.log(myMap.size); // 输出: 2
测试:
Set
是一种集合,用于存储唯一的元素列表。Set
自动确保没有重复的值。其主要特性包括:
- 元素的顺序也是基于插入顺序的。
- 可以检查元素是否存在,添加新元素,删除元素等。
Set
的元素可以是任何类型的值,但每个值只能出现一次。Set
的大小同样可以通过size
属性获取。
const mySet = new Set();
mySet.add('a');
mySet.add('b');
mySet.add('a'); // 这不会添加,因为 'a' 已经存在
console.log(mySet.has('a')); // 输出: true
console.log(mySet.size); // 输出: 2
1.11. Array.of()
- 从零个或多个参数创建新的数组实例。
const arr = Array.of(1, 2, 3);
console.log(arr)
测试:
1.12. 生成器函数 function* () {}
- 可以使用yield关键字暂停和恢复函数的执行。当生成器函数被调用时,它不会立即执行,而是返回一个迭代器对象。当迭代器的
next()
方法被调用时,函数会从上一次暂停的地方继续执行,直到遇到下一个yield
表达式或函数结束。 - 可以返回一个序列。生成器函数可以产生一系列的值,而不是像普通函数那样只能返回一个值。
- 可以接收外部传入的值。通过
next()
方法的参数,可以向生成器函数内部传递值。
function* numberGenerator() {yield 1; // 第一次调用next()时返回yield 2; // 第二次调用next()时返回return 'done'; // 可以返回一个最终值,但通常不使用
}const gen = numberGenerator();console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 'done', done: true }
console.log(gen.next()); // { value: undefined, done: true }
测试:
在这个例子中,numberGenerator
是一个生成器函数,它会产生两个数字,然后结束。每次调用gen.next()
都会执行生成器函数直到遇到下一个yield
表达式或者函数结束,并返回一个包含value
和done
属性的对象。value
属性包含了yield表达式产生的值,而done
属性表示生成器是否已经完成了所有的迭代。
1.13. 默认参数和剩余参数
- 函数参数变得更加灵活,支持默认值和收集不定数量的参数。
// 参数默认值
function sum(a = 10, ...nums) {return a + nums.reduce((acc, curr) => acc + curr, 0);
}
console.log(sum()); // 10// 剩余参数
function sum(...nums) {return nums.reduce((acc, curr) => acc + curr, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
测试:
1.14. String.prototype.repeat()
- 重复字符串多次。
const str = 'abc'.repeat(3);
console.log(str); // 'abcabcabc'
测试:
2. ES7 (ECMAScript 2016): 数学运算的提升
2.1. 指数运算符 (**)
- 简化了幂运算的语法。
const result = 2 ** 3;
console.log(result); // 8
测试:
3. ES8 (ECMAScript 2017): 异步编程的革命
3.1. 异步函数 (async/await)
- 使异步代码看起来更像同步代码,极大地简化了异步流程的控制。
async function fetchData() {const response = await fetch('/data');return await response.json();
}
3.2. 扩展运算符 (...)
- 用于复制数组和对象中的元素,也用于函数调用时收集参数。
const arr = [...[1, 2], ...[3, 4]];
console.log(arr)
测试:
3.3. Array.prototype.includes()
- 检查数组是否包含特定元素,返回布尔值。
const arr = [1, 2, 3];
console.log(arr.includes(2)); // true
测试:
3.4. String.prototype.padStart() 和 String.prototype.padEnd()
- 在字符串的开始或结束填充字符。
const str1 = '123'.padStart(7, '0');
console.log(str1); // '0000123'const str2 = '123'.padEnd(7, '0');
console.log(str2); // '1230000'
测试:
4. ES9 (ECMAScript 2018): 异步迭代的引入
4.1. 异步迭代 (async iterators)
- 支持异步遍历数据流,为处理大量数据提供了新的途径。
const asyncIterable = {[Symbol.asyncIterator]() {return {next: () => Promise.resolve({done: false, value: 1})};}
};
console.log(asyncIterable)
测试:
4.2. String.prototype.trimLeft() 和 String.prototype.trimRight()
- 左右两侧去除空白字符。
const str1 = ' hello world ';
console.log(str1.trimLeft()); // 'hello world '
const str2 = ' hello world ';
console.log(str2.trimRight()); // ' hello world'
测试:
5. ES10 (ECMAScript 2019): 数组和字符串的增强
5.1. 扁平化数组 (flat and flatMap)
- 提供了将多维数组扁平化到指定深度的能力。
- 它可以接受一个可选的参数
depth
,表示要展开的数组的深度。如果不提供depth
参数,或者将其设置为 1,那么flat
将只会展开第一层的子数组。
const arr = [1, [2, [3, [4]]]];
const flatArr = arr.flat(2);
console.log(flatArr); // [1, 2, 3, [4]]const deepArr = [1, [2, [3, [4, [5]]]]];
const deepFlatArr = deepArr.flat(4);
console.log(deepFlatArr); // [1, 2, 3, 4, 5]
测试:
5.2. Array.prototype.flatMap()
Array.prototype.flatMap()
结合了map()
和flat()
的功能。flatMap()
首先对数组中的每个元素应用一个映射函数,然后将结果展平一层。这使得处理和转换嵌套数组变得更加简单和高效。
语法:array.flatMap(callback(element[, index[, array]])[, thisArg])
flatMap()
接受一个回调函数作为参数,这个函数会被应用于数组的每个元素,并且可以返回一个新的数组。flatMap()
会自动将这些新数组展平到一个数组中,而不是返回一个嵌套数组。callback
: 必需。一个函数,其参数与map()
方法相同。element
: 当前被处理的数组元素。index
: 可选。当前元素的索引。array
: 可选。调用flatMap
的数组本身。thisArg
: 可选。指定执行回调函数时this
的值。
const numbers = [[1, 2], [3, 4], [5, 6]];const flattened = numbers.flatMap(x => x);console.log(flattened); // 输出: [1, 2, 3, 4, 5, 6]
5.3. String.prototype.trimStart 和 trimEnd
- 提供了更细粒度的字符串修剪功能。
const str1 = ' Hello World ';
console.log(str1.trimStart());
const str2 = ' Hello World ';
console.log(str2.trimEnd());
5.4. String.prototype.matchAll
- 返回字符串中所有匹配正则表达式的迭代器,便于处理复杂的文本模式。
const str = 'hello world';
const regex = /l/g;
const matches = str.matchAll(regex);
for (const match of matches) {console.log(match);
}
测试: