JavaScript使用符号三个点(...)作为剩余运算符和展开运算符,不过这两个运算符是有区别的。
最主要的区别就是,剩余运算符将用户提供的某些特定值的其余部分放入JavaScript数组中,而展开运算符则将可迭代的对象展开为单个元素。
例如下面这段代码,其中使用了剩余运算符将部分值存放到数组中:
// Use rest to enclose the rest of specific user-supplied values into an array:
function myBio(firstName, lastName, ...otherInfo) {
return otherInfo;
}
// Invoke myBio function while passing five arguments to its parameters:
myBio("Oluwatobi", "Sofela", "CodeSweetly", "Web Developer", "Male");
// The invocation above will return:
["CodeSweetly", "Web Developer", "Male"]
上面的代码中,我们使用...otherInfo将传入函数myBio()参数中的剩余部分"CodeSweetly","We Developer"和"Male"存放到数组中。
然后我们再来看下面这个例子,其中使用了展开运算符:
// Define a function with three parameters:
function myBio(firstName, lastName, company) {
return `${firstName} ${lastName} runs ${company}`;
}
// Use spread to expand an array’s items into individual arguments:
myBio(...["Oluwatobi", "Sofela", "CodeSweetly"]);
// The invocation above will return:
“Oluwatobi Sofela runs CodeSweetly”
上面的代码中,我们使用展开运算符(...)将数组["Oluwatobi", "Sofela", "CodeSweetly"]的内容逐一展开并传递给函数myBio()的参数。
如果你对剩余运算符和展开运算符不是很熟悉,不用太担心,本文接下来会对它们进行介绍。
在接下来的章节中,我们将详细讨论剩余运算符和展开运算符在JavaScript中是如何工作的。
正如上面所提到的,剩余运算符将用户提供的某些特定值的其余部分放入JavaScript数组中。语法如下:
...yourValues
上面代码中的三个点(...)用来表示剩余运算符。
剩余运算符之后的内容用来表示你希望填充到数组中的值。注意,只能在函数定义的最后一个参数中使用剩余运算符。
在JavaScript函数中,剩余运算符被用在函数最后一个参数的前面。如下面的代码:
// Define a function with two regular parameters and one rest parameter:
function myBio(firstName, lastName, ...otherInfo) {
return otherInfo;
}
这里,剩余运算符告诉JavaScript程序将用户提供给参数otherInfo的任何值都添加到一个数组中。然后,将该数组赋值给otherInfo参数。
因此,我们将...otherInfo称之为剩余参数。
需要注意的是,调用时传递给函数的实参中剩余参数是可选的。
另一个例子:
// Define a function with two regular parameters and one rest parameter:
function myBio(firstName, lastName, ...otherInfo) {
return otherInfo;
}
// Invoke myBio function while passing five arguments to its parameters:
myBio("Oluwatobi", "Sofela", "CodeSweetly", "Web Developer", "Male");
// The invocation above will return:
["CodeSweetly", "Web Developer", "Male"]
上面的代码中,调用函数myBio()时传入了5个参数,而函数myBio()的实参只有3个。也就是说,参数"Oluwatobi"和"Sofela"分别传递给了firstName和lastName,其它的参数("CodeSweetly","Web Developer"和"Male")都传递给了剩余参数otherInfo。所以,函数myBio()返回的结果是["CodeSweetly", "Web Developer", "Male"],它们都是剩余参数otherInfo的内容。
记住,不能在任何包含剩余参数,默认参数,或参数解构的函数体中使用"use strict"指令。否则,JavaScript将报语法错误。
考虑下面的代码:
// Define a function with one rest parameter:
function printMyName(...value) {
"use strict";
return value;
}
// The definition above will return:
"Uncaught SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list"
注意:只有当整个脚本或封闭作用域处于strict模式时,才可以将"use strict"放到函数体外。
现在我们知道了剩余运算符在函数中的作用,接下来我们来看看它在参数解构中是如何工作的。
剩余运算符在参数解构赋值时通常被用在最后一个变量的前面。下面是一个例子:
// Define a destructuring array with two regular variables and one rest variable:
const [firstName, lastName, ...otherInfo] = [
"Oluwatobi", "Sofela", "CodeSweetly", "Web Developer", "Male"
];
// Invoke the otherInfo variable:
console.log(otherInfo);
// The invocation above will return:
["CodeSweetly", "Web Developer", "Male"]
这里的剩余运算符(...)告诉JavaScript程序将用户提供的其它值都添加到一个数组中。然后,将该数组赋值给otherInfo变量。
因此,我们将这里的...otherInfo称之为剩余变量。
另一个例子:
// Define a destructuring object with two regular variables and one rest variable:
const { firstName, lastName, ...otherInfo } = {
firstName: "Oluwatobi",
lastName: "Sofela",
companyName: "CodeSweetly",
profession: "Web Developer",
gender: "Male"
}
// Invoke the otherInfo variable:
console.log(otherInfo);
// The invocation above will return:
{companyName: "CodeSweetly", profession: "Web Developer", gender: "Male"}
注意上面的代码中,剩余运算符将一个属性对象(而不是数组)赋值给otherInfo变量。也就是说,当你在解构一个对象时使用剩余运算符,它将生成一个属性对象而非数组。
但是,如果你在解构数组或函数时使用剩余运算符,它将生成数组字面量。
你应该已经注意到了,在JavaScript arguments和剩余参数之间存在一些区别,下面我们来看看这些区别都有哪些。
下面我列出了JavaScript arguments和剩余参数之间的一些区别:
请记住这一点,JavaScript arguments对象不是真正的数组。它是一个类似于数组的对象,不具备数组所拥有的任何特性。
而剩余参数是一个真正的数组,你可以在它上面使用数组所拥有的任何方法。例如,你可以对一个剩余参数使用sort()、map()、forEach()或pop()方法。但你不能对arguments对象使用这些方法。
arguments对象在箭头函数中不可用,而剩余参数在所有的函数中都是可用的,也包括箭头函数。
优先使用剩余参数而不是arguments对象,特别是在编写ES6兼容代码时。
我们已经了解了剩余运算符是如何工作的,下面我们来讨论下展开运算符,并看看它和剩余运算符之间的区别。
展开运算符(...)将一个可迭代的对象展开为单个元素。
展开运算符可以应用在数组字面量,函数调用,以及被初始化的属性对象中,它将可迭代对象的值逐一展开到单独的元素中。实际上,它和剩余操作符正好相反。
注意,只有在数组字面量,函数调用,或被初始化的属性对象中使用展开运算符时才有效。
下面我们来看一些实际的例子。
const myName = ["Sofela", "is", "my"];
const aboutMe = ["Oluwatobi", ...myName, "name."];
console.log(aboutMe);
// The invocation above will return:
[ "Oluwatobi", "Sofela", "is", "my", "name." ]
上面的代码中,展开运算符(...)将数组myName拷贝到aboutMe中。
注意:
const myName = "Oluwatobi Sofela";
console.log([...myName]);
// The invocation above will return:
[ "O", "l", "u", "w", "a", "t", "o", "b", "i", " ", "S", "o", "f", "e", "l", "a" ]
上面的代码中,我们在数组字面量中使用展开运算符([...])将myName字符串的值展开为一个数组。这样,字符串"Oluwatobi Sofela"的内容被展开到数组[ "O", "l", "u", "w", "a", "t", "o", "b", "i", " ", "S", "o", "f", "e", "l", "a" ]中。
const numbers = [1, 3, 5, 7];
function addNumbers(a, b, c, d) {
return a + b + c + d;
}
console.log(addNumbers(...numbers));
// The invocation above will return:
16
上面的代码中,我们使用展开运算符将数组numbers的内容展开并传递给函数addNumbers()的参数。如果数组numbers的元素多于4个,JavaScript只会将前4个元素作为参数传递给函数addNumbers()而忽略其余的元素。
下面是一些其它的例子:
const numbers = [1, 3, 5, 7, 10, 200, 90, 59];
function addNumbers(a, b, c, d) {
return a + b + c + d;
}
console.log(addNumbers(...numbers));
// The invocation above will return:
16
const myName = "Oluwatobi Sofela";
function spellName(a, b, c) {
return a + b + c;
}
console.log(spellName(...myName)); // returns: "Olu"
console.log(spellName(...myName[3])); // returns: "wundefinedundefined"
console.log(spellName([...myName])); // returns: "O,l,u,w,a,t,o,b,i, ,S,o,f,e,l,aundefinedundefined"
console.log(spellName({...myName})); // returns: "[object Object]undefinedundefined"
const myNames = ["Oluwatobi", "Sofela"];
const bio = { ...myNames, runs: "codesweetly.com" };
console.log(bio);
// The invocation above will return:
{ 0: "Oluwatobi", 1: "Sofela", runs: "codesweetly.com" }
上面的代码中,我们在bio对象内部使用展开运算符将数组myNames的值展开为各个属性。
当使用展开运算符时,请记住以下三个基本信息。
由于属性对象是非可迭代对象,所以不能使用展开运算符将它的值进行展开。但是,你可以使用展开运算符将一个对象的属性克隆到另一个对象中。
看下面这个例子:
const myName = { firstName: "Oluwatobi", lastName: "Sofela" };
const bio = { ...myName, website: "codesweetly.com" };
console.log(bio);
// The invocation above will return:
{ firstName: "Oluwatobi", lastName: "Sofela", website: "codesweetly.com" };
上面的代码中,我们使用展开运算符将myName对象的内容克隆到了bio对象中。
注意:
假设我们使用展开运算符将对象A的属性克隆到对象B中,如果对象B包含与对象A中相同的属性,那么对象B的属性将覆盖对象A的属性。换句话说,在这个过程中,对象A中那些与对象B相同的属性不会被克隆。
看下面这个例子:
const myName = { firstName: "Tobi", lastName: "Sofela" };
const bio = { ...myName, firstName: "Oluwatobi", website: "codesweetly.com" };
console.log(bio);
// The invocation above will return:
{ firstName: "Oluwatobi", lastName: "Sofela", website: "codesweetly.com" };
注意,展开运算符没有将myName对象的firstName属性的值复制到bio对象中,因为对象bio中已经包含firstName属性了。
如果在只包含原语值的对象(或数组)上使用展开运算符,JavaScript不会在原对象和复制对象之间创建任何引用。
看下面这段代码:
const myName = ["Sofela", "is", "my"];
const aboutMe = ["Oluwatobi", ...myName, "name."];
console.log(aboutMe);
// The invocation above will return:
["Oluwatobi", "Sofela", "is", "my", "name."]
注意,myName中的每一个元素都是一个原语值,因此,当我们使用展开运算符将myName克隆到aboutMe时,JavaScript不会在两个数组之间创建任何引用。所以,对myName数组所做的任何修改都不会反映到aboutMe数组中,反之亦然。
让我们给myName数组添加一个元素:
myName.push("real");
现在我们来检查一下myName和aboutMe的值:
console.log(myName); // ["Sofela", "is", "my", "real"]
console.log(aboutMe); // ["Oluwatobi", "Sofela", "is", "my", "name."]
请注意,我们对myName数组的修改并没有反映到aboutMe数组中,因为展开运算符并没有在原始数组和复制数组之间创建任何引用。
假设myName包含非原语项,这种情况下,展开运算符将在原数组的非原语项和克隆项之间创建一个引用。
看下面的例子:
const myName = [["Sofela", "is", "my"]];
const aboutMe = ["Oluwatobi", ...myName, "name."];
console.log(aboutMe);
// The invocation above will return:
[ "Oluwatobi", ["Sofela", "is", "my"], "name." ]
注意,这里的myName数组包含一个非原语项。
因此,当使用展开运算符将myName的内容克隆到aboutMe时,JavaScript将在两个数组之间创建一个引用。这样,任何对myName数组的修改都会反映到aboutMe数组中,反之亦然。
作为例子,我们同样给myName数组添加一个元素:
myName[0].push("real");
现在我们来查看myName和aboutMe的值:
console.log(myName); // [["Sofela", "is", "my", "real"]]
console.log(aboutMe); // ["Oluwatobi", ["Sofela", "is", "my", "real"], "name."]
注意,对myName数组的修改内容反映到了aboutMe数组中,因为展开运算符在原始数组和复制数组之间创建了一个引用。
另外一个例子:
const myName = { firstName: "Oluwatobi", lastName: "Sofela" };
const bio = { ...myName };
myName.firstName = "Tobi";
console.log(myName); // { firstName: "Tobi", lastName: "Sofela" }
console.log(bio); // { firstName: "Oluwatobi", lastName: "Sofela" }
上面的代码中,myName的更新内容没有反映到bio对象中,因为我们在只包含原语值的对象上使用展开运算符。
注意,开发人员通常将这里的myName称之为浅对象,因为它只包含原语项。
另外一个例子:
const myName = {
fullName: { firstName: "Oluwatobi", lastName: "Sofela" }
};
const bio = { ...myName };
myName.fullName.firstName = "Tobi";
console.log(myName); // { fullName: { firstName: "Tobi", lastName: "Sofela" } }
console.log(bio); // { fullName: { firstName: "Tobi", lastName: "Sofela" } }
上面的代码中,myName的更新内容被反映到bio对象中,因为我们在包含非原语值的对象上使用了展开运算符。
注意:
本文讨论了剩余操作符和展开操作符之间的区别,并通过示例说明了它们在JavaScript中是如何工作的。
原文地址:https://www.freecodecamp.org/news/javascript-rest-vs-spread-operators/
... 运算符, 是ES6里一个新引入的运算法, 也叫展开/收集 运算符, 我们每天都要和它打交道。这篇文章,我就带你系统的回顾下这个运算符, 介绍一些基础和进阶的用法。
JavaScript是一门了不起的语言。我喜欢它的灵活性:只需以你喜欢的方式做事:更改变量类型,动态的向对象添加方法或属性,对不同的变量类型使用运算符等等。然而动态是要付出代价的,开发人员需要知道怎样处理对于不同操作符的类型转换
TypeScript 2.1 增加了对 对象扩展运算和 rest 属性提案的支持,该提案在 ES2018 中标准化。可以以类型安全的方式使用 rest 和 spread 属性。对象 rest 属性假设已经定义了一个具有三个属性的简单字面量对象
我们可以使用展开操作符复制数组,不过要注意的是这是一个浅拷贝。这样我们就可以复制一个基本的数组,注意,它不适用于多级数组或带有日期或函数的数组。
JS 里的操作符大家每天都在使用,还有一些 ES2020、ES2021 新加的实用操作符,这些共同构成了 JS 灵活的语法生态
你有没有花一个下午的时间浏览过 Mozilla 文档?如果你有,你会很清楚网上有很多关于 JavaScript 的信息。这使得人们很容易忽略一些不同寻常的 JavaScript 操作符。
扩展运算符( spread )是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。array.push(...items)和add(...numbers)这两行,都是函数的调用,它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列。
首先,如果是嵌套写法的话,简单的嵌套还好,但是当嵌套变得很深的时候就有点难以阅读了。嵌套的执行流程是从右到左移动的,而不是我们正常阅读代码从左到右的方向
很多人都对双竖杠||非常熟悉,因为这个经常在项目中经常会用到。单竖杠|,却很少在项目开发中使用到。|是位运算符,||是逻辑运算符。平常,经常使用以下这个几个方法对数字进行处理。
Spread 和 Rest 是 ES6 Javascript 提供的两个功能,分别主要用于解构和函数参数处理。Spread 从可迭代对象(如数组、字符串或对象)中获取元素并将它们分散到各个部分。这就像将一副纸牌铺在桌子上一样。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!