
扩展运算符(Spread Operator)是ES6中引入的强大特性,它使用三个点(...)语法,可以极大地简化数组、对象和函数参数的操作。今天我通过完整可运行的示例,给大家深入解析扩展运算符的各种应用场景。
扩展运算符的基本功能是"展开"可迭代对象(如数组、字符串)或对象字面量,将它们的元素或属性展开到新的上下文环境中。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扩展运算符基础示例</title>
</head>
<body>
<h2>扩展运算符基础示例</h2>
<div id="output"></div>
<script>
// 获取输出容器
const output = document.getElementById('output');
// 1. 展开数组
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
output.innerHTML += `<p>数组展开: [${arr1}] + [${arr2}] = [${combined}]</p>`;
// 2. 展开字符串
const str = "Hello";
const chars = [...str];
output.innerHTML += `<p>字符串展开: "${str}" → [${chars}]</p>`;
// 3. 复制数组(浅拷贝)
const original = [10, 20, 30];
const copy = [...original];
original.push(40); // 修改原数组
output.innerHTML += `<p>数组复制: 原数组 [${original}],复制数组 [${copy}]</p>`;
// 4. 函数参数传递
function sum(a, b, c) {
return a + b + c;
}
const numbers = [5, 10, 15];
const result = sum(...numbers);
output.innerHTML += `<p>函数参数传递: sum(...[5, 10, 15]) = ${result}</p>`;
</script>
</body>
</html>运行效果:

说明:
扩展运算符可以展开数组、字符串等可迭代对象
使用扩展运算符复制数组是浅拷贝,对于简单数据类型是安全的
扩展运算符可以将数组元素展开作为函数参数
扩展运算符极大地简化了数组的各种操作,让代码更加简洁易读。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扩展运算符在数组操作中的应用</title>
</head>
<body>
<h2>扩展运算符在数组操作中的应用</h2>
<div id="output"></div>
<script>
const output = document.getElementById('output');
// 1. 合并多个数组
const arr1 = ['a', 'b'];
const arr2 = ['c', 'd'];
const arr3 = ['e', 'f'];
const merged = [...arr1, ...arr2, ...arr3];
output.innerHTML += `<p>数组合并: ${JSON.stringify(merged)}</p>`;
// 2. 在特定位置插入元素
const originalArray = [1, 2, 5, 6];
const elementsToInsert = [3, 4];
// 在索引2的位置插入[3, 4]
const insertedArray = [
...originalArray.slice(0, 2),
...elementsToInsert,
...originalArray.slice(2)
];
output.innerHTML += `<p>数组插入: ${JSON.stringify(insertedArray)}</p>`;
// 3. 创建数组副本(避免引用问题)
const sourceArray = [{id: 1}, {id: 2}, {id: 3}];
const shallowCopy = [...sourceArray];
// 修改原数组中的对象
sourceArray[0].id = 100;
output.innerHTML += `<p>数组浅拷贝: 原数组[0].id = ${sourceArray[0].id},复制数组[0].id = ${shallowCopy[0].id}</p>`;
output.innerHTML += `<p>说明:扩展运算符执行的是浅拷贝,对象属性仍然共享引用</p>`;
// 4. 将类数组对象转换为数组
function getArguments() {
// arguments是类数组对象
return [...arguments];
}
const argsArray = getArguments(10, 20, 30, 40);
output.innerHTML += `<p>类数组转换: ${JSON.stringify(argsArray)} (类型: ${typeof argsArray})</p>`;
// 5. 数组去重
const duplicateArray = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [...new Set(duplicateArray)];
output.innerHTML += `<p>数组去重: [${duplicateArray}] → [${uniqueArray}]</p>`;
</script>
</body>
</html>运行效果:

