面試刨根問到底:[...undefined] 執(zhí)行結(jié)果是什么
據(jù)說面試中只有 5% 的人能講透徹,你會(huì)是其中之一嗎?
[...undefined] 輸出什么?
... 可以稱之為 展開語法(Spread syntax),又可以叫 擴(kuò)展運(yùn)算符。可以在函數(shù)調(diào)用/數(shù)組構(gòu)造時(shí), 將數(shù)組表達(dá)式或者 string 在語法層面展開;還可以在構(gòu)造字面量對(duì)象時(shí), 將對(duì)象表達(dá)式按 key-value 的方式展開。
首先我們來看數(shù)組中常見的用法:
const foo = [1, 2, 3];
const bar = [0, ...foo];
console.log(bar);
// [0, 1, 2, 3]
這種方式類似數(shù)組中的 slice() ,將 foo 淺拷貝到 bar 數(shù)組里。
我們?cè)跒g覽器上輸出結(jié)果:
[...undefined]
// Uncaught TypeError: undefined is not iterable
看到代碼報(bào)錯(cuò) Uncaught TypeError,因?yàn)?undefined 是不可迭代的。那什么可迭代呢,試試普通對(duì)象:
[...{}]
// Uncaught TypeError: {} is not iterable
從這里可以看出普通對(duì)象也不行。在數(shù)組或函數(shù)參數(shù)中使用展開語法時(shí), 該語法只能用于 可迭代對(duì)象
什么是 可迭代對(duì)象?
要成為可迭代對(duì)象, 一個(gè)對(duì)象必須實(shí)現(xiàn) @@iterator 方法。這意味著對(duì)象(或者它原型鏈上的某個(gè)對(duì)象)必須有一個(gè)鍵為 @@iterator 的屬性,可通過常量 Symbol.iterator 訪問該屬性
什么意思呢,看一個(gè)簡(jiǎn)單的例子最直接:
const counter = {
*[Symbol.iterator]() {
yield 1;
},
};
[...counter];
// 正常執(zhí)行,結(jié)果:
// [1]
一個(gè)普通對(duì)象中,必須包含一個(gè) Symbol.iterator 屬性,并規(guī)定是一個(gè)無參數(shù)的函數(shù),其返回值為一個(gè)符合 迭代器協(xié)議 的對(duì)象。迭代器協(xié)議 屬于 迭代協(xié)議。迭代協(xié)議 并不是新的內(nèi)置實(shí)現(xiàn)或語法,而是協(xié)議。這些協(xié)議可以被任何遵循某些約定的對(duì)象來實(shí)現(xiàn)。
什么是迭代器?
只有實(shí)現(xiàn)了 迭代器協(xié)議 的一個(gè)對(duì)象才能成為迭代器。例如:
const counter = {
next() {
return { value: undefined, done: true };
},
};
迭代器是通過使用 next() 方法實(shí)現(xiàn) 迭代器協(xié)議 的任何一個(gè)對(duì)象,該方法返回具有兩個(gè)屬性的對(duì)象:value,這是序列中的 next 值;和 done ,如果已經(jīng)迭代到序列中的最后一個(gè)值,則它為 true 。如果 value 和 done 一起存在,則它是迭代器的返回值。
可迭代對(duì)象 和 迭代器 有什么區(qū)別呢?
如果實(shí)現(xiàn)了 迭代器協(xié)議 和 可迭代協(xié)議,那么它就是一個(gè) 可迭代對(duì)象。
const counter = {
next() {
return { value: undefined, done: true };
},
[Symbol.iterator]() {
return this;
},
};
[Symbol.iterator]() 通過返回 this 以便調(diào)用該方法后獲得一個(gè)迭代器,以下方式也許會(huì)更好理解:
const counter = {
[Symbol.iterator]() {
// 返回一個(gè)迭代器
return {
next() {
return { value: undefined, done: true };
},
};
},
};
當(dāng)一個(gè)對(duì)象需要被迭代的時(shí)候,首先,會(huì)不帶參數(shù)調(diào)用它的 @@iterator 方法,然后使用此方法返回的迭代器獲得要迭代的值。此函數(shù)可以是普通函數(shù),也可以是生成器函數(shù),以便在調(diào)用時(shí)返回迭代器對(duì)象。在此生成器函數(shù)的內(nèi)部,可以使用 yield 提供每個(gè)條目。
如果 @@iterator 方法不返回迭代器呢?
const counter = {
[Symbol.iterator]() {
return 1;
},
};
[...counter];
// Uncaught TypeError: Result of the Symbol.iterator method is not an object
如果一個(gè)可迭代對(duì)象的 @@iterator 方法不能返回迭代器對(duì)象,那么可以認(rèn)為它是一個(gè)不符合標(biāo)準(zhǔn)的可迭代對(duì)象。
生成器(Generator)對(duì)象到底是一個(gè)迭代器,還是一個(gè)可迭代對(duì)象?
生成器對(duì)象既是迭代器,也是可迭代對(duì)象,因?yàn)樗?可迭代協(xié)議 和 迭代器協(xié)議。
function* gen() {
yield 1;
yield 2;
yield 3;
}
[...gen()];
// [1, 2, 3]
那內(nèi)置可迭代對(duì)象有哪些呢?
目前所有的內(nèi)置可迭代對(duì)象有:
StringArrayTypedArrayMapSetargumentsNodeList
它們的原型對(duì)象都實(shí)現(xiàn)了 @@iterator 方法。
那想實(shí)現(xiàn) [...obj] 展開一個(gè)普通對(duì)象,有辦法嗎?
顧名思義,那就是要將普通對(duì)象轉(zhuǎn)換為 可迭代對(duì)象。
展開對(duì)象的 key:[...Object.keys(obj)]展開對(duì)象的 value:[...Object.values(obj)]展開整個(gè)對(duì)象: [...Object.entries(obj)]
那 {...undefined} 執(zhí)行結(jié)果是什么?
展開語法在字面量對(duì)象的行為細(xì)節(jié)與數(shù)組中有很大差別,可以看作是 Object.assign({}, undefined),主要區(qū)別是 Object.assign() 函數(shù)會(huì)觸發(fā) setters,而展開語法則不會(huì)
執(zhí)行結(jié)果則是返回一個(gè) {} 空對(duì)象,{...true}、{...1}、{...null} 亦是如此。
{ ...[1, 2, 3] } 執(zhí)行結(jié)果呢?
首先在對(duì)象中不能將 ... 后面當(dāng)作 可迭代對(duì)象 看待,應(yīng)該看作是一個(gè)普通對(duì)象,即包含 key:
{ ...[1, 2, 3] }
// {0: 1, 1: 2, 2: 3}
那請(qǐng)說 ... 語法作為剩余參數(shù)的特性
展開語法可以用于函數(shù)的最后一個(gè)命名參數(shù),它由剩余參數(shù)組成的真數(shù)組。它與 arguments 對(duì)象的區(qū)別主要有三個(gè):
剩余參數(shù)只包含那些沒有對(duì)應(yīng)形參的實(shí)參,而 arguments對(duì)象包含了傳給函數(shù)的所有實(shí)參arguments對(duì)象不是一個(gè)真正的數(shù)組,而剩余參數(shù)是真正的Array實(shí)例arguments對(duì)象還有一些附加的屬性(如callee屬性)
展開語法還可以用于數(shù)組/對(duì)象解構(gòu),剩余元素必須是數(shù)組/對(duì)象的最后一個(gè)元素。
參考資料
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols

長按/掃描添加我!


點(diǎn)點(diǎn)贊

點(diǎn)在看
