[英]I don't understand about spread syntax inside objects
我不了解對象內的傳播語法。
console.log(...false) // TypeError not iterable
console.log(...1) // TypeError not iterable
console.log(...null) // TypeError not iterable
console.log(...undefined) // TypeError not iterable
我理解上面的代碼由於非迭代器而發生錯誤。
但是這些代碼運行良好。
console.log({...false}) // {}
console.log({...1}) // {}
console.log({...null}) // {}
console.log({...undefined}) // {}
請讓我知道為什么上述代碼有效。
這對於了解正在發生的事情非常重要,所以我必須從它開始。
語言中沒有定義擴展運算符。 有擴展語法,但作為其他類型語法的子類別。 這聽起來只是語義,但它對如何以及為什么...
工作有非常實際的影響。
操作員每次都以相同的方式行事。 如果您使用delete
運算符作為delete obj.x
,那么無論上下文如何,您總是會得到相同的結果。 與typeof
或什-
(減號)相同。 運算符定義將在代碼中完成的操作。 它總是相同的動作。 有時運算符可能會像+
一樣重載:
console.log("a" + "b"); //string concatenation console.log(1 + 2); //number addition
但它仍然不隨上下文而變化——你把這個表達放在什么地方。
該...
語法是不同的-它不是在不同的地方相同的操作:
const arr = [1, 2, 3]; const obj = { foo: "hello", bar: "world" }; console.log(Math.max(...arr)); //spread arguments in a function call function fn(first, ...others) {} //rest parameters in function definition console.log([...arr]); //spread into an array literal console.log({...obj}); //spread into an object literal
這些都是不同的語法,看起來相似,行為相似,但絕對不一樣。 如果...
是運算符,您可以更改操作數並且仍然有效,但事實並非如此:
const obj = { foo: "hello", bar: "world" }; console.log(Math.max(...obj)); //spread arguments in a function call //not valid with objects
function fn(...first, others) {} //rest parameters in function definition //not valid for the first of multiple parameters
const obj = { foo: "hello", bar: "world" }; console.log([...obj]); //spread into an array literal //not valid when spreading an arbitrary object into an array
所以,每次使用...
有不同的規則和工作並不像任何其他用途。
原因很簡單: ...
是不是一回事的。 該語言定義了不同事物的語法,例如函數調用、函數定義、數組文字和對象。 讓我們專注於最后兩個:
這是有效的語法:
const arr = [1, 2, 3]; // ^^^^^^^^^ // | // +--- array literal syntax console.log(arr); const obj = { foo: "hello", bar: "world!" }; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // | // +--- object literal syntax console.log(obj);
但這些不是:
const arr = [0: 1, 1: 2, 2: 3]; //invalid - you cannot have key-value pairs
const obj = { 1, 2, 3 }; //invalid - you need key-value pairs
不足為奇 - 不同的語法有不同的規則。
同樣,這同樣適用於使用...
— [...arr]
和{...obj}
只是您可以在 JavaScript 中使用的兩種不同類型的代碼,但...
用法之間沒有重疊,只是如何您可以將1
用作[1]
和{ 1: "one" }
但這兩次的含義不同。
這是真正需要回答的問題。 畢竟,這些是不同的操作。
您的帶有console.log(...false)
和console.log({...false})
示例演示了一個函數調用和一個對象字面量的用法,所以我將討論這兩個。 請注意,數組字面量擴展語法[...arr]
在有效和無效方面的行為非常相似,但在這里並不是很相關。 重要的是為什么對象的行為不同,所以我們只需要一個例子來比較。
fn(...args)
規范甚至沒有針對此構造的特殊名稱。 它只是ArgumentList
的一種類型,在第12.3.8.1節運行時語義:ArgumentListEvaluation (ECMAScript 語言規范鏈接)中,它本質上定義了“如果參數列表具有...
那么像這樣評估代碼”。 我會為您省去規范中使用的無聊語言(如果您想查看該鏈接,請隨時訪問該鏈接)。
要采取的步驟的關鍵點是使用...args
引擎將嘗試獲取args
的迭代器。 本質上是由迭代協議(MDN鏈接)定義的。 為此,它將嘗試調用用@@iterator
(或@@asyncIterator
)定義的方法。 這就是你得到 TypeError 的地方——它發生在args
沒有公開這樣的方法時。 沒有方法,意味着它不是可迭代的,因此引擎無法繼續調用該函數。
為了完整起見,如果args
是可迭代的,那么引擎將遍歷整個迭代器直到耗盡並從結果中創建參數。 這意味着我們可以在函數調用中使用任何具有擴展語法的任意迭代:
const iterable = { [Symbol.iterator]() { //define an @@iterator method to be a valid iterable const arr = ["!", "world", "hello"]; let index = arr.length; return { next() { //define a `next` method to be a valid iterator return { //go through `arr` backwards value: arr[--index], done: index < 0 } } } } } console.log(...iterable);
{...obj}
規范中仍然沒有此構造的特殊名稱。 它是對象文字的一種PropertyDefinition
類型。 第12.2.6.8節運行時語義:PropertyDefinitionEvaluation (ECMAScript 語言規范鏈接)定義了如何處理它。 我將再次為您提供定義。
不同之處在於obj
元素在擴展其屬性時是如何處理的。 為此,執行抽象操作CopyDataProperties ( target, source, excludedItems )
(ECMAScript 語言規范鏈接)。 這個可能值得一讀,以更好地了解到底發生了什么。 我將只關注重要的細節:
用表達式{...foo}
target
將是新對象source
將是foo
excludedItems
將是一個空列表,所以它無關緊要如果source
(提醒,這是代碼中的foo
)為null
或undefined
則操作結束, target
從CopyDataProperties
操作返回。 否則,繼續。
下一個重要的事情是foo
將變成一個對象。 這將使用ToObject ( argument )
抽象操作,其定義如下(再次提醒您不會在此處獲得null
或undefined
):
參數類型 | 結果 |
---|---|
不明確的 | 拋出 TypeError 異常。 |
空值 | 拋出 TypeError 異常。 |
布爾值 | 返回一個新的布爾對象,其 [[BooleanData]] 內部插槽設置為參數。 有關布爾對象的描述,請參見 19.3。 |
數字 | 返回一個新的 Number 對象,其 [[NumberData]] 內部槽設置為參數。 有關 Number 對象的說明,請參見 20.1。 |
細繩 | 返回一個新的 String 對象,其 [[StringData]] 內部槽設置為參數。 有關 String 對象的描述,請參見 21.1。 |
象征 | 返回一個新的 Symbol 對象,其 [[SymbolData]] 內部槽設置為參數。 參見 19.4 了解 Symbol 對象的描述。 |
大整數 | 返回一個新的 BigInt 對象,其 [[BigIntData]] 內部槽設置為參數。 有關 BigInt 對象的描述,請參見 20.2。 |
目的 | 返回參數。 |
我們稱這個操作的結果from
。
from
中的所有可枚舉屬性都將以其值寫入target
。
展開操作完成, target
是使用對象字面量語法定義的新對象。 完成的!
更概括地說,當您使用帶有對象字面量的擴展語法時,正在擴展的源將首先轉換為對象,然后實際上只有自己的可枚舉屬性會被復制到正在實例化的對象上。 在傳播null
或undefined
的情況下,傳播只是一個無操作:不會復制任何屬性並且操作正常完成(不拋出錯誤)。
這與函數調用中的傳播方式非常不同,因為不依賴於迭代協議。 您傳播的項目根本不必是可迭代的。
因為像Number
和Boolean
這樣的原始包裝器不產生任何自己的屬性,所以沒有什么可以復制的:
const numberWrapper = new Number(1); console.log( Object.getOwnPropertyNames(numberWrapper), //nothing Object.getOwnPropertySymbols(numberWrapper), //nothing Object.getOwnPropertyDescriptors(numberWrapper), //nothing ); const booleanWrapper = new Boolean(false); console.log( Object.getOwnPropertyNames(booleanWrapper), //nothing Object.getOwnPropertySymbols(booleanWrapper), //nothing Object.getOwnPropertyDescriptors(booleanWrapper), //nothing );
然而,字符串對象確實有自己的屬性,其中一些是可枚舉的。 這意味着您可以將字符串擴展到對象中:
const string = "hello"; const stringWrapper = new String(string); console.log( Object.getOwnPropertyNames(stringWrapper), //indexes 0-4 and `length` Object.getOwnPropertySymbols(stringWrapper), //nothing Object.getOwnPropertyDescriptors(stringWrapper), //indexes are enumerable, `length` is not ); console.log({...string}) // { "0": "h", "1": "e", "2": "l", "3": "l", "4": "o" }
這里更好地說明了值在傳播到對象中時的行為方式:
function printProperties(source) { //convert to an object const from = Object(source); const descriptors = Object.getOwnPropertyDescriptors(from); const spreadObj = {...source}; console.log( `own property descriptors:`, descriptors, `\\nproduct when spread into an object:`, spreadObj ); } const boolean = false; const number = 1; const emptyObject = {}; const object1 = { foo: "hello" }; const object2 = Object.defineProperties({}, { //do a more fine-grained definition of properties foo: { value: "hello", enumerable: false }, bar: { value: "world", enumerable: true } }); console.log("--- boolean ---"); printProperties(boolean); console.log("--- number ---"); printProperties(number); console.log("--- emptyObject ---"); printProperties(emptyObject); console.log("--- object1 ---"); printProperties(object1); console.log("--- object2 ---"); printProperties(object2);
對象傳播是完全不同的。 它在內部映射到Object.assign()
。
所以const a = {...1}
與const a = Object.assign({}, 1)
這里Object.assign({},1)
將1
視為object
而不是number
。 因此,您沒有收到任何拋出的異常。
此外,如果您對數組[...1]
嘗試過相同的操作,它應該會拋出錯誤,因為它不會將1
視為object
並且您會得到與..1
相同的行為。
總結一下:
console.log({...false}) => console.log(Object.assign({}, false))
console.log({...1}) => console.log(Object.assign({}, 1))
console.log({...null}) => console.log(Object.assign({}, null))
console.log({...undefined}) => console.log(Object.assign({}, undefined))
嗯,這就是 JS 的美妙之處,這要歸功於可迭代協議。 這意味着它意味着數組或映射。 默認情況下,這兩個都在語言構造中分配了行為,即它是一組我們可以一個接一個迭代的項目。 我們還可以根據需要統計和添加和刪除項目。
Example.JS 默認將它們理解為系列集合或集合或組。
const array1 = [1, 4, 9, 16]; console.log(array1.length); array1.push(5); console.log(array1.length);
現在這些不是 JS 中唯一的可迭代對象類型,字符串也是。
string = 'abc'; console.log(string.length) string = string+'d'; console.log(string.length) console.log(string[3])
然后有類似數組的對象也可以迭代
let arrayLike = { 0: "Hello", 1: "World", }; console.log(arrayLike[1])
{...false}
幾乎可以在下面的例子中對 b 發生什么。
它仍然是一個空對象,因為對象需要鍵值配對。
a = [1,2,3]; b={1,2,3}; console.log(a[1]); console.log(b[1]);
a 不需要配對鍵值定義,默認情況下它自己這樣做,正如眾所周知的索引。
a = [4,5,6]; b={1:4,2:5,3:6}; console.log(a[1]); console.log(b[1]);
a = [1,2,3]; b=[4,5,6]; c= [...a,...b]; d = [...a,b[1]]; console.log(c); console.log(d);
...(三個點)只告訴 Js 將其視為數組,如果它是可迭代的,否則只會拋出錯誤。 true false 不可迭代,大括號中的對象也不可迭代。 這就是為什么對象保持空白,因為......不會在非迭代項目上工作。 這有效
a = [1,2,3]; b = {...a}; console.log(b)
這不 - kaboom
a = [...false];
這也不起作用,只是保持沉默 - shshshs
a = {...false};
我希望你明白這一點。 其他任何事情都會彈出后續問題。
For example
var array1 = [1, 2, 3, 4];
var array2 = [5, 6, 7, 8];
array2 = [ ...array1, ...array2 ] // [1, 2, 3, 4, 5, 6, 7, 8]
/** spread array example */
var str1 = "hello";
var result_ary = [...str1] // ["h", "e", "l", "l", "o"]
Spread 語法 (...) 允許在需要零個或多個參數(用於函數調用)或元素(用於數組文字)的地方擴展諸如數組表達式或字符串之類的可迭代對象,或者擴展對象表達式需要零個或多個鍵值對(用於對象文字)的地方。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.