JavaScript 中的三个点的故事
曾几何时,JavaScript 语言经历了一次重大升级,名为 ES6/ES2015。它引入了许多不同的新功能。其中之一就是可以在任何兼容容器(对象、数组、字符串、集合、映射)前面写三个连续的“.”。这些小小的“.”让我们能够编写更优雅、更简洁的代码。我将解释这三个“.”的工作原理,并展示最常见的用例。
三个连续的点有两种含义:扩展运算符和剩余运算符。
扩展运算符
展开运算符允许可迭代对象在接收者内部单独展开。可迭代对象和接收者可以是任何可循环的对象,例如数组、对象、集合、映射。您可以将一个容器的各个部分单独放入另一个容器中。
const newArray = ['first', ...anotherArray];
剩余参数
剩余参数语法允许我们将无限数量的参数表示为数组。命名参数可以位于剩余参数之前。
const func = (first, second, ...rest) => {};
用例
定义固然有用,但仅仅通过定义很难理解概念。我认为日常用例可能会弥补对定义的缺失理解。
复制数组
当我们必须改变一个数组但又不想触及原始数组(其他人可能会使用它)时,我们必须复制它。
const fruits = ['apple', 'orange', 'banana'];
const fruitsCopied = [...fruits]; // ['apple', 'orange', 'banana']
console.log(fruits === fruitsCopied); // false
// old way
fruits.map(fruit => fruit);
它选取数组中的每个元素,并将每个元素放入一个新的数组结构中。我们可以使用运算符实现数组的复制map
,并进行恒等映射。
唯一数组
我们希望从数组中剔除重复元素。最简单的解决方案是什么?
该Set
对象仅存储唯一元素,并可用数组填充。它也是可迭代的,因此我们可以将其展开回一个新数组,而我们收到的是一个包含唯一值的数组。
const fruits = ['apple', 'orange', 'banana', 'banana'];
const uniqueFruits = [...new Set(fruits)]; // ['apple', 'orange', 'banana']
// old way
fruits.filter((fruit, index, arr) => arr.indexOf(fruit) === index);
连接数组
我们可以用该方法连接两个单独的数组concat
,但为什么不再次使用扩展运算符呢?
const fruits = ['apple', 'orange', 'banana'];
const vegetables = ['carrot'];
const fruitsAndVegetables = [...fruits, ...vegetables]; // ['apple', 'orange', 'banana', 'carrot']
const fruitsAndVegetables = ['carrot', ...fruits]; // ['carrot', 'apple', 'orange', 'banana']
// old way
const fruitsAndVegetables = fruits.concat(vegetables);
fruits.unshift('carrot');
将参数作为数组传递
传递参数时,展开运算符开始让我们的代码更具可读性。在 ES6 之前,我们必须将函数应用于arguments
。现在,我们只需将参数展开到函数中,就能让代码更加简洁。
const mixer = (x, y, z) => console.log(x, y, z);
const fruits = ['apple', 'orange', 'banana'];
mixer(...fruits); // 'apple', 'orange', 'banana'
// old way
mixer.apply(null, fruits);
切片数组
使用该方法切片更直接slice
,但如果我们需要,也可以使用扩展运算符。我们必须逐个命名剩余元素,因此从大数组中间切片并不是一个好方法。
const fruits = ['apple', 'orange', 'banana'];
const [apple, ...remainingFruits] = fruits; // ['orange', 'banana']
// old way
const remainingFruits = fruits.slice(1);
将参数转换为数组
JavaScript 中的参数是类似数组的对象。你可以使用索引访问它,但不能像 , 那样调用数组方法map
。filter
参数是一个可迭代对象,那么我们该如何处理它呢?在它们前面加上三个点,就可以像数组一样访问它们!
const mixer = (...args) => console.log(args);
mixer('apple'); // ['apple']
将 NodeList 转换为数组
参数就像函数NodeList
的返回值querySelectorAll
。它们的行为也有点像数组,但没有相应的方法。
[...document.querySelectorAll('div')];
// old way
Array.prototype.slice.call(document.querySelectorAll('div'));
复制对象
最后,我们来谈谈对象操作。复制的操作与数组相同。之前,复制操作可以通过Object.assign
一个空的对象字面量来实现。
const todo = { name: 'Clean the dishes' };
const todoCopied = { ...todo }; // { name: 'Clean the dishes' }
console.log(todo === todoCopied); // false
// old way
Object.assign({}, todo);
合并对象
合并的唯一区别是具有相同键的属性会被覆盖。最右边的属性具有最高优先级。
const todo = { name: 'Clean the dishes' };
const state = { completed: false };
const nextTodo = { name: 'Ironing' };
const merged = { ...todo, ...state, ...nextTodo }; // { name: 'Ironing', completed: false }
// old way
Object.assign({}, todo, state, nextTodo);
需要注意的是,合并操作只会在层级结构的第一层创建副本。更深的层级将具有相同的引用。
将字符串拆分为字符
最后再说一下字符串。你可以使用展开运算符将字符串拆分成多个字符。当然,如果你使用空字符串调用 split 方法,效果也是一样的。
const country = 'USA';
console.log([...country]); // ['U', 'S', 'A']
// old way
country.split('');
就这样
我们研究了 JavaScript 中三个点的许多不同用例。正如您所见,ES6 不仅提高了代码编写效率,还引入了一些有趣的方法来解决长期存在的问题。现在所有主流浏览器都支持这种新语法;阅读本文时,您可以在浏览器控制台中尝试上述所有示例。无论哪种方式,您都可以开始使用扩展运算符和剩余参数。这是对 JavaScript 的一个极好的补充,您应该了解一下。
文章来源:https://dev.to/sonicoder/the-tale-of- Three-dots-in-javascript-4287