ECMAScript 2015 (ES6) 是 JavaScript 语言的一次重要更新,引入了大量新特性和改进。这些变化不仅增强了语言的功能,也使得代码更加简洁、易读和易于维护。给开发者带来极大的便利。
一、变量和作用域的改进
1. let
和 const
ES6 引入了 let
和 const
关键字来声明变量,提供了更严格的作用域控制和更好的代码安全性。
let
: 用于声明可以重新赋值的变量,具有块级作用域。
let count = 0;
if (true) {
let count = 1; // 仅在此块内有效
console.log(count); // 输出 1
}
console.log(count); // 输出 0
const
: 用于声明常量,一旦赋值就不能重新赋值。它同样具有块级作用域。
const PI = 3.14159;
PI = 3.14; // TypeError: Assignment to constant variable.
使用 let
和 const
替代 var
,可以避免变量提升和作用域污染的问题,使代码易于理解。
二、模板字符串
模板字符串使用反引号 (`) 括起来,允许在字符串中嵌入表达式和变量,非常适合构建动态内容。
const name = "Alice";
const message = `Hello, ${name}!`;
console.log(message); // 输出 "Hello, Alice!"
模板字符串还支持多行文本,使得格式化长文本更为简洁。
const multiline = `This is a
multiline string.`;
三、解构赋值
解构赋值允许从数组或对象中提取值并赋给变量,是处理复杂数据结构的一种简洁方法。
- 数组解构
const [a, b] = [1, 2];
console.log(a); // 输出 1
console.log(b); // 输出 2
- 对象解构
const person = { name: "Alice", age: 25 };
const { name, age } = person;
console.log(name); // 输出 "Alice"
console.log(age); // 输出 25
解构赋值还可以用于函数参数,使函数更加灵活和易于使用。
function greet({ name, age }) {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
greet({ name: "Bob", age: 30 });
四、箭头函数
箭头函数是一种简洁的函数定义方式,并且不会绑定 this
关键字,保持 this
指向定义时的上下文。
const add = (x, y) => x + y;
console.log(add(2, 3)); // 输出 5
它们非常适合简短的回调函数和内联函数。
const numbers = [1, 2, 3];
const squares = numbers.map(n => n * n);
console.log(squares); // 输出 [1, 4, 9]
五、默认参数
默认参数允许在函数定义时为参数指定默认值,简化了参数处理逻辑。
function greet(name = "Guest") {
console.log(`Hello, ${name}!`);
}
greet(); // 输出 "Hello, Guest!"
greet("Alice"); // 输出 "Hello, Alice!"
六、扩展运算符和剩余参数
扩展运算符(...
)和剩余参数提供了一种便捷的数组或对象展开和函数参数处理方式。
- 扩展运算符
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // 输出 [1, 2, 3, 4, 5]
- 剩余参数
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 输出 10
七、类和继承
ES6 引入了类的语法糖,使得面向对象编程更为直观。class
关键字定义了类,而 constructor
方法定义了类的构造函数。
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog("Rex");
dog.speak(); // 输出 "Rex barks."
八、模块化
ES6 模块系统提供了 import
和 export
关键字,用于模块的导入和导出。这使得代码的组织和重用更加方便。
- 导出
// math.js
export function add(x, y) {
return x + y;
}
- 导入
// main.js
import { add } from './math';
console.log(add(2, 3)); // 输出 5
九、Promise 和异步编程
Promise 是 ES6 引入的用于处理异步操作的对象,它代表一个未来可能完成或失败的操作及其结果。
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("数据获取成功");
}, 1000);
});
};
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
Promise 解决了回调地狱的问题,使得异步代码更加清晰和易于管理。
十、Symbol
Symbol
是一种新的原始数据类型,表示独一无二的值。它主要用于定义对象的私有属性,避免属性名冲突。
const sym1 = Symbol('description');
const sym2 = Symbol('description');
console.log(sym1 === sym2); // 输出 false
const obj = {
[sym1]: 'value1',
[sym2]: 'value2',
};
console.log(obj[sym1]); // 输出 'value1'
console.log(obj[sym2]); // 输出 'value2'
十一、Map 和 Set
ES6 引入了新的集合类型 Map
和 Set
,提供了更高效的数据存储和访问方式。
- Map: 一个键值对的集合,键和值可以是任意类型。
const map = new Map();
map.set('name', 'Alice');
map.set('age', 30);
console.log(map.get('name')); // 输出 'Alice'
console.log(map.has('age')); // 输出 true
- Set: 一个值的集合,集合中的值是唯一的。
const set = new Set([1, 2, 3, 3, 4]);
console.log(set); // 输出 Set { 1, 2, 3, 4 }
十二、WeakMap 和 WeakSet
WeakMap
和 WeakSet
是 Map
和 Set
的弱引用版本,它们的键是弱引用,不会阻止垃圾回收。这对于缓存和保存 DOM 元素等情况特别有用。
let obj = { key: 'value' };
const weakMap = new WeakMap();
weakMap.set(obj, 'some value');
// 当 obj 被置为 null 时,它所占用的内存可以被回收
obj = null;
十三、Generators 和 yield
生成器(Generators)是一个可以暂停和恢复的函数,通过 function*
关键字定义,使用 yield
表达式来暂停执行。
function* numbers() {
yield 1;
yield 2;
yield 3;
}
const gen = numbers();
console.log(gen.next().value); // 输出 1
console.log(gen.next().value); // 输出 2
console.log(gen.next().value); // 输出 3
生成器函数返回一个迭代器对象,每次调用 next()
会继续执行到下一个 yield
表达式,并返回相应的值。
十四、模块的动态导入
ES6 引入了静态模块导入(import
),但也有时候需要动态加载模块。这可以通过 import()
函数实现,它返回一个 Promise。
import('./module.js')
.then((module) => {
module.default();
})
.catch((error) => {
console.error('模块加载失败:', error);
});
十五、新的数字和字符串方法
ES6 增加了许多有用的数字和字符串处理方法:
数字方法:
Number.isNaN()
和Number.isFinite()
Number.isInteger()
Number.parseInt()
和Number.parseFloat()
字符串方法:
includes()
: 检查字符串是否包含某个子串。startsWith()
和endsWith()
: 检查字符串是否以某个子串开头或结尾。repeat()
: 重复字符串指定次数。
const str = 'Hello, world!';
console.log(str.includes('world')); // 输出 true
console.log(str.startsWith('Hello')); // 输出 true
console.log(str.endsWith('!')); // 输出 true
console.log(str.repeat(3)); // 输出 'Hello, world!Hello, world!Hello, world!'
十六、新的数组方法
ES6 为数组引入了多个新的方法,增强了数组操作的灵活性和便捷性:
-
Array.from()
: 将类数组对象或可迭代对象转换为数组。 -
Array.of()
: 根据提供的参数创建一个新的数组实例。 -
find()
和findIndex()
: 查找数组中满足条件的第一个元素或其索引。 -
fill()
: 用静态值填充数组的部分或全部元素。 -
entries()
,keys()
,values()
: 返回数组的迭代器对象。
const arr = [1, 2, 3, 4, 5];
console.log(Array.from('hello')); // 输出 ['h', 'e', 'l', 'l', 'o']
console.log(Array.of(1, 2, 3)); // 输出 [1, 2, 3]
console.log(arr.find(n => n > 3)); // 输出 4
console.log(arr.findIndex(n => n > 3)); // 输出 3
console.log(arr.fill(0, 2, 4)); // 输出 [1, 2, 0, 0, 5]
十七、Proxy 和 Reflect
Proxy
对象可以用来定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。Reflect
对象提供了与 Proxy
一致的方法,反映了语言内部操作的行为。
const handler = {
get: (target, prop) => {
return prop in target ? target[prop] : `Property ${prop} does not exist.`;
},
};
const person = { name: 'Alice' };
const proxyPerson = new Proxy(person, handler);
console.log(proxyPerson.name); // 输出 'Alice'
console.log(proxyPerson.age); // 输出 'Property age does not exist.'
十八、Promise.all
和 Promise.race
ES6 中的 Promise
对象不仅提供了处理单一异步操作的方法,还引入了 Promise.all
和 Promise.race
用于处理多个 Promise 的集合。
Promise.all
: 等待所有 Promise 都完成,并返回一个包含所有结果的数组。如果有任何一个 Promise 被拒绝,则返回拒绝的那个原因。
Promise.all([promise1, promise2, promise3])
.then(values => console.log(values))
.catch(error => console.error(error));
Promise.race
: 返回第一个完成的 Promise 的结果,无论是成功还是失败。
Promise.race([promise1, promise2, promise3])
.then(value => console.log(value))
.catch(error => console.error(error));