Appearance
ES6+新特性
ES6(ECMAScript 2015)及后续版本引入了许多新特性,大大增强了 JavaScript 的功能。本章将介绍常用的 ES6+ 新特性。
let 和 const
let 块级作用域
javascript
// let 声明的变量具有块级作用域
if (true) {
let name = '张三';
console.log(name); // 张三
}
// console.log(name); // 错误:name 未定义
// var 是函数作用域
if (true) {
var age = 25;
}
console.log(age); // 25
// let 不会变量提升
// console.log(x); // 错误:Cannot access 'x' before initialization
let x = 10;
// var 会变量提升
console.log(y); // undefined
var y = 20;
// let 不允许重复声明
let a = 1;
// let a = 2; // 错误:不能重复声明
// 暂时性死区(Temporal Dead Zone)
let temp = 'global';
function test() {
// 暂时性死区开始
// console.log(temp); // 错误
let temp = 'local'; // 暂时性死区结束
console.log(temp);
}
test();const 常量
javascript
// const 声明的变量必须初始化
const PI = 3.14159;
// const x; // 错误:缺少初始化
// const 声明的变量不能重新赋值
const name = '张三';
// name = '李四'; // 错误:不能重新赋值
// const 也是块级作用域
if (true) {
const MAX = 100;
}
// console.log(MAX); // 错误:未定义
// const 对象的属性可以修改
const person = {
name: '张三'
};
person.name = '李四'; // 允许
person.age = 25; // 允许添加新属性
// person = {}; // 错误:不能重新赋值
// const 数组的元素可以修改
const arr = [1, 2, 3];
arr.push(4); // 允许
arr[0] = 10; // 允许
// arr = []; // 错误
// 冻结对象使其完全不可变
const frozen = Object.freeze({ name: '张三' });
// frozen.name = '李四'; // 无效(严格模式下报错)箭头函数
基本语法
javascript
// 传统函数
function add(a, b) {
return a + b;
}
// 箭头函数
const add = (a, b) => {
return a + b;
};
// 简写:单个表达式可省略花括号和 return
const add = (a, b) => a + b;
// 单个参数可省略括号
const double = x => x * 2;
// 无参数需要空括号
const sayHi = () => console.log('Hi!');
// 返回对象需要用括号包裹
const createUser = (name, age) => ({ name, age });
// 多行函数体需要花括号
const calculate = (a, b) => {
const sum = a + b;
const product = a * b;
return { sum, product };
};this 绑定
javascript
// 箭头函数没有自己的 this,继承外层作用域的 this
const person = {
name: '张三',
// 传统函数:this 指向调用者
greet: function() {
console.log('你好,' + this.name);
},
// 箭头函数:this 继承自外层(通常是 window)
greetArrow: () => {
console.log('你好,' + this.name); // this.name 是 undefined
},
// 正确用法:在方法内部使用箭头函数
greetLater: function() {
setTimeout(() => {
console.log('你好,' + this.name); // this 指向 person
}, 1000);
}
};
person.greet(); // 你好,张三
person.greetArrow(); // 你好,undefined
person.greetLater(); // 你好,张三
// 箭头函数不能用作构造函数
// const Person = (name) => { this.name = name; };
// new Person('张三'); // 错误
// 箭头函数没有 arguments 对象
const func = () => {
// console.log(arguments); // 错误
};解构赋值
数组解构
javascript
// 基本用法
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3
// 跳过元素
const [first, , third] = [1, 2, 3];
console.log(first, third); // 1 3
// 默认值
const [x, y, z = 10] = [1, 2];
console.log(x, y, z); // 1 2 10
// 剩余元素
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]
// 交换变量
let m = 1, n = 2;
[m, n] = [n, m];
console.log(m, n); // 2 1
// 嵌套解构
const [[a1, a2], [b1, b2]] = [[1, 2], [3, 4]];
console.log(a1, a2, b1, b2); // 1 2 3 4对象解构
javascript
// 基本用法
const { name, age } = { name: '张三', age: 25 };
console.log(name, age); // 张三 25
// 重命名变量
const { name: userName, age: userAge } = { name: '张三', age: 25 };
console.log(userName, userAge); // 张三 25
// 默认值
const { name, city = '北京' } = { name: '张三' };
console.log(city); // 北京
// 剩余属性
const { a, ...rest } = { a: 1, b: 2, c: 3 };
console.log(a); // 1
console.log(rest); // { b: 2, c: 3 }
// 嵌套解构
const person = {
name: '张三',
address: {
city: '北京',
district: '朝阳'
}
};
const { address: { city } } = person;
console.log(city); // 北京
// 解构函数参数
function greet({ name, age }) {
console.log(`${name}, ${age}岁`);
}
greet({ name: '张三', age: 25 });展开运算符
数组展开
javascript
// 展开数组
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // [1, 2, 3, 4, 5]
// 复制数组
const copy = [...arr1];
console.log(copy); // [1, 2, 3]
// 合并数组
const arr3 = [1, 2];
const arr4 = [3, 4];
const merged = [...arr3, ...arr4];
console.log(merged); // [1, 2, 3, 4]
// 将字符串转为数组
const chars = [...'hello'];
console.log(chars); // ['h', 'e', 'l', 'l', 'o']
// 将 NodeList 转为数组
// const divs = [...document.querySelectorAll('div')];
// Math.max 使用展开
const numbers = [1, 5, 3, 9, 2];
console.log(Math.max(...numbers)); // 9对象展开
javascript
// 展开对象
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };
console.log(obj2); // { a: 1, b: 2, c: 3 }
// 复制对象(浅拷贝)
const copy = { ...obj1 };
// 合并对象(后面的属性会覆盖前面的)
const obj3 = { a: 1, b: 2 };
const obj4 = { b: 3, c: 4 };
const merged = { ...obj3, ...obj4 };
console.log(merged); // { a: 1, b: 3, c: 4 }
// 修改对象属性
const person = { name: '张三', age: 25 };
const updated = { ...person, age: 26 };
console.log(updated); // { name: '张三', age: 26 }模板字符串
javascript
// 基本用法
const name = '张三';
const greeting = `你好,${name}!`;
console.log(greeting); // 你好,张三!
// 多行字符串
const html = `
<div class="container">
<h1>标题</h1>
<p>内容</p>
</div>
`;
// 表达式计算
const a = 10;
const b = 20;
console.log(`${a} + ${b} = ${a + b}`); // 10 + 20 = 30
// 调用函数
function toUpper(str) {
return str.toUpperCase();
}
console.log(`Hello ${toUpper('world')}!`); // Hello WORLD!
// 标签模板
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] ? `<strong>${values[i]}</strong>` : '';
return result + str + value;
}, '');
}
const result = highlight`你好,${name}!欢迎来到${'北京'}`;
console.log(result); // 你好,<strong>张三</strong>!欢迎来到<strong>北京</strong>
// 原始字符串
console.log(String.raw`Hello\nWorld`); // Hello\nWorld(不转义)默认参数
javascript
// 函数默认参数
function greet(name = '游客', greeting = '你好') {
console.log(`${greeting},${name}!`);
}
greet(); // 你好,游客!
greet('张三'); // 你好,张三!
greet('张三', '欢迎'); // 欢迎,张三!
// 默认值可以是表达式
function add(a, b = a * 2) {
return a + b;
}
console.log(add(5)); // 15
// 默认值可以是函数调用
function getDefault() {
return '默认值';
}
function test(value = getDefault()) {
console.log(value);
}
test(); // 默认值
test('自定义'); // 自定义
// 解构参数默认值
function createUser({ name = '匿名', age = 0 } = {}) {
return { name, age };
}
console.log(createUser()); // { name: '匿名', age: 0 }
console.log(createUser({ name: '张三' })); // { name: '张三', age: 0 }剩余参数
javascript
// 剩余参数收集所有剩余参数
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// 与普通参数结合
function greetAll(greeting, ...names) {
names.forEach(name => {
console.log(`${greeting},${name}!`);
});
}
greetAll('你好', '张三', '李四', '王五');
// 剩余参数是真正的数组
function logArgs(...args) {
console.log(Array.isArray(args)); // true
console.log(args.length);
}
// 剩余参数必须是最后一个参数
// function wrong(...rest, last) {} // 错误
// 解构中的剩余元素
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [2, 3, 4, 5]
const { a, ...others } = { a: 1, b: 2, c: 3 };
console.log(a); // 1
console.log(others); // { b: 2, c: 3 }对象字面量增强
javascript
// 属性简写
const name = '张三';
const age = 25;
// ES5
const person1 = {
name: name,
age: age
};
// ES6 简写
const person2 = { name, age };
// 方法简写
const person3 = {
name: '张三',
// ES5
greet: function() {
console.log('你好');
},
// ES6 简写
greet2() {
console.log('你好');
}
};
// 计算属性名
const key = 'name';
const obj = {
[key]: '张三',
['full' + 'Name']: '张三三',
['get' + 'Age']() {
return 25;
}
};
console.log(obj.name); // 张三
console.log(obj.fullName); // 张三三
console.log(obj.getAge()); // 25类(Class)
基本语法
javascript
// ES5 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log('你好,我是' + this.name);
};
// ES6 类
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
greet() {
console.log(`你好,我是${this.name}`);
}
// getter
get info() {
return `${this.name} (${this.age}岁)`;
}
// setter
set info(value) {
const [name, age] = value.split(',');
this.name = name;
this.age = parseInt(age);
}
// 静态方法
static create(name, age) {
return new Person(name, age);
}
// 静态属性
static species = '人类';
}
const person = new Person('张三', 25);
person.greet(); // 你好,我是张三
console.log(person.info); // 张三 (25岁)
// 静态方法调用
const person2 = Person.create('李四', 30);
console.log(Person.species); // 人类继承
javascript
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name}发出声音`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
// 重写方法
speak() {
console.log(`${this.name}汪汪叫`);
}
// 新方法
fetch() {
console.log(`${this.name}去捡球`);
}
}
const dog = new Dog('小黄', '金毛');
dog.speak(); // 小黄汪汪叫
dog.fetch(); // 小黄去捡球
// 检查继承关系
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true私有属性(ES2022)
javascript
class Person {
// 私有属性(以 # 开头)
#name;
#age;
constructor(name, age) {
this.#name = name;
this.#age = age;
}
// 公有方法访问私有属性
getName() {
return this.#name;
}
// 私有方法
#validateAge(age) {
return age > 0 && age < 150;
}
setAge(age) {
if (this.#validateAge(age)) {
this.#age = age;
}
}
}
const person = new Person('张三', 25);
console.log(person.getName()); // 张三
// console.log(person.#name); // 错误:私有属性无法访问模块化
导出(export)
javascript
// module.js
// 命名导出
export const name = '张三';
export const age = 25;
export function greet() {
console.log('你好');
}
export class Person {
constructor(name) {
this.name = name;
}
}
// 统一导出
const a = 1;
const b = 2;
function add(x, y) {
return x + y;
}
export { a, b, add };
// 重命名导出
export { add as sum };
// 默认导出(每个模块只能有一个)
export default function() {
console.log('默认导出');
}
// 或
const obj = { name: '张三' };
export default obj;导入(import)
javascript
// 导入命名导出
import { name, age, greet } from './module.js';
// 重命名导入
import { add as sum } from './module.js';
// 导入所有
import * as module from './module.js';
console.log(module.name);
module.greet();
// 导入默认导出
import myFunc from './module.js';
myFunc();
// 混合导入
import myFunc, { name, age } from './module.js';
// 动态导入
async function loadModule() {
const module = await import('./module.js');
module.greet();
}
// 仅执行模块(不导入)
import './module.js';Promise
基本用法
javascript
// 创建 Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve('成功');
} else {
reject('失败');
}
}, 1000);
});
// 使用 Promise
promise
.then(result => {
console.log(result); // 成功
return '处理后的结果';
})
.then(result => {
console.log(result); // 处理后的结果
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log('完成');
});
// Promise 状态
// pending:进行中
// fulfilled:已成功
// rejected:已失败Promise 静态方法
javascript
// Promise.resolve()
const p1 = Promise.resolve('成功');
p1.then(result => console.log(result));
// Promise.reject()
const p2 = Promise.reject('失败');
p2.catch(error => console.log(error));
// Promise.all():所有都成功才成功
const p3 = Promise.resolve(1);
const p4 = Promise.resolve(2);
const p5 = Promise.resolve(3);
Promise.all([p3, p4, p5])
.then(results => console.log(results)) // [1, 2, 3]
.catch(error => console.log(error));
// Promise.race():第一个完成的结果
Promise.race([p3, p4, p5])
.then(result => console.log(result)); // 1
// Promise.allSettled():等待所有完成
Promise.allSettled([p3, Promise.reject('失败')])
.then(results => console.log(results));
// [{ status: 'fulfilled', value: 1 }, { status: 'rejected', reason: '失败' }]
// Promise.any():任意一个成功就成功
Promise.any([Promise.reject('失败'), p3])
.then(result => console.log(result)); // 1async/await
基本用法
javascript
// async 函数返回 Promise
async function greet() {
return '你好';
}
greet().then(result => console.log(result)); // 你好
// await 等待 Promise 完成
async function getData() {
const result = await fetch('/api/data');
const data = await result.json();
return data;
}
// 错误处理
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.log('错误:', error);
throw error;
}
}
// 并行执行
async function fetchAll() {
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json())
]);
return { users, posts };
}Map 和 Set
Map
javascript
// 创建 Map
const map = new Map();
// 设置值
map.set('name', '张三');
map.set('age', 25);
map.set(1, '数字键');
// 获取值
console.log(map.get('name')); // 张三
// 检查键是否存在
console.log(map.has('name')); // true
// 删除
map.delete('age');
// 大小
console.log(map.size);
// 清空
// map.clear();
// 遍历
map.forEach((value, key) => {
console.log(key, value);
});
for (let [key, value] of map) {
console.log(key, value);
}
// 从数组创建
const map2 = new Map([
['name', '张三'],
['age', 25]
]);
// 对象转 Map
const obj = { name: '张三', age: 25 };
const map3 = new Map(Object.entries(obj));
// Map 转对象
const obj2 = Object.fromEntries(map3);Set
javascript
// 创建 Set
const set = new Set();
// 添加值
set.add(1);
set.add(2);
set.add(2); // 重复值不会被添加
console.log(set.size); // 2
// 检查是否存在
console.log(set.has(1)); // true
// 删除
set.delete(1);
// 清空
// set.clear();
// 遍历
set.forEach(value => console.log(value));
for (let value of set) {
console.log(value);
}
// 数组去重
const arr = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3]
// 从数组创建
const set2 = new Set([1, 2, 3, 3]);小结
本章学习了 ES6+ 的常用新特性:
- let 和 const:块级作用域、常量
- 箭头函数:简洁语法、this 绑定
- 解构赋值:数组解构、对象解构
- 展开运算符:数组展开、对象展开
- 模板字符串:字符串插值、多行字符串
- 默认参数:函数参数默认值
- 剩余参数:收集剩余参数
- 类:class 语法、继承、私有属性
- 模块化:export、import
- Promise:异步编程
- async/await:同步风格的异步代码
- Map 和 Set:新的数据结构
下一章我们将学习 异步编程,深入了解 JavaScript 的异步机制。
