02-解构赋值与扩展运算符
分类:02-ES6+现代特性
发布于:
阅读时间:85 分钟
解构赋值与扩展运算符
📋 学习目标
- 掌握数组和对象的解构赋值语法
- 学会使用扩展运算符进行数组和对象操作
- 理解函数参数解构和返回值解构
- 掌握高级解构技巧和实际应用场景
- 了解解构赋值的性能影响和最佳实践
🎯 数组解构赋值
1. 基础数组解构
// 基础解构
const fruits = ['apple', 'banana', 'orange', 'grape'];
const [first, second, third] = fruits;
console.log(first, second, third); // 'apple', 'banana', 'orange'
// 跳过某些元素
const [, , thirdFruit] = fruits;
console.log(thirdFruit); // 'orange'
// 使用剩余元素
const [head, ...tail] = fruits;
console.log(head); // 'apple'
console.log(tail); // ['banana', 'orange', 'grape']
// 默认值
const colors = ['red'];
const [primary = 'black', secondary = 'white'] = colors;
console.log(primary, secondary); // 'red', 'white'
// 解构不存在的位置
const [a, b, c, d, e = 'default'] = ['x', 'y', 'z'];
console.log(a, b, c, d, e); // 'x', 'y', 'z', undefined, 'default'
// 交换变量
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y); // 2, 1
2. 嵌套数组解构
// 嵌套数组解构
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
const [[a, b, c], [d, e, f], [g, h, i]] = matrix;
console.log(a, b, c, d, e, f, g, h, i); // 1, 2, 3, 4, 5, 6, 7, 8, 9
// 部分解构
const [, [, secondRow]] = matrix;
const [firstInSecondRow] = secondRow;
console.log(firstInSecondRow); // 4
// 使用默认值处理不规则数组
const irregularArray = [1, [2, 3], 4];
const [num1, [num2, num3], num4, num5 = 5] = irregularArray;
console.log(num1, num2, num3, num4, num5); // 1, 2, 3, 4, 5
3. 函数返回值解构
// 函数返回数组
function getUser() {
return ['Alice', 25, 'Developer'];
}
const [name, age, profession] = getUser();
console.log(name, age, profession); // 'Alice', 25, 'Developer'
// 函数返回多个值
function getMinMax(numbers) {
return [Math.min(...numbers), Math.max(...numbers)];
}
const [min, max] = getMinMax([5, 2, 8, 1, 9]);
console.log(min, max); // 1, 9
// 解构函数参数
function processUser([name, age, city]) {
console.log(`${name} is ${age} years old and lives in ${city}`);
}
processUser(['Bob', 30, 'New York']); // Bob is 30 years old and lives in New York
🏗️ 对象解构赋值
1. 基础对象解构
// 基础对象解构
const person = {
name: 'Alice',
age: 25,
city: 'New York'
};
const { name, age, city } = person;
console.log(name, age, city); // 'Alice', 25, 'New York'
// 重命名变量
const { name: userName, age: userAge } = person;
console.log(userName, userAge); // 'Alice', 25
// 默认值
const user = {
name: 'Bob',
age: 30
};
const { name: name2, age: age2, city: city2 = 'Unknown' } = user;
console.log(name2, age2, city2); // 'Bob', 30, 'Unknown'
// 解构嵌套对象
const user2 = {
personal: {
name: 'Charlie',
age: 35
},
contact: {
email: 'charlie@example.com',
phone: '123-456-7890'
}
};
const {
personal: { name: name3 },
contact: { email }
} = user2;
console.log(name3, email); // 'Charlie', 'charlie@example.com'
2. 对象属性解构
// 解构对象属性
const settings = {
theme: 'dark',
language: 'en',
notifications: true,
autoSave: false
};
// 只解构需要的属性
const { theme, language } = settings;
console.log(theme, language); // 'dark', 'en'
// 解构到新变量名
const { theme: currentTheme, language: currentLang } = settings;
console.log(currentTheme, currentLang); // 'dark', 'en'
// 解构并设置默认值
const { autoSave = true, fontSize = 14 } = settings;
console.log(autoSave, fontSize); // false, 14
// 解构剩余属性
const { theme, ...otherSettings } = settings;
console.log(theme); // 'dark'
console.log(otherSettings); // { language: 'en', notifications: true, autoSave: false }
3. 动态属性名解构
// 动态属性名解构
const person3 = {
firstName: 'David',
lastName: 'Johnson',
age: 28
};
const propName = 'firstName';
const { [propName]: firstName } = person3;
console.log(firstName); // 'David'
// 计算属性名
const user3 = {
'user-name': 'Eve Wilson',
'user-age': 32
};
const { ['user-name']: userName, ['user-age']: userAge } = user3;
console.log(userName, userAge); // 'Eve Wilson', 32
// 从Map解构
const userMap = new Map([
['name', 'Frank'],
['age', 40]
]);
const { 0: nameKey, 1: ageKey } = [...userMap.entries()];
console.log(nameKey, ageKey); // 'name', 'age'
🔧 函数参数解构
1. 对象参数解构
// 函数参数解构
function displayUser({ name, age, city = 'Unknown' }) {
console.log(`${name} is ${age} years old and lives in ${city}`);
}
displayUser({ name: 'Alice', age: 25, city: 'Boston' });
// Alice is 25 years old and lives in Boston
displayUser({ name: 'Bob', age: 30 });
// Bob is 30 years old and lives in Unknown
// 可选参数解构
function processConfig({
timeout = 1000,
retries = 3,
endpoint = '/api'
} = {}) {
console.log(`Config: timeout=${timeout}, retries=${retries}, endpoint=${endpoint}`);
}
processConfig(); // Config: timeout=1000, retries=3, endpoint=/api
processConfig({ timeout: 2000 }); // Config: timeout=2000, retries=3, endpoint=/api
// 嵌套对象参数解构
function printAddress({
person: { name },
location: { street, city, country }
}) {
console.log(`${name} lives at ${street}, ${city}, ${country}`);
}
printAddress({
person: { name: 'Grace' },
location: {
street: '123 Main St',
city: 'Seattle',
country: 'USA'
}
});
// Grace lives at 123 Main St, Seattle, USA
2. 数组参数解构
// 数组参数解构
function calculateSum([a, b, c]) {
return a + b + c;
}
console.log(calculateSum([1, 2, 3])); // 6
// 使用默认值
function getCoordinates([x = 0, y = 0] = []) {
return { x, y };
}
console.log(getCoordinates([5, 10])); // { x: 5, y: 10 }
console.log(getCoordinates([])); // { x: 0, y: 0 }
console.log(getCoordinates()); // { x: 0, y: 0 }
// 混合解构
function processData([id, { name, age }, options = {}]) {
console.log(`Processing ${name} (ID: ${id}, Age: ${age})`);
console.log('Options:', options);
}
processData([
1,
{ name: 'Henry', age: 45 },
{ priority: 'high' }
]);
// Processing Henry (ID: 1, Age: 45)
// Options: { priority: 'high' }
3. REST参数与解构结合
// 解构第一个参数,其余作为数组
function logFirstAndRest([first, ...rest]) {
console.log('First:', first);
console.log('Rest:', rest);
}
logFirstAndRest([1, 2, 3, 4, 5]);
// First: 1
// Rest: [2, 3, 4, 5]
// 解构前几个参数,其余作为对象
function extractRequired({ required1, required2, ...rest }) {
console.log('Required:', required1, required2);
console.log('Rest:', rest);
}
extractRequired({
required1: 'A',
required2: 'B',
optional1: 'C',
optional2: 'D'
});
// Required: A B
// Rest: { optional1: 'C', optional2: 'D' }
🚀 扩展运算符
1. 数组扩展运算符
// 数组合并
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
// 在数组中插入元素
const original = [2, 3, 4];
const withExtension = [1, ...original, 5];
console.log(withExtension); // [1, 2, 3, 4, 5]
// 数组复制
const numbers = [1, 2, 3];
const copy = [...numbers];
console.log(copy); // [1, 2, 3]
console.log(copy === numbers); // false (不同的数组)
// 类数组对象转数组
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const realArray = [...arrayLike];
console.log(realArray); // ['a', 'b', 'c']
// 字符串转数组
const str = 'hello';
const charArray = [...str];
console.log(charArray); // ['h', 'e', 'l', 'l', 'o']
// Set转数组
const set = new Set([1, 2, 3, 3, 4]);
const setArray = [...set];
console.log(setArray); // [1, 2, 3, 4]
2. 对象扩展运算符
// 对象合并
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = { ...obj1, ...obj2 };
console.log(merged); // { a: 1, b: 3, c: 4 }
// 添加属性
const base = { x: 1, y: 2 };
const extended = { ...base, z: 3 };
console.log(extended); // { x: 1, y: 2, z: 3 }
// 覆盖属性
const original = { name: 'Alice', age: 25 };
const updated = { ...original, age: 26 };
console.log(updated); // { name: 'Alice', age: 26 }
// 对象复制
const user = { name: 'Bob', age: 30 };
const userCopy = { ...user };
console.log(userCopy); // { name: 'Bob', age: 30 }
console.log(userCopy === user); // false (不同的对象)
// 合并多个对象
const defaults = { theme: 'light', language: 'en' };
const userSettings = { theme: 'dark' };
const finalSettings = { ...defaults, ...userSettings };
console.log(finalSettings); // { theme: 'dark', language: 'en' }
3. 函数调用中的扩展运算符
// 函数参数展开
function add(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(add(...numbers)); // 6
// Math.max应用
const maxNumber = Math.max(...[10, 20, 30, 40, 50]);
console.log(maxNumber); // 50
// 数组去重
const uniqueArray = [...new Set([1, 2, 2, 3, 4, 4, 5])];
console.log(uniqueArray); // [1, 2, 3, 4, 5]
// 数组扁平化
const nested = [1, [2, 3], [4, [5, 6]]];
const flat = [].concat(...nested);
console.log(flat); // [1, 2, 3, 4, [5, 6]]
🎨 高级解构技巧
1. 复杂嵌套解构
// 复杂嵌套对象解构
const data = {
users: [
{
id: 1,
name: 'Alice',
contact: {
email: 'alice@example.com',
phones: {
mobile: '123-456-7890',
home: '098-765-4321'
}
}
},
{
id: 2,
name: 'Bob',
contact: {
email: 'bob@example.com',
phones: {
mobile: '555-123-4567'
}
}
}
]
};
// 解构第一个用户的手机号
const {
users: [
{
contact: {
phones: { mobile: firstUserMobile }
}
}
]
} = data;
console.log(firstUserMobile); // '123-456-7890'
// 解构所有用户的邮箱
const {
users: [
{ contact: { email: email1 } },
{ contact: { email: email2 } }
]
} = data;
console.log(email1, email2); // 'alice@example.com', 'bob@example.com'
2. 解构与默认值结合
// 深度嵌套解构与默认值
const response = {
data: {
user: {
name: 'Charlie'
}
}
};
const {
data: {
user: { name },
profile: { age = 25, city = 'Unknown' } = {}
}
} = response;
console.log(name); // 'Charlie'
console.log(age); // 25
console.log(city); // 'Unknown'
// 函数参数的复杂解构
function processApiResponse({
data: {
items = [],
total = 0
} = {},
status = 'unknown'
} = {}) {
console.log(`Status: ${status}`);
console.log(`Items: ${items.length}, Total: ${total}`);
}
processApiResponse({
data: {
items: [{ id: 1 }, { id: 2 }],
total: 2
},
status: 'success'
});
// Status: success
// Items: 2, Total: 2
3. 解构在算法中的应用
// 快速排序中的解构
function quickSort([pivot, ...rest]) {
if (rest.length === 0) return [pivot];
const less = rest.filter(x => x <= pivot);
const greater = rest.filter(x => x > pivot);
return [...quickSort(less), pivot, ...quickSort(greater)];
}
const unsorted = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
const sorted = quickSort(unsorted);
console.log(sorted); // [1, 1, 2, 3, 3, 3, 4, 5, 5, 5, 6, 9]
// 数组交换元素
function swap([a, b, ...rest]) {
return [b, a, ...rest];
}
console.log(swap([1, 2, 3, 4, 5])); // [2, 1, 3, 4, 5]
🔄 解构与扩展运算符结合使用
1. 数组操作
// 数组首尾操作
const arr = [2, 3, 4, 5];
// 在开头添加元素
const newStart = [1, ...arr];
console.log(newStart); // [1, 2, 3, 4, 5]
// 在结尾添加元素
const newEnd = [...arr, 6];
console.log(newEnd); // [2, 3, 4, 5, 6]
// 替换中间元素
const [first, , ...middle] = arr;
const replaced = [first, 99, ...middle];
console.log(replaced); // [2, 99, 4, 5]
// 数组旋转
const rotateLeft = ([first, ...rest]) => [...rest, first];
const rotateRight = (arr) => {
const [last, ...rest] = [...arr].reverse();
return [last, ...rest.reverse()];
};
console.log(rotateLeft([1, 2, 3, 4, 5])); // [2, 3, 4, 5, 1]
console.log(rotateRight([1, 2, 3, 4, 5])); // [5, 1, 2, 3, 4]
2. 对象操作
// 选择性更新对象
const user = { name: 'Alice', age: 25, city: 'New York' };
const updateUser = { ...user, age: 26 };
console.log(updateUser); // { name: 'Alice', age: 26, city: 'New York' }
// 选择性删除属性(通过解构和重组)
const { city, ...userWithoutCity } = user;
console.log(userWithoutCity); // { name: 'Alice', age: 25 }
// 对象属性重命名
const { name: fullName, ...rest } = user;
const renamedUser = { fullName, ...rest };
console.log(renamedUser); // { fullName: 'Alice', age: 25, city: 'New York' }
// 合并配置
const defaultConfig = {
theme: 'light',
language: 'en',
autoSave: true,
timeout: 5000
};
const userConfig = {
theme: 'dark',
timeout: 10000
};
const finalConfig = { ...defaultConfig, ...userConfig };
console.log(finalConfig);
// { theme: 'dark', language: 'en', autoSave: true, timeout: 10000 }
3. 函数式编程应用
// 函数组合
const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x);
const addOne = x => x + 1;
const double = x => x * 2;
const toString = x => x.toString();
const processNumber = compose(toString, double, addOne);
console.log(processNumber(5)); // '12'
// 管道函数
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);
const pipeline = pipe(
x => x + 1,
x => x * 2,
x => x - 3
);
console.log(pipeline(5)); // 9
// 使用解构和扩展运算符的数据转换
const transformUsers = users =>
users.map(({ name, age, ...rest }) => ({
fullName: name,
isAdult: age >= 18,
...rest
}));
const sampleUsers = [
{ name: 'Alice', age: 25, city: 'NYC' },
{ name: 'Bob', age: 17, city: 'LA' }
];
console.log(transformUsers(sampleUsers));
// [
// { fullName: 'Alice', isAdult: true, city: 'NYC' },
// { fullName: 'Bob', isAdult: false, city: 'LA' }
// ]
⚠️ 常见陷阱
1. 解构不存在的属性
// ❌ 错误:解构不存在的嵌套属性
const user = { name: 'Alice' };
// const { profile: { age } } = user; // TypeError: Cannot destructure property 'profile' of undefined
// ✅ 解决方案:使用默认值
const { profile: { age = 25 } = {} } = user;
console.log(age); // 25
// 或者先检查属性是否存在
const userProfile = user.profile || {};
const { age: userAge = 25 } = userProfile;
console.log(userAge); // 25
2. 数组越界解构
// ❌ 可能的问题:数组长度不足
const [a, b, c, d] = [1, 2];
console.log(d); // undefined
// ✅ 解决方案:使用默认值
const [a2, b2, c2, d2 = 0] = [1, 2];
console.log(d2); // 0
// 或者检查数组长度
const arr = [1, 2];
const [first, second, third] = arr.length >= 3 ? arr : [1, 2, 0];
console.log(third); // 0
3. 扩展运算符的性能问题
// ❌ 性能问题:在大型数组上使用扩展运算符
const largeArray = new Array(100000).fill(0);
const copied = [...largeArray]; // 可能很慢
// ✅ 更好的方法:使用slice
const copiedBetter = largeArray.slice();
// ❌ 对象深度克隆的性能问题
const deepObj = { /* 很深的对象 */ };
const cloned = { ...deepObj }; // 浅拷贝,不会复制嵌套对象
// ✅ 深度克隆函数
function deepClone(obj) {
if (obj =<mark> null || typeof obj !</mark> 'object') return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof Array) return obj.map(item => deepClone(item));
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
4. this指向问题
// ❌ 问题:解构中的this指向
const obj = {
data: { name: 'Alice', age: 25 },
// 使用普通函数,this指向有问题
getData() {
const { name, age } = this.data;
return function() {
console.log(this.name, this.age); // undefined (this指向全局或undefined)
};
},
// 使用箭头函数,this指向正确
getArrowData() {
const { name, age } = this.data;
return () => {
console.log(name, age); // 正确访问外层变量
};
}
};
const regularFunc = obj.getData();
regularFunc(); // undefined, undefined
const arrowFunc = obj.getArrowData();
arrowFunc(); // 'Alice', 25
📝 最佳实践
1. 使用默认值
// ✅ 总是提供默认值
function processUser({ name = 'Anonymous', age = 0, city = 'Unknown' } = {}) {
return { name, age, city };
}
// ✅ 嵌套解构时也提供默认值
function processNested({
profile = {},
contact: { email = '' } = {}
} = {}) {
return { profile, contact };
}
2. 解构重命名
// ✅ 使用有意义的变量名
const {
'user-name': userName,
'user-age': userAge
} = userObject;
// ✅ 避免命名冲突
const {
name: userName,
age: userAge
} = user;
3. 性能考虑
// ✅ 避免不必要的扩展运算符
// 不要这样:
const arr = [...array1, ...array2, ...array3];
// 更好的方式:
const arr = array1.concat(array2, array3);
// ✅ 浅拷贝时优先使用Object.assign
const shallowCopy = Object.assign({}, sourceObj);
// 深拷贝时使用专门的函数
const deepCopy = JSON.parse(JSON.stringify(sourceObj));
4. 错误处理
// ✅ 解构时进行错误处理
function safeDestructure(obj) {
try {
const { required, optional = 'default' } = obj;
return { required, optional };
} catch (error) {
console.error('Destructuring error:', error);
return { required: null, optional: null };
}
}
🎯 小结
- 掌握了数组和对象的解构赋值语法
- 学会了使用扩展运算符进行数组和对象操作
- 理解了函数参数解构和返回值解构
- 掌握了高级解构技巧和实际应用场景
- 了解了常见的解构陷阱和性能影响
- 学会了解构赋值的最佳实践
解构赋值和扩展运算符是ES6+的重要特性,它们让代码更简洁、更易读,是现代JavaScript开发中不可或缺的工具。
下一步学习:Symbol类型与SetMap