说明:
扩展运算符可以方便地合并多个数组
可以配合slice方法在数组的任意位置插入元素
扩展运算符创建的是浅拷贝,对于嵌套对象需要额外处理
可以将arguments、NodeList等类数组对象转换为真正的数组
结合Set可以轻松实现数组去重
ES2018引入了对象扩展运算符,极大简化了对象的操作。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扩展运算符在对象操作中的应用</title>
<style>
.property {
margin: 5px 0;
padding: 8px;
background-color: #f5f5f5;
border-radius: 4px;
}
</style>
</head>
<body>
<h2>扩展运算符在对象操作中的应用</h2>
<div id="output"></div>
<script>
const output = document.getElementById('output');
// 1. 对象浅拷贝
const originalObj = {
name: "张三",
age: 25,
address: {
city: "北京",
street: "长安街"
}
};
const copiedObj = { ...originalObj };
originalObj.age = 30; // 修改原对象的简单属性
originalObj.address.city = "上海"; // 修改原对象的嵌套属性
const displayObj = (obj, title) => {
let html = `<div class="property"><strong>${title}:</strong><br>`;
for (let key in obj) {
if (typeof obj[key] === 'object') {
html += ` ${key}: ${JSON.stringify(obj[key])}<br>`;
} else {
html += ` ${key}: ${obj[key]}<br>`;
}
}
html += '</div>';
return html;
};
output.innerHTML += displayObj(originalObj, "原对象 (修改后)");
output.innerHTML += displayObj(copiedObj, "复制对象");
output.innerHTML += `<p class="property">说明:扩展运算符执行的是浅拷贝,嵌套对象仍然共享引用</p>`;
// 2. 对象合并
const baseInfo = { name: "李四", age: 28 };
const contactInfo = { email: "lisi@example.com", phone: "13800138000" };
const workInfo = { company: "科技公司", position: "前端工程师" };
const mergedObj = { ...baseInfo, ...contactInfo, ...workInfo };
output.innerHTML += displayObj(mergedObj, "合并后的对象");
// 3. 对象属性覆盖(后面的属性覆盖前面的)
const defaultSettings = { theme: "light", fontSize: 14, notifications: true };
const userSettings = { theme: "dark", fontSize: 16 };
const finalSettings = { ...defaultSettings, ...userSettings };
output.innerHTML += displayObj(finalSettings, "设置合并(用户设置覆盖默认设置)");
// 4. 添加新属性
const person = { name: "王五", age: 35 };
const personWithJob = { ...person, job: "设计师", salary: 15000 };
output.innerHTML += displayObj(personWithJob, "添加新属性");
// 5. 移除属性(结合解构)
const user = { id: 1, name: "赵六", password: "secret", role: "admin" };
const { password, ...userWithoutPassword } = user;
output.innerHTML += displayObj(userWithoutPassword, "移除password属性后的对象");
</script>
</body>
</html>运行效果:

