[英]Spread Syntax ES6
考慮以下示例代碼
var x = ["a", "b", "c"];
var z = ["p", "q"];
var d = [...x, ...z];
var e = x.concat(z);
這里d
和e
的值完全相同,等於["a", "b", "c", "p", "q"]
,所以,
.concat
明顯更有效:http: //jsperf.com/spread-into-array-vs-concat 因為...
(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] );
}
這是另一個隨意的例子,但它更清楚為什么擴展運算符在一些其他冗長和笨重的情況下會很好用。
Spread 也適用於任意可迭代對象,這意味着它不僅適用於
Array
,還適用於Map
和Set
等。
這是一個很好的觀點,並且增加了一個想法——雖然在 ES5 中並非不可能實現——擴展運算符中引入的功能是新語法中更有用的項目之一。
有關此特定上下文中擴展運算符的實際底層語法(因為...
也可以是“rest”參數),請參閱規范。 正如我在上面所寫的,“顯式迭代索引以擴展數組的更基本的底層語法”足以說明這一點,但實際定義使用GetValue
和GetIterator
作為后面的變量。
把問題打亂了,讓我們從基本問題開始:展開語法到底有什么用?
擴展語法基本上解包了可迭代的元素,例如數組或對象。 或者,對於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.iterator和Symbol.isConcatSpreadable :
Spread運算符使用@@iterator
符號來遍歷數組和類似數組的對象,例如:
(這就是為什么你可以在他們身上使用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 展平為其數組元素。
如果設置為false
, Array.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 方法而不是擴展運算符。 但是,擴展運算符的使用不限於數組的串聯。
擴展語法允許擴展數組表達式或字符串等可迭代對象。 它可以用於以下場景。
帶數組的擴展運算符
帶對象的擴展運算符
要查看所有這些用途的演示並嘗試代碼,請點擊以下鏈接(codepen.io)
/**
* 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.