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