说明:
对象扩展运算符创建的是浅拷贝,嵌套对象仍然共享引用
合并对象时,后面对象的属性会覆盖前面对象的同名属性
可以方便地添加新属性到对象中
结合解构赋值可以"移除"对象中的某些属性
扩展运算符在函数参数处理中有着广泛的应用,特别是在处理不定数量参数时。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扩展运算符在函数参数中的应用</title>
<style>
.result {
margin: 10px 0;
padding: 10px;
background-color: #e8f4fd;
border-left: 4px solid #2196F3;
}
</style>
</head>
<body>
<h2>扩展运算符在函数参数中的应用</h2>
<div id="output"></div>
<script>
const output = document.getElementById('output');
// 1. 替代apply方法
const numbers = [5, 10, 15, 20];
const maxNumber = Math.max(...numbers);
output.innerHTML += `<div class="result">数组中的最大值: Math.max(...[5, 10, 15, 20]) = ${maxNumber}</div>`;
// 2. 收集剩余参数
function displayUser(firstName, lastName, ...hobbies) {
let result = `姓名: ${firstName} ${lastName}`;
if (hobbies.length > 0) {
result += `<br>爱好: ${hobbies.join(', ')}`;
}
return result;
}
output.innerHTML += `<div class="result">${displayUser('张', '三', '游泳', '读书', '编程')}</div>`;
output.innerHTML += `<div class="result">${displayUser('李', '四')}</div>`;
// 3. 动态参数函数
function createURL(baseURL, ...pathSegments) {
return baseURL + pathSegments.join('/') + '/';
}
const apiURL = createURL('https://api.example.com', 'v1', 'users', 'profile');
output.innerHTML += `<div class="result">创建URL: ${apiURL}</div>`;
// 4. 合并配置对象
function mergeConfig(defaultConfig, userConfig) {
return { ...defaultConfig, ...userConfig };
}
const defaultConfig = {
apiUrl: 'https://api.default.com',
timeout: 5000,
retry: 3
};
const userConfig = {
apiUrl: 'https://api.custom.com',
timeout: 10000
};
const finalConfig = mergeConfig(defaultConfig, userConfig);
output.innerHTML += `<div class="result">合并配置: ${JSON.stringify(finalConfig)}</div>`;
// 5. 柯里化函数中使用扩展运算符
function curryAdd(x) {
return function(...args) {
return args.reduce((sum, num) => sum + num, x);
};
}
const add5 = curryAdd(5);
const result1 = add5(10); // 15
const result2 = add5(10, 20, 30); // 65
output.innerHTML += `<div class="result">柯里化函数: add5(10) = ${result1}, add5(10, 20, 30) = ${result2}</div>`;
// 6. 在箭头函数中使用
const logAll = (...args) => {
console.log('所有参数:', args);
return `共收到 ${args.length} 个参数`;
};
output.innerHTML += `<div class="result">${logAll(1, 'hello', true, {name: 'test'})}</div>`;
</script>
</body>
</html>运行效果:

