面试题:️说说 ES6 扩展运算符的作用及使用场景?

ES6(ECMAScript 2015)引入的扩展运算符(Spread Operator),使用三个点 ... 表示,是一种非常强大且常用的语法特性。它的核心作用是将一个可迭代对象(如数组、字符串、类数组对象)或对象“展开”(展开其元素或属性),以便在需要多个参数、元素或属性的地方使用。

注意:扩展运算符 ... 与 Rest 参数 ... 语法相同,但作用相反:

  • 扩展运算符:在调用/使用时,将一个集合“打散”成单个元素。
  • Rest 参数:在定义时,将多个单个元素“收集”成一个集合。

一、 扩展运算符的主要作用

  1. 展开数组元素:将数组的元素逐个取出。
  2. 展开对象属性:将对象的可枚举属性(不包括原型链上的)逐个取出。
  3. 展开字符串:将字符串的每个字符逐个取出。
  4. 展开类数组对象:如 argumentsNodeList 等。

二、 主要使用场景

1. 数组合并与复制 (Array Concatenation & Copying)

这是最常见的用途之一。

  • 合并数组
    const arr1 = [1, 2];
    const arr2 = [3, 4];
    const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
    // 等价于 arr1.concat(arr2),但更简洁
  • 复制数组(浅拷贝)
    const original = [1, 2, 3];
    const copy = [...original]; // [1, 2, 3]
    // 修改 copy 不会影响 original
    copy.push(4);
    console.log(original); // [1, 2, 3]
    console.log(copy); // [1, 2, 3, 4]
    注意:这是浅拷贝。如果数组元素是对象或数组,修改嵌套对象会影响原对象。
  • 在任意位置插入元素
    const numbers = [1, 4, 5];
    const withInsert = [0, ...numbers, 6]; // [0, 1, 4, 5, 6]
    const withInsert2 = [1, ...numbers.slice(1)]; // [1, 4, 5] (替换第一个元素)

2. 传递数组元素作为函数参数 (Function Arguments)

将数组的元素“打散”作为函数的独立参数传入。

function add(a, b, c) {
  return a + b + c;
}

const args = [1, 2, 3];
// ES5 方式:使用 apply
console.log(add.apply(null, args)); // 6
// ES6 方式:使用扩展运算符
console.log(add(...args)); // 6 (更简洁直观)

// 也可以与其他参数结合
console.log(add(...[1, 2], 3)); // 6

3. 对象合并与复制 (Object Merging & Copying) (ES2018)

扩展运算符也可用于对象(严格来说是 ES2018 的特性,但常与 ES6 特性一起讨论)。

  • 合并对象(浅拷贝)
    const defaults = { theme: 'light', fontSize: 14 };
    const userSettings = { theme: 'dark', color: 'blue' };
    const settings = { ...defaults, ...userSettings };
    console.log(settings); // { theme: 'dark', fontSize: 14, color: 'blue' }
    // 注意:后出现的属性会覆盖前面同名的属性
  • 复制对象(浅拷贝)
    const originalObj = { name: 'Alice', age: 30 };
    const copyObj = { ...originalObj };
    copyObj.age = 31;
    console.log(originalObj.age); // 30
    console.log(copyObj.age);     // 31
    注意:同样是浅拷贝。
  • 添加或修改属性
    const user = { name: 'Bob' };
    const updatedUser = { ...user, age: 25, name: 'Robert' };
    console.log(updatedUser); // { name: 'Robert', age: 25 }

4. 将类数组对象转换为数组

将 argumentsNodeListHTMLCollection 等类数组对象转换为真正的数组。

// 在函数中处理 arguments
function logArgs() {
  const argsArray = [...arguments]; // 转换为数组
  argsArray.forEach(arg => console.log(arg));
}
logArgs('a', 'b', 'c'); // 输出 a, b, c

// 将 NodeList 转换为数组
const nodeList = document.querySelectorAll('div');
const divArray = [...nodeList]; // 现在可以使用 map, filter 等数组方法
const texts = divArray.map(div => div.textContent);

5. 字符串展开

将字符串的每个字符展开为数组元素。

const str = 'hello';
const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']
console.log(Math.max(...[...'321'])); // 3 (将字符串 '321' 展开为 ['3','2','1'],再作为参数传给 Math.max)

三、 与其他操作符的结合

  • 与解构赋值结合
  • 与模板字符串结合

四、 注意事项

  1. 浅拷贝:无论是数组还是对象,扩展运算符进行的都是浅拷贝。如果对象包含嵌套的对象或数组,修改嵌套部分会影响原对象。
  2. 可枚举属性:对象扩展运算符只复制对象自身的、可枚举的属性。
  3. 不复制原型:扩展运算符不会复制对象的原型链。
  4. null 和 undefined:在对象扩展中,null 和 undefined 会被忽略,不会报错。
    const obj = { ...null, ...undefined, a: 1 }; // { a: 1 }
  5. 性能:对于大型数组或对象,扩展运算符可能会有性能开销,因为它需要遍历并创建新结构。

总结

ES6 的扩展运算符 ... 是一个语法糖,它的主要作用是展开可迭代对象或对象的元素/属性。主要使用场景包括:

  • 数组:合并、复制、作为函数参数、转换类数组对象。
  • 对象:合并、复制、添加/修改属性(ES2018)。
  • 字符串:转换为字符数组。

它让代码更加简洁、直观和函数式,是现代 JavaScript 开发中不可或缺的工具。理解其“展开”的本质,并注意其“浅拷贝”的特性,是正确使用它的关键。

THE END
喜欢就支持一下吧
点赞8 分享