两者的区别
JavaScript扩展运算符和剩余运算符都是在ES6中引入的新特性,它们的主要作用是使操作符号更加灵活,让开发人员可以更方便地处理数组和对象。
扩展运算符和剩余运算符虽然都使用三个点(...)符号,但是它们在语法和使用上有很大的区别。按照我的理解,其根本区别在于两者出现在赋值运算符中的位置不同。
扩展运算符在赋值运算符的右侧,它的作用是将一个数组或对象展开,将其中的元素或属性逐一赋值给新的变量。例如:
const arr = [1, 2, 3];
const [a, b, c] = [...arr];
剩余运算符在赋值运算符的左侧,它的作用是将一个数组或对象的剩余部分捆绑为一个新的数组或对象。例如:
const [a, b, ...rest] = [1, 2, 3, 4, 5];
拓展运算符的运用场景
扩展运算符可以用于很多不同的场景,以下是一些常见的运用场景:
将可迭代对象转换为数组
扩展运算符可以将一个可迭代对象(如字符串、数组、Set等)转换为数组。其实现原理是遍历该可迭代对象,并将其中的每个元素依次添加到新的数组中。
- 字符串
const str = 'hello';
console.log(str) //hello
const arr = [...str];
console.log(arr); // ['h', 'e', 'l', 'l', 'o']
上面的代码将字符串展开成一个由单个字符组成的数组。
- Map
const map1 = new Map([
['key1', 'value1'],
['key2', 'value2']
]);
console.log(map1) //Map(2) { 'key1' => 'value1', 'key2' => 'value2' }
const keys = [...map1.keys()];
console.log(keys); // ['key1', 'key2']
const values = [...map1.values()];
console.log(values); // ['value1', 'value2']
上面的代码使用将Map的keys或values转换为数组。
- Set
const set1 = new Set([1, 2, 3]);
console.log(set1) //Set(3) { 1, 2, 3 }
const arr1 = [...set1];
console.log(arr1); // [1, 2, 3]
上面的代码将Set转换为数组。
合并可迭代对象
扩展运算符可以用于任何可迭代对象(包括数组、字符串、Set、Map等)。其实现原理是将可迭代对象(如数组、字符串、Set等)中的每个元素依次提取出来,然后添加到一个新的可迭代对象(包括数组、字符串、Set、Map等)中。
- 字符串
const str1 = 'hello';
const str2 = 'world';
const str3 = [...str1, ...str2].join('');
console.log(str3); // 'helloworld'
上面的代码使用join('')将两个展开的字符串合并成一个新的字符串。
- 数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [...arr1, ...arr2];
console.log(arr3); // [1, 2, 3, 4]
上面的代码将两个数组展开并合并成一个新的数组。
- Map
const map1 = new Map([['a', 1], ['b', 2]]);
const map2 = new Map([['b', 3], ['c', 4]]);
const map3 = new Map([...map1, ...map2]);
console.log(map3); // Map(3) {'a' => 1, 'b' => 3, 'c' => 4}
上面的代码使用new Map()将两个展开的Map对象并合并成一个新的Map对象,其中后面的键值对会覆盖前面的相同键的值。
- Set
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);
const set3 = new Set([...set1, ...set2]);
console.log(set3); // Set(4) {1, 2, 3, 4}
上面的代码使用new Set()将两个展开的Set对象并合并成一个新的Set对象。
- 对象
虽然对象本身不是可迭代对象,但是它的属性和值是可以被迭代的,因此我们可以使用扩展运算符来对对象进行操作。
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const mergedObj = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }
需要注意的是,当多个对象合并时,如果两个对象有相同的属性名,则后面的对象的属性值会覆盖前面的对象的属性值。因此,在使用扩展运算符合并对象时,需要注意属性名的唯一性,以避免属性值被覆盖的情况。
传递参数到函数中
function sum(a, b, c) {
return a + b + c;
}
const arr = [1, 2, 3];
console.log(sum(...arr)); // 6
算法应用
- 数组扁平化
const arr = [1, [2, [3, 4], 5], 6];
const flattenedArr = [];
while (arr.length) {
const next = arr.shift();
if (Array.isArray(next)) {
arr.unshift(...next);
} else {
flattenedArr.push(next);
}
}
console.log(flattenedArr); // [1, 2, 3, 4, 5, 6]
- 数组去重
const arr = [1, 2, 3, 3, 4, 4, 5];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4, 5]
在这个示例中,我们使用扩展运算符将arr数组转换为Set数据结构,再使用扩展运算符将Set转换回数组。由于Set不允许重复元素存在,因此新数组uniqueArr中只包含了arr数组中的不重复元素。
- 排序算法
在排序算法中,可以使用扩展运算符结合Array.sort()方法进行排序。例如,下面是一个使用快速排序算法对数组进行升序排序的函数:
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
const pivotIndex = Math.floor(arr.length / 2);
const pivot = arr[pivotIndex];
const left = [];
const right = [];
for (let i = 0; i < arr.length; i++) {
if (i === pivotIndex) {
continue;
}
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
在这个函数中,我们使用扩展运算符将左半部分、中间的轴元素和右半部分连接起来,得到一个排序后的新数组。
剩余运算符运用场景
- 不定参数函数:可以将剩余运算符用于定义一个不定参数函数,允许函数接受任意数量的参数。
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
- 数组拷贝:可以使用剩余运算符将数组中的一部分拷贝到另一个数组中。
const originalArray = [1, 2, 3, 4, 5];
const [first, second, ...rest] = originalArray;
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
需要注意的是,剩余运算符只能在函数参数或数组/对象的结尾使用,因为它会将剩余的所有元素放入一个新的数组或对象中。
如果我们使用如下的代码:
const originalArray = [1, 2, 3, 4, 5];
const [first, second, ...rest, last] = originalArray;
这段代码会抛出语法错误,因为剩余运算符只能在数组/对象的结尾使用,这里它被放在了中间。