说明:
扩展运算符可以替代Function.prototype.apply,使代码更简洁
在函数参数中使用...rest可以收集所有剩余参数
可以创建接受任意数量参数的灵活函数
在配置合并等场景中特别有用
可以与箭头函数完美结合使用
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扩展运算符综合实战</title>
<style>
.demo-container {
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
margin: 15px 0;
background-color: #f9f9f9;
}
.demo-title {
color: #333;
border-bottom: 2px solid #4CAF50;
padding-bottom: 5px;
margin-top: 0;
}
.code-block {
background-color: #2d2d2d;
color: #f8f8f2;
padding: 10px;
border-radius: 5px;
font-family: 'Courier New', monospace;
overflow-x: auto;
margin: 10px 0;
}
</style>
</head>
<body>
<h1>扩展运算符综合实战</h1>
<div id="output"></div>
<script>
const output = document.getElementById('output');
// 场景1: 状态管理中的状态更新 (类似React的setState)
function updateState(oldState, newState) {
return {
...oldState,
...newState,
updatedAt: new Date().toISOString()
};
}
const initialState = {
user: null,
isLoading: false,
theme: 'light',
preferences: {
fontSize: 14,
notifications: true
}
};
const newState = updateState(initialState, {
user: { name: '张三', id: 123 },
isLoading: true,
preferences: { ...initialState.preferences, fontSize: 16 }
});
output.innerHTML += `
<div class="demo-container">
<h3 class="demo-title">场景1: 状态管理中的不可变更新</h3>
<p><strong>原状态:</strong> ${JSON.stringify(initialState, null, 2)}</p>
<p><strong>新状态:</strong> ${JSON.stringify(newState, null, 2)}</p>
<div class="code-block">
// 使用扩展运算符进行不可变更新
function updateState(oldState, newState) {
return {
...oldState, // 展开原状态
...newState, // 用新状态覆盖
updatedAt: new Date().toISOString() // 添加新字段
};
}
</div>
</div>
`;
// 场景2: 合并API响应数据
function mergeAPIResponses(responses) {
return responses.reduce((merged, response) => ({
...merged,
...response.data,
meta: {
...merged.meta,
...response.meta,
sources: [...(merged.meta?.sources || []), response.source]
}
}), {});
}
const apiResponses = [
{
source: 'user-api',
data: { id: 1, name: '张三', email: 'zhangsan@example.com' },
meta: { timestamp: '2023-10-01T10:00:00Z', version: 'v1' }
},
{
source: 'profile-api',
data: { age: 30, city: '北京' },
meta: { timestamp: '2023-10-01T10:05:00Z', requestId: 'abc123' }
}
];
const mergedData = mergeAPIResponses(apiResponses);
output.innerHTML += `
<div class="demo-container">
<h3 class="demo-title">场景2: 合并多个API响应</h3>
<p><strong>合并后的数据:</strong> ${JSON.stringify(mergedData, null, 2)}</p>
<div class="code-block">
// 合并多个API响应
function mergeAPIResponses(responses) {
return responses.reduce((merged, response) => ({
...merged, // 展开已合并的数据
...response.data, // 添加新数据
meta: {
...merged.meta, // 展开原meta
...response.meta, // 添加新meta
sources: [...(merged.meta?.sources || []), response.source]
}
}), {});
}
</div>
</div>
`;
// 场景3: 函数式编程工具函数
const fpUtils = {
// 管道函数:将多个函数组合起来
pipe: (...fns) => (x) => fns.reduce((v, f) => f(v), x),
// 部分应用函数
partial: (fn, ...fixedArgs) => (...remainingArgs) =>
fn(...fixedArgs, ...remainingArgs),
// 防抖函数
debounce: (fn, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
};
// 使用示例
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const add5 = fpUtils.partial(add, 5);
const calculate = fpUtils.pipe(add5, x => multiply(x, 2));
output.innerHTML += `
<div class="demo-container">
<h3 class="demo-title">场景3: 函数式编程工具函数</h3>
<p><strong>pipe(加5, 乘2)(10) =</strong> ${calculate(10)}</p>
<p><strong>partial(add, 5)(10) =</strong> ${add5(10)}</p>
<div class="code-block">
// 使用扩展运算符创建函数式工具
const fpUtils = {
pipe: (...fns) => (x) => fns.reduce((v, f) => f(v), x),
partial: (fn, ...fixedArgs) => (...remainingArgs) =>
fn(...fixedArgs, ...remainingArgs),
debounce: (fn, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
};
</div>
</div>
`;
// 场景4: 处理表单数据
function handleFormChange(currentFormData, fieldName, value) {
return {
...currentFormData,
[fieldName]: value,
touched: {
...currentFormData.touched,
[fieldName]: true
}
};
}
const initialFormData = {
username: '',
email: '',
age: '',
touched: {}
};
const updatedForm = handleFormChange(initialFormData, 'username', '张三');
const finalForm = handleFormChange(updatedForm, 'age', '30');
output.innerHTML += `
<div class="demo-container">
<h3 class="demo-title">场景4: 表单状态管理</h3>
<p><strong>更新表单字段:</strong> ${JSON.stringify(finalForm, null, 2)}</p>
<div class="code-block">
// 处理表单字段更新
function handleFormChange(currentFormData, fieldName, value) {
return {
...currentFormData, // 展开原表单数据
[fieldName]: value, // 更新指定字段
touched: {
...currentFormData.touched, // 展开原touched状态
[fieldName]: true // 标记字段已被触摸
}
};
}
</div>
</div>
`;
</script>
</body>
</html>运行效果:

说明:
扩展运算符在状态管理中可以实现不可变更新,这是现代前端框架的核心概念
可以优雅地合并多个API响应,处理复杂的数据结构
在函数式编程中,扩展运算符可以创建灵活的工具函数
表单处理是扩展运算符的常见应用场景,可以清晰地管理表单状态

浅拷贝问题:扩展运算符执行的是浅拷贝,对于嵌套对象需要特别注意
性能考虑:对于大型数组或对象,过度使用扩展运算符可能影响性能
浏览器兼容性:对象扩展运算符在ES2018中引入,需要确保目标环境支持
可读性:虽然扩展运算符能让代码更简洁,但过度使用可能降低可读性
扩展运算符是JavaScript中一个非常强大的特性,它简化了数组和对象的操作,使函数参数处理更加灵活。以上是关于扩展运算符的介绍,希望对大家能有所帮助!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。