簡體   English   中英

擴展語法 ES6

[英]Spread Syntax ES6

考慮以下示例代碼

var x = ["a", "b", "c"];
var z = ["p", "q"];

var d = [...x, ...z];

var e = x.concat(z);

這里de的值完全相同,等於["a", "b", "c", "p", "q"] ,所以,

  1. 這兩者之間究竟有什么區別?
  2. 哪個更有效,為什么?
  3. 擴展語法到底有什么用?
  1. 在您給出的示例中,兩者之間基本上沒有區別
  2. .concat明顯更有效:http: //jsperf.com/spread-into-array-vs-concat 因為... (spread) 只是更基本的底層語法之上的語法糖,它顯式迭代索引以擴展大批。
  3. Spread 允許在更笨重的直接數組操作之上使用糖化語法

為了擴展上面的#3,您對 spread 的使用是一個有點做作的例子(盡管它可能會經常出現在野外)。 例如,當應將整個參數列表傳遞給函數體中的.call時,Spread 很有用。

function myFunc(){
    otherFunc.call( myObj, ...args );
}

相對

function myFunc(){
    otherFunc.call( myObj, args[0], args[1], args[2], args[3], args[4] );
}

這是另一個隨意的例子,但它更清楚為什么擴展運算符在一些其他冗長和笨重的情況下會很好用。

正如@loganfsmyth 指出的那樣

Spread 也適用於任意可迭代對象,這意味着它不僅適用於Array ,還適用於MapSet等。

這是一個很好的觀點,並且增加了一個想法——雖然在 ES5 中並非不可能實現——擴展運算符中引入的功能是新語法中更有用的項目之一。


有關此特定上下文中擴展運算符的實際底層語法(因為...也可以是“rest”參數),請參閱規范 正如我在上面所寫的,“顯式迭代索引以擴展數組的更基本的底層語法”足以說明這一點,但實際定義使用GetValueGetIterator作為后面的變量。

把問題打亂了,讓我們從基本問題開始:展開語法到底有什么用?

擴展語法基本上解包了可迭代的元素,例如數組或對象。 或者,對於MDN Web Docs 中關於傳播語法的更詳細解釋:

擴展語法允許在預期零個或多個參數(對於函數調用)或元素(對於數組字面量)的地方擴展諸如數組表達式或字符串之類的可迭代對象,或者在零個或多個參數的地方擴展對象表達式鍵值對(用於對象文字)是預期的。

以下是擴展語法的典型用例的一些簡單示例以及擴展語法和其余參數之間差異的示例(它們可能看起來相同,但它們執行的功能幾乎相反)。

函數調用:

 const multiArgs = (one, two) => { console.log(one, two); }; const args = [1, 2]; multiArgs(...args); // 1 2

數組或字符串文字:

 const arr1 = [2, 3]; const arr2 = [1, ...arr1, 4]; console.log(arr2); // [1, 2, 3, 4] const s = 'split'; console.log(...s); // split

對象字面量:

 const obj1 = { 1: 'one' }; const obj2 = { 2: 'two' }; const obj3 = { ...obj1, ...obj2 }; console.log(obj3); // { 1: 'one', 2: 'two' }

Rest 參數語法與擴展語法不同:

Rest 參數語法看起來與擴展語法相同,但實際上將未知數量的函數參數表示為一個數組。 因此,與其“解包”可迭代的,其余參數實際上將多個參數打包到一個數組中。

 const multiArgs = (...args) => { console.log(args); }; multiArgs('a', 'b', 'c'); // ['a', 'b', 'c']

傳播語法性能/效率:

為了解決與其他方法相比效率的問題,唯一誠實的答案是“它取決於”。 瀏覽器一直在變化,與特定函數相關的上下文和數據會產生截然不同的性能結果,因此您會發現各種相互沖突的性能時序,這表明傳播語法比您可能使用的各種數組或對象方法快得多,也快得離譜用來完成類似的目標。 最后,任何速度優化至關重要的情況都應該進行比較測試,而不是依賴於忽略代碼和數據細節的簡單函數的通用時序。

concat()的比較:

最后是關於問題代碼中顯示的擴展語法和concat()之間差異的快速評論。 不同之處在於擴展語法不僅可以用於連接數組,而且可以在 IE 等較舊的瀏覽器中使用concat() 在您不關心與舊瀏覽器的兼容性並且不需要對速度進行微優化的情況下,傳播語法和concat()之間的選擇只是您發現更具可讀性的問題: arr3 = arr1.concat(arr2)arr3 = [...arr1, ...arr2]

這個例子的輸出是一樣的,但是在底層的行為是不一樣的,

考慮(檢查瀏覽器的控制台):

var x = [], y = [];

x[1] = "a";
y[1] = "b";

var usingSpread = [...x, ...y];
var usingConcat = x.concat(y);

console.log(usingSpread); // [ undefined, "a", undefined, "b"]
console.log(usingConcat); // [ , "a", , "b"] 

console.log(1 in usingSpread); // true
console.log(1 in usingConcat); // false

Array.prototype.concat將保留數組中的空槽,而 Spread 將用undefined的值替換它們。

輸入Symbol.iteratorSymbol.isConcatSpreadable

Spread運算符使用@@iterator符號來遍歷數組和類似數組的對象,例如:

  • Array.prototype
  • TypedArray.prototype
  • String.prototype
  • Map.prototype
  • 設置.prototype

(這就是為什么你可以在他們身上使用for .. of

我們可以覆蓋默認的iterator符號來查看spread運算符的行為:

 var myIterable = ["a", "b", "c"]; var myIterable2 = ["d", "e", "f"]; myIterable[Symbol.iterator] = function*() { yield 1; yield 2; yield 3; }; console.log(myIterable[0], myIterable[1], myIterable[2]); // abc console.log([...myIterable]); // [1,2,3] var result = [...myIterable, ...myIterable2]; console.log(result); // [1,2,3,"d","e","f"] var result2 = myIterable.concat(myIterable2); console.log(result2); // ["a", "b", "c", "d", "e", "f"]

另一方面, @@isConcatSpreadable

一個布爾值屬性,如果為 true,則表示對象應通過 Array.prototype.concat 展平為其數組元素。

如果設置為falseArray.concat將不會展平數組:

 const alpha = ['a', 'b', 'c']; const numeric = [1, 2, 3]; let alphaNumeric = alpha.concat(numeric); // console.log(alphaNumeric); numeric[Symbol.isConcatSpreadable] = false; alphaNumeric = alpha.concat(numeric); // alphaNumeric = [...alpha, ...numeric]; // the above line will output : ["a","b","c",1,2,3] console.log(JSON.stringify(alphaNumeric)); // ["a","b","c",[1,2,3]]

然而,當涉及到Objects時, spread行為不同,因為它們是不可迭代

var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
var objCopy = {...obj}; // copy

它將自己的可枚舉屬性從提供的對象復制到新對象上。

擴展運算符更快,請檢查spread-into-array-vs-concat (至少從 Chrome 67 開始)

並檢查 三個點如何在某些用例中更改 javascript ,其中包括解構賦值(數組或對象):

 const arr = [1, 2, 3, 4, 5, 6, 7]; const [first, , third, ...rest] = arr; console.log({ first, third, rest });

並將字符串拆分為字符數組:

console.log( [...'hello'] ) // [ "h", "e" , "l" , "l", "o" ]

在給定的示例中,這兩者之間沒有區別。 對於連接,我們可以使用 concat 方法而不是擴展運算符。 但是,擴展運算符的使用不限於數組的串聯。

擴展語法允許擴展數組表達式或字符串等可迭代對象。 它可以用於以下場景。

  1. 帶數組的擴展運算符

    • 數組串聯
    • 字符串到數組
    • 數組作為函數的參數。
  2. 帶對象的擴展運算符

    • 對象的串聯

要查看所有這些用途的演示並嘗試代碼,請點擊以下鏈接(codepen.io)

ES6-Spread Operator 的演示

/**
* Example-1: Showing How Spread Operator can be used to concat two or more     
arrays. 
*/
const americas = ['South America', 'North America'];

const eurasia = ['Europe', 'Asia'];

const world = [...americas, ...eurasia];

/**
* Example-2: How Spread Operator can be used for string to array.
*/
const iLiveIn = 'Asia';
const iLiveIntoArray = [...iLiveIn];

/**
* Example-3: Using Spread Operator to pass arguments to function
*/
const numbers = [1,4,5];

const add = function(n1,n2,n3){
return n1 + n2 + n3;
};

const addition = add(numbers[0],numbers[1],numbers[2]);
const additionUsingSpread = add(...numbers);

/**
* Example-4: Spread Operator, can be used to concat the array
*/

const personalDetails = {
  name: 'Ravi',
  age: '28',
  sex: 'male'
};

const professionalDetails = {
  occupation: 'Software Engineer',
  workExperience: '4 years'
};

const completeDetails = {...personalDetails, ...professionalDetails};

常量顏色 = ['藍色','紅色','黑色']; // 簡單數組。

const my_colours = ['藍色','紅色','黑色','黃色','綠色'];

const favourite_colours = [...my_colours,'grey']; //[...] 在另一個數組中傳播運算符訪問數據。

擴展語法允許在預期零個或多個元素的地方擴展可迭代對象。 這種高級解釋可能會令人困惑,因此“現實世界”的示例如下:

如果沒有擴展語法,您可以像這樣多次更新對象:

//If I needed to change the speed or damage at any time of a race car

const raceCar = {name: 'Ferrari 250 GT'};
const stats = {speed: 66, damage: 1, lap: 2};

raceCar['speed'] = stats.speed;
raceCar['damage'] = stats.damage;

或者,更簡潔的解決方案是使用擴展語法創建一個新對象:

//Creates a new object with priority from left to right 
const lap1 = { ...raceCar, ...stats }

//Or a specific variable:
const enterPitStop = {...raceCar, speed: 0 }

從本質上講,您將創建一個新的不可變對象,而不是改變raceCar 的原始對象。

在向數組添加新值時也很有幫助。 通過傳播,您可以通過復制前一個數組來推送/取消移動多個變量。 在傳播之前,你會像這樣推動:

var raceCars = ['Ferrari 250 GT', 'Le Mans Series', '24 Heures du Mans'];

//Sometimes, you will need to push multiple items to an array, which gets messy in large projects!
raceCars.push('Car 1');
raceCars.push('Car 2');
raceCars.push('Car 3');

相反,為了簡單起見,您將復制該數組並將其添加到一個新變量或同一個變量中。

//Push values to array
raceCars = [...raceCars, 'Car 1', 'Car 2', 'Car 3'];

//This is dynamic! Add the values anywhere in the array:

//Adds the values at the front as opposed to the end
raceCars = ['Car 1', 'Car 2', 'Car 3', ...raceCars];

//Another dynamic examples of adding not in the front or back:
raceCars = ['Car 1', 'Car 2', ...raceCars, 'Car 3'];

我鼓勵您在Mozilla 開發者網站上查看更詳細的文檔。

上下文:您想連接兩個數組,以便使用三點展開語法“按值”獲取副本,但您正在使用復雜/嵌套數組。

發現:注意嵌套數組不是按值傳遞,而是按引用傳遞。 換句話說,只有第一級項目作為副本“按值”傳遞。 請參閱示例:

 sourceArray1 = [ 1, [2, 3] ] // Third element is a nested array sourceArray2 = [ 4, 5 ] targetArray = [ ...sourceArray1, ...sourceArray2] console.log("Target array result:\n", JSON.stringify(targetArray), "\n\n") //it seems a copy, but... console.log("Let's update the first source value:\n") sourceArray1[0] = 10 console.log("Updated source array:\n", JSON.stringify(sourceArray1), "\n") console.log("Target array is NOT updated, It keeps a copy by value: 1\n") console.log(JSON.stringify(targetArray), "\n\n") //But if you update a nested value, it has NOT been copied console.log("Let's update a nested source value:\n") sourceArray1[1][0] = 20 console.log("Updated source nested array:\n", JSON.stringify(sourceArray1), "\n") console.log("Target array is updated BY REFERENCE!\n") console.log(JSON.stringify(targetArray)) // it is not a copy, it is a reference! console.log("\nCONCLUSION: ... spread syntax make a copy 'by value' for first level elements, but 'by reference' for nested/complex elements (This applies also for objects) so take care!\n")

Syntax

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));
// expected output: 6

console.log(sum.apply(null, numbers));

如果您有任何困惑,請觀看此視頻以獲得更多幫助。謝謝。 https://youtu.be/U00S_b2Fv2E

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM