W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
在 JavaScript 中,很多內(nèi)建函數(shù)都支持傳入任意數(shù)量的參數(shù)。
例如:
Math.max(arg1, arg2, ..., argN)
? —— 返回參數(shù)中的最大值。Object.assign(dest, src1, ..., srcN)
? —— 依次將屬性從 ?src1..N
? 復(fù)制到 ?dest
?。在本章中,我們將學(xué)習(xí)如何編寫支持傳入任意數(shù)量參數(shù)的函數(shù),以及如何將數(shù)組作為參數(shù)傳遞給這類函數(shù)。
在 JavaScript 中,無論函數(shù)是如何定義的,你都可以在調(diào)用它時(shí)傳入任意數(shù)量的參數(shù)。
例如:
function sum(a, b) {
return a + b;
}
alert( sum(1, 2, 3, 4, 5) );
雖然這里這個(gè)函數(shù)不會(huì)因?yàn)閭魅脒^多的參數(shù)而報(bào)錯(cuò)。但是,當(dāng)然,只有前兩個(gè)參數(shù)被求和了。
我們可以在函數(shù)定義中聲明一個(gè)數(shù)組來收集參數(shù)。語法是這樣的:...變量名
,這將會(huì)聲明一個(gè)數(shù)組并指定其名稱,其中存有剩余的參數(shù)。這三個(gè)點(diǎn)的語義就是“收集剩余的參數(shù)并存進(jìn)指定數(shù)組中”。
例如,我們需要把所有的參數(shù)都放到數(shù)組 args
中:
function sumAll(...args) { // 數(shù)組名為 args
let sum = 0;
for (let arg of args) sum += arg;
return sum;
}
alert( sumAll(1) ); // 1
alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6
我們也可以選擇將第一個(gè)參數(shù)獲取為變量,并將剩余的參數(shù)收集起來。
下面的例子把前兩個(gè)參數(shù)獲取為變量,并把剩余的參數(shù)收集到 ?titles
? 數(shù)組中:
function showName(firstName, lastName, ...titles) {
alert( firstName + ' ' + lastName ); // Julius Caesar
// 剩余的參數(shù)被放入 titles 數(shù)組中
// i.e. titles = ["Consul", "Imperator"]
alert( titles[0] ); // Consul
alert( titles[1] ); // Imperator
alert( titles.length ); // 2
}
showName("Julius", "Caesar", "Consul", "Imperator");
Rest 參數(shù)必須放到參數(shù)列表的末尾
Rest 參數(shù)會(huì)收集剩余的所有參數(shù),因此下面這種用法沒有意義,并且會(huì)導(dǎo)致錯(cuò)誤:
function f(arg1, ...rest, arg2) { // arg2 在 ...rest 后面?! // error }
...rest
必須寫在參數(shù)列表最后。
有一個(gè)名為 arguments
的特殊類數(shù)組對象可以在函數(shù)中被訪問,該對象以參數(shù)在參數(shù)列表中的索引作為鍵,存儲(chǔ)所有參數(shù)。
例如:
function showName() {
alert( arguments.length );
alert( arguments[0] );
alert( arguments[1] );
// 它是可遍歷的
// for(let arg of arguments) alert(arg);
}
// 依次顯示:2,Julius,Caesar
showName("Julius", "Caesar");
// 依次顯示:1,Ilya,undefined(沒有第二個(gè)參數(shù))
showName("Ilya");
在過去,JavaScript 中不支持 rest 參數(shù)語法,而使用 arguments
是獲取函數(shù)所有參數(shù)的唯一方法?,F(xiàn)在它仍然有效,我們可以在一些老代碼里找到它。
但缺點(diǎn)是,盡管 arguments
是一個(gè)類數(shù)組,也是可迭代對象,但它終究不是數(shù)組。它不支持?jǐn)?shù)組方法,因此我們不能調(diào)用 arguments.map(...)
等方法。
此外,它始終包含所有參數(shù),我們不能像使用 rest 參數(shù)那樣只截取參數(shù)的一部分。
因此,當(dāng)我們需要這些功能時(shí),最好使用 rest 參數(shù)。
箭頭函數(shù)沒有 ?
"arguments"
?如果我們在箭頭函數(shù)中訪問
arguments
,訪問到的arguments
并不屬于箭頭函數(shù),而是屬于箭頭函數(shù)外部的“普通”函數(shù)。
舉個(gè)例子:
function f() { let showArg = () => alert(arguments[0]); showArg(); } f(1); // 1
我們已經(jīng)知道,箭頭函數(shù)沒有自身的
this
?,F(xiàn)在我們知道了它們也沒有特殊的arguments
對象。
我們剛剛看到了如何從參數(shù)列表中獲取數(shù)組。
有時(shí)候我們也需要做與之相反的事。
例如,內(nèi)建函數(shù) Math.max 會(huì)返回參數(shù)中最大的值:
alert( Math.max(3, 5, 1) ); // 5
如果我們有一個(gè)數(shù)組 [3, 5, 1]
,我們該如何用它調(diào)用 Math.max
呢?
直接“原樣”傳入這個(gè)數(shù)組是不會(huì)奏效的,因?yàn)?nbsp;Math.max
期望的是列表形式的數(shù)值型參數(shù),而不是一個(gè)數(shù)組:
let arr = [3, 5, 1];
alert( Math.max(arr) ); // NaN
毫無疑問,我們不能手動(dòng)地去一一設(shè)置參數(shù) Math.max(arg[0], arg[1], arg[2])
,因?yàn)槲覀儾淮_定這兒有多少個(gè)。在代碼執(zhí)行時(shí),參數(shù)數(shù)組中可能有很多個(gè)元素,也可能一個(gè)都沒有。而且,這樣的代碼也很不優(yōu)雅。
Spread 語法 可以解決這個(gè)問題!它看起來和 rest 參數(shù)很像,也使用 ...
,但是二者的用途完全相反。
當(dāng)在函數(shù)調(diào)用中使用 ...arr
時(shí),它會(huì)把可迭代對象 arr
“展開”到參數(shù)列表中。
以 Math.max
為例:
let arr = [3, 5, 1];
alert( Math.max(...arr) ); // 5(spread 語法把數(shù)組轉(zhuǎn)換為參數(shù)列表)
我們還可以通過這種方式傳入多個(gè)可迭代對象:
let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
alert( Math.max(...arr1, ...arr2) ); // 8
我們甚至還可以將 spread 語法與常規(guī)值結(jié)合使用:
let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
并且,我們還可以使用 spread 語法來合并數(shù)組:
let arr = [3, 5, 1];
let arr2 = [8, 9, 15];
let merged = [0, ...arr, 2, ...arr2];
alert(merged); // 0,3,5,1,2,8,9,15(0,然后是 arr,然后是 2,然后是 arr2)
在上面的示例中,我們使用數(shù)組展示了 spread 語法,其實(shí)我們可以用 spread 語法這樣操作任何可迭代對象。
例如,在這兒我們使用 spread 語法將字符串轉(zhuǎn)換為字符數(shù)組:
let str = "Hello";
alert( [...str] ); // H,e,l,l,o
Spread 語法內(nèi)部使用了迭代器來收集元素,與 for..of
的方式相同。
因此,對于一個(gè)字符串,for..of
會(huì)逐個(gè)返回該字符串中的字符,...str
也同理會(huì)得到 "H","e","l","l","o"
這樣的結(jié)果。隨后,字符列表被傳遞給數(shù)組初始化器 [...str]
。
對于這個(gè)特定任務(wù),我們還可以使用 Array.from
來實(shí)現(xiàn),因?yàn)樵摲椒〞?huì)將一個(gè)可迭代對象(如字符串)轉(zhuǎn)換為數(shù)組:
let str = "Hello";
// Array.from 將可迭代對象轉(zhuǎn)換為數(shù)組
alert( Array.from(str) ); // H,e,l,l,o
運(yùn)行結(jié)果與 [...str]
相同。
不過 Array.from(obj)
和 [...obj]
存在一個(gè)細(xì)微的差別:
Array.from
? 適用于類數(shù)組對象也適用于可迭代對象。因此,對于將一些“東西”轉(zhuǎn)換為數(shù)組的任務(wù),?Array.from
? 往往更通用。
還記得我們 之前講過的 Object.assign()
嗎?
使用 spread 語法也可以做同樣的事情(譯注:也就是進(jìn)行淺拷貝)。
let arr = [1, 2, 3];
let arrCopy = [...arr]; // 將數(shù)組 spread 到參數(shù)列表中
// 然后將結(jié)果放到一個(gè)新數(shù)組
// 兩個(gè)數(shù)組中的內(nèi)容相同嗎?
alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true
// 兩個(gè)數(shù)組相等嗎?
alert(arr === arrCopy); // false(它們的引用是不同的)
// 修改我們初始的數(shù)組不會(huì)修改副本:
arr.push(4);
alert(arr); // 1, 2, 3, 4
alert(arrCopy); // 1, 2, 3
并且,也可以通過相同的方式來復(fù)制一個(gè)對象:
let obj = { a: 1, b: 2, c: 3 };
let objCopy = { ...obj }; // 將對象 spread 到參數(shù)列表中
// 然后將結(jié)果返回到一個(gè)新對象
// 兩個(gè)對象中的內(nèi)容相同嗎?
alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true
// 兩個(gè)對象相等嗎?
alert(obj === objCopy); // false (not same reference)
// 修改我們初始的對象不會(huì)修改副本:
obj.d = 4;
alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4}
alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3}
這種方式比使用 let arrCopy = Object.assign([], arr)
復(fù)制數(shù)組,或使用 let objCopy = Object.assign({}, obj)
復(fù)制對象來說更為簡便。因此,只要情況允許,我們傾向于使用它。
當(dāng)我們在代碼中看到 ?"..."
? 時(shí),它要么是 rest 參數(shù),要么是 spread 語法。
有一個(gè)簡單的方法可以區(qū)分它們:
...
? 出現(xiàn)在函數(shù)參數(shù)列表的最后,那么它就是 rest 參數(shù),它會(huì)把參數(shù)列表中剩余的參數(shù)收集到一個(gè)數(shù)組中。...
? 出現(xiàn)在函數(shù)調(diào)用或類似的表達(dá)式中,那它就是 spread 語法,它會(huì)把一個(gè)數(shù)組展開為列表。使用場景:
我們可以使用這兩種語法輕松地互相轉(zhuǎn)換列表與參數(shù)數(shù)組。
舊式的 ?arguments
?(類數(shù)組且可迭代的對象)也依然能夠幫助我們獲取函數(shù)調(diào)用中的所有參數(shù)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: