面試題:[…undefined] 輸出什么?
[…undefined] 輸出什么?
...
可以稱之為 展開語法(Spread syntax),又可以叫 擴展運算符??梢栽诤瘮嫡{用/數組構造時, 將數組表達式或者 string
在語法層面展開;還可以在構造字面量對象時, 將對象表達式按 key-value
的方式展開。
首先我們來看數組中常見的用法:
const foo = [1, 2, 3]; const bar = [0, ...foo]; console.log(bar); // [0, 1, 2, 3]
這種方式類似數組中的?slice()
?,將?foo
?淺拷貝到?bar
?數組里。
我們在瀏覽器上輸出結果:
[...undefined] // Uncaught TypeError: undefined is not iterable
看到代碼報錯?Uncaught TypeError
,因為?undefined
?是不可迭代的。那什么可迭代呢,試試普通對象:
[...{}] // Uncaught TypeError: {} is not iterable
從這里可以看出普通對象也不行。在數組或函數參數中使用展開語法時, 該語法只能用于?可迭代對象
。
什么是?可迭代對象
?
要成為可迭代對象, 一個對象必須實現
@@iterator
方法。這意味著對象(或者它原型鏈上的某個對象)必須有一個鍵為@@iterator
的屬性,可通過常量Symbol.iterator
訪問該屬性。
什么意思呢,看一個簡單的例子最直接:
const counter = { *[Symbol.iterator]() { yield1; }, }; [...counter]; // 正常執行,結果: // [1]
一個普通對象中,必須包含一個?Symbol.iterator
?屬性,并規定是一個無參數的函數,其返回值為一個符合?迭代器協議
?的對象。迭代器協議
?屬于?迭代協議
。迭代協議
?并不是新的內置實現或語法,而是協議。這些協議可以被任何遵循某些約定的對象來實現。
什么是迭代器?
只有實現了?迭代器協議
?的一個對象才能成為迭代器。例如:
const counter = { next() { return { value: undefined, done: true }; }, };
迭代器是通過使用?next()
?方法實現?迭代器協議
?的任何一個對象,該方法返回具有兩個屬性的對象:value
,這是序列中的 next 值;和?done
?,如果已經迭代到序列中的最后一個值,則它為 true 。如果?value
?和?done
?一起存在,則它是迭代器的返回值。
可迭代對象
?和?迭代器
?有什么區別呢?
如果實現了?迭代器協議
?和?可迭代協議
,那么它就是一個?可迭代對象
。
const counter = { next() { return { value: undefined, done: true }; }, [Symbol.iterator]() { returnthis; }, };
[Symbol.iterator]()
?通過返回 this 以便調用該方法后獲得一個迭代器,以下方式也許會更好理解:
const counter = { [Symbol.iterator]() { // 返回一個迭代器 return { next() { return { value: undefined, done: true }; }, }; }, };
當一個對象需要被迭代的時候,首先,會不帶參數調用它的?@@iterator
?方法,然后使用此方法返回的迭代器獲得要迭代的值。此函數可以是普通函數,也可以是生成器函數,以便在調用時返回迭代器對象。在此生成器函數的內部,可以使用?yield
?提供每個條目。
如果?@@iterator
?方法不返回迭代器呢?
const counter = { [Symbol.iterator]() { return1; }, }; [...counter]; // Uncaught TypeError: Result of the Symbol.iterator method is not an object
如果一個可迭代對象的?@@iterator
?方法不能返回迭代器對象,那么可以認為它是一個不符合標準的可迭代對象。
生成器(Generator)對象到底是一個迭代器,還是一個可迭代對象?
生成器對象既是迭代器,也是可迭代對象,因為它符合?可迭代協議
?和?迭代器協議
。
function* gen() { yield1; yield2; yield3; } [...gen()]; // [1, 2, 3]
那內置可迭代對象有哪些呢?
目前所有的內置可迭代對象有:
String
Array
TypedArray
Map
Set
arguments
NodeList
它們的原型對象都實現了?@@iterator
?方法。
那想實現?[...obj]
?展開一個普通對象,有辦法嗎?
顧名思義,那就是要將普通對象轉換為?可迭代對象
。
- 展開對象的?
key
:[...Object.keys(obj)]
- 展開對象的?
value
:[...Object.values(obj)]
- 展開整個對象:
[...Object.entries(obj)]
那?{...undefined}
?執行結果是什么?
展開語法在字面量對象的行為細節與數組中有很大差別,可以看作是?Object.assign({}, undefined)
,主要區別是?Object.assign()
?函數會觸發?setters
,而展開語法則不會。
執行結果則是返回一個?{}
?空對象,{...true}
、{...1}
、{...null}
?亦是如此。
{ ...[1, 2, 3] }
?執行結果呢?
首先在對象中不能將?...
?后面當作?可迭代對象
?看待,應該看作是一個普通對象,即包含?key
:
{ ...[1, 2, 3] } // {0: 1, 1: 2, 2: 3}
那請說?...
?語法作為剩余參數的特性
展開語法可以用于函數的最后一個命名參數,它由剩余參數組成的真數組。它與?arguments
?對象的區別主要有三個:
- 剩余參數只包含那些沒有對應形參的實參,而?
arguments
?對象包含了傳給函數的所有實參; arguments
?對象不是一個真正的數組,而剩余參數是真正的?Array
?實例;arguments
?對象還有一些附加的屬性(如?callee
?屬性)。
展開語法還可以用于數組/對象解構,剩余元素必須是數組/對象的最后一個元素。
1. 本站所有文章教程及資源素材均來源于網絡與用戶分享或為本站原創,僅限用于學習和研究。
2. 如果內容損害你的權益請聯系客服QQ:1642748312給予處理。
碼云筆記 » 面試題:[…undefined] 輸出什么?