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函式中是如何作業的?
在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"
記住,不能在任何包含剩余引數,默認引數,或引數解構的函式體中使用"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和剩余引數之間的一些區別:
區別1:arguments物件是一個類似于陣列的物件,但它并非真正的陣列!
請記住這一點,JavaScript arguments物件不是真正的陣列,它是一個類似于陣列的物件,不具備陣列所擁有的任何特性,
而剩余引數是一個真正的陣列,你可以在它上面使用陣列所擁有的任何方法,例如,你可以對一個剩余引數使用sort()、map()、forEach()或pop()方法,但你不能對arguments物件使用這些方法,
區別2:不能在箭頭函式中使用arguments物件
arguments物件在箭頭函式中不可用,而剩余引數在所有的函式中都是可用的,也包括箭頭函式,
區別3:優先使用剩余引數
優先使用剩余引數而不是arguments物件,特別是在撰寫ES6兼容代碼時,
我們已經了解了剩余運算子是如何作業的,下面我們來討論下展開運算子,并看看它和剩余運算子之間的區別,
什么是展開運算子以及它在JavaScript中是如何作業的?
展開運算子(...)將一個可迭代的物件展開為單個元素,
展開運算子可以應用在陣列字面量,函式呼叫,以及被初始化的屬性物件中,它將可迭代物件的值逐一展開到單獨的元素中,實際上,它和剩余運算子正好相反,
注意,只有在陣列字面量,函式呼叫,或被初始化的屬性物件中使用展開運算子時才有效,
下面我們來看一些實際的例子,
示例1:展開運算子在陣列字面量中如何作業
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中,
注意:
- 對myName的任何修改不會反映到aboutMe中,因為myName陣列中的所有值都是原語,擴展運算子只是簡單地將myName陣列的內容復制并粘貼到aboutMe中,而不創建任何對原始陣列元素的參考,
- 展開運算子只做淺拷貝,所以,當myName陣列中包含任何非原語值時,JavaScript將在myName和aboutMe之間創建一個參考,有關展開運算子如何處理原語值和非原語值的更多資訊,可以查看這里,
- 假設我們沒有使用展開運算子來復制陣列myName的內容,例如我們撰寫這行代碼const aboutMe = [ "Oluwatobi", myName, "name." ] 這種情況下JavaScript將給myName分配一個參考,這樣,所有對myName陣列所做的修改就都會反映到aboutMe陣列中,
示例2:如何使用展開運算子將字串轉換為陣列
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" ]中,
示例3:展開運算子如何在函式呼叫中作業
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"
查看運行結果
示例4:展開運算子在物件字面量中如何作業
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的值展開為各個屬性,
有關展開運算子我們需要知道的
當使用展開運算子時,請記住以下三個基本資訊,
1. 展開運算子不能展開物件字面量的值
由于屬性物件是非可迭代物件,所以不能使用展開運算子將它的值進行展開,但是,你可以使用展開運算子將一個物件的屬性克隆到另一個物件中,
看下面這個例子:
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物件中,
注意:
- 展開運算子只能展開可迭代物件的值,
- 只有當一個物件包含一個帶有@@iterator key的屬性時,才是一個可迭代的物件,
- Array,TypedArray,String,Map,Set都是內置的可迭代型別,因為它們默認都帶有@@iterator屬性,
- 屬性物件是非可迭代陣列型別,因為默認情況下它沒有@@iterator屬性,
- 可以在屬性物件上添加@@iterator使其成為可迭代物件,
2. 展開運算子不克隆相同的屬性
假設我們使用展開運算子將物件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屬性了,
3. 注意展開運算子在包含非原語的物件中是何如作業的
如果在只包含原語值的物件(或陣列)上使用展開運算子,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陣列包含非原語項呢?
假設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物件中,因為我們在包含非原語值的物件上使用了展開運算子,
注意:
- 我們稱這里的myName為深物件,因為它包含非原語項,
- 將一個物件克隆到另一個物件時,如果創建了參考,則進行的是淺拷貝,例如,...myName產生了myName物件的一個淺拷貝,因為你對其中一個物件所做的任何修改都會反映到另一個物件中,
- 將一個物件克隆到另一個物件時,如果沒有創建參考,則進行的時深拷貝,例如,我可以通過const bio = JSON.parse(JSON.stringify(myName))將myName物件深拷貝到bio物件中,如此一來,JavaScript將把myName克隆到bio中而不創建任何參考,
- 我們可以通過用一個新物件來替換myName或bio中的fullName子物件,從而切斷myName和bio之間的參考,例如,使用myName.fullName = { firstName: "Tobi", lastName: "Sofela" }來斷開myName和bio之間的指標,
結語
本文討論了剩余運算子和展開運算子之間的區別,并通過示例說明了它們在JavaScript中是如何作業的,
原文地址:https://www.freecodecamp.org/news/javascript-rest-vs-spread-operators/
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/394981.html
標籤:其他
下一篇:配置自動生成的按鈕以顯示不同的值
