溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JavaScript函數(shù)中的參數(shù)怎么應(yīng)用

發(fā)布時(shí)間:2022-08-04 09:42:15 來(lái)源:億速云 閱讀:95 作者:iii 欄目:web開(kāi)發(fā)

這篇文章主要介紹了JavaScript函數(shù)中的參數(shù)怎么應(yīng)用的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇JavaScript函數(shù)中的參數(shù)怎么應(yīng)用文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。

JavaScript函數(shù)中的參數(shù)怎么應(yīng)用

一、函數(shù)的形參和實(shí)參

函數(shù)的參數(shù)會(huì)出現(xiàn)在兩個(gè)地方,分別是函數(shù)定義處和函數(shù)調(diào)用處,這兩個(gè)地方的參數(shù)是有區(qū)別的。

  • 形參(形式參數(shù))

    在函數(shù)定義中出現(xiàn)的參數(shù)可以看做是一個(gè)占位符,它沒(méi)有數(shù)據(jù),只能等到函數(shù)被調(diào)用時(shí)接收傳遞進(jìn)來(lái)的數(shù)據(jù),所以稱為形式參數(shù),簡(jiǎn)稱形參。

  • 實(shí)參(實(shí)際參數(shù))

    函數(shù)被調(diào)用時(shí)給出的參數(shù)包含了實(shí)實(shí)在在的數(shù)據(jù),會(huì)被函數(shù)內(nèi)部的代碼使用,所以稱為實(shí)際參數(shù),簡(jiǎn)稱實(shí)參。

形參和實(shí)參的區(qū)別和聯(lián)系

  • 1) 形參變量只有在函數(shù)被調(diào)用時(shí)才會(huì)分配內(nèi)存,調(diào)用結(jié)束后,立刻釋放內(nèi)存,所以形參變量只有在函數(shù)內(nèi)部有效,不能在函數(shù)外部使用。

  • 2) 實(shí)參可以是常量、變量、表達(dá)式、函數(shù)等,無(wú)論實(shí)參是何種類型的數(shù)據(jù),在進(jìn)行函數(shù)調(diào)用時(shí),它們都必須有確定的值,以便把這些值傳送給形參,所以應(yīng)該提前用賦值、輸入等辦法使實(shí)參獲得確定值。

  • 3) 實(shí)參和形參在數(shù)量上、類型上、順序上必須嚴(yán)格一致,否則會(huì)發(fā)生“類型不匹配”的錯(cuò)誤。當(dāng)然,如果能夠進(jìn)行自動(dòng)類型轉(zhuǎn)換,或者進(jìn)行了強(qiáng)制類型轉(zhuǎn)換,那么實(shí)參類型也可以不同于形參類型。

  • 4) 函數(shù)調(diào)用中發(fā)生的數(shù)據(jù)傳遞是單向的,只能把實(shí)參的值傳遞給形參,而不能把形參的值反向地傳遞給實(shí)參;換句話說(shuō),一旦完成數(shù)據(jù)的傳遞,實(shí)參和形參就再也沒(méi)有瓜葛了,所以,在函數(shù)調(diào)用過(guò)程中,形參的值發(fā)生改變并不會(huì)影響實(shí)參。

  • 5) 形參和實(shí)參雖然可以同名,但它們之間是相互獨(dú)立的,互不影響,因?yàn)閷?shí)參在函數(shù)外部有效,而形參在函數(shù)內(nèi)部有效。

形參和實(shí)參的功能是傳遞數(shù)據(jù),發(fā)生函數(shù)調(diào)用時(shí),實(shí)參的值會(huì)傳遞給形參。

二、參數(shù)傳遞

函數(shù)允許我們將數(shù)據(jù)傳遞進(jìn)去,通過(guò)傳遞的數(shù)據(jù)從而影響函數(shù)執(zhí)行結(jié)果,使函數(shù)更靈活、復(fù)用性更強(qiáng)。

function foo(a, b) {
    console.log([a, b]);
}

foo(1, 2); // 輸出 [1, 2]

這個(gè)例子中,ab 屬于函數(shù)中的局部變量,只能在函數(shù)中訪問(wèn)。調(diào)用函數(shù)時(shí),傳遞的數(shù)據(jù)會(huì)根據(jù)位置來(lái)匹配對(duì)應(yīng),分別賦值給 ab。

創(chuàng)建函數(shù)時(shí),function 函數(shù)名 后面括號(hào)中設(shè)定的參數(shù)被稱為形參;調(diào)用函數(shù)時(shí),函數(shù)名后面括號(hào)中傳入的參數(shù)被稱為實(shí)參。上面例子中,ab 是形參,傳入的 12 是實(shí)參。

因?yàn)樾螀⑹且崖暶鞯淖兞?,所以不能再?letconst 重復(fù)聲明。

function foo(a, b) {
    let a = 1; // 報(bào)錯(cuò),a 已聲明
    const b = 1; // 報(bào)錯(cuò),b 已聲明
}

JavaScript 中所有函數(shù)傳遞都是按值傳遞的,不會(huì)按引用傳遞。所謂的值,就是指直接保存在變量上的值,如果把對(duì)象作為參數(shù)傳遞,那么這個(gè)值就是這個(gè)對(duì)象的引用,而不是對(duì)象本身。這里實(shí)際上是一個(gè)隱式的賦值過(guò)程,所以給函數(shù)傳遞參數(shù)時(shí),相當(dāng)于從一個(gè)變量賦值到另一個(gè)變量。

原始值:

function add(num) {
    return num + 1;
}

let count = 5;
let result = add(count); // 此處參數(shù)傳遞的過(guò)程可以看作是 num = count

console.log(count); // 5
console.log(result); // 6

引用值:

function setName(obj) {
    obj.name = "小明";
}

let person = {};

setName(person); // 此處參數(shù)傳遞的過(guò)程可以看作是 obj = person;
console.log(person); // {name: "小明"}

三、理解參數(shù)

JavaScript 中的函數(shù)既不會(huì)檢測(cè)參數(shù)的類型,也不會(huì)檢測(cè)傳入?yún)?shù)的個(gè)數(shù)。定義函數(shù)時(shí)設(shè)置兩個(gè)形參,不意味著調(diào)用時(shí)必須傳入兩個(gè)參數(shù)。實(shí)際調(diào)用時(shí)不管是傳了一個(gè)還是三個(gè),甚至不傳參數(shù)也不會(huì)報(bào)錯(cuò)。

所有函數(shù)(非箭頭)中都有一個(gè)名為 arguments 的特殊的類數(shù)組對(duì)象(不是 Array 的實(shí)例),它保存著所有實(shí)參的副本,我們可以通過(guò)它按照數(shù)組的索引訪問(wèn)方式獲取所有實(shí)參的值,也可以訪問(wèn)它的 arguments.length 屬性來(lái)確定函數(shù)實(shí)際調(diào)用時(shí)傳入的參數(shù)個(gè)數(shù)。

例如:

function foo(a, b) {
	console.log(arguments[0]);
    console.log(arguments[1]);
    console.log(arguments.length);
}

foo(10, 20); // 依次輸出 10、20、2

上面例子中,foo() 函數(shù)的第一個(gè)參數(shù)是 a,第二個(gè)參數(shù)是b ,可以通過(guò) arguments[x] 的方式來(lái)分別獲取同樣的值 。因此,你甚至可以在聲明函數(shù)時(shí)不設(shè)置形參。

function foo() {
	console.log(arguments[0]);
    console.log(arguments[1]);
}

foo(10, 20); // 依次輸出 10、20

由此可見(jiàn),JavaScript 函數(shù)的形參只是方便使用才寫出來(lái)的。想傳多少個(gè)參數(shù)都不會(huì)產(chǎn)生錯(cuò)誤。

還有一個(gè)要注意的是,arguments 可以跟形參一起使用,并且 arguments 對(duì)象中的值會(huì)和對(duì)應(yīng)的形參保持同步。例如:

function foo(a) {
	arguments[0] ++;
    console.log(a);
}

foo(10); // 輸出 11
//------------------------------------
function foo2(a) {
	a++;
    console.log(arguments[0]);
}

foo2(10); // 輸出 11

當(dāng)修改 arguments[0] 或 a 的值時(shí),另一個(gè)也被改變了。這并不意味著它們?cè)L問(wèn)同一個(gè)內(nèi)存地址,畢竟我們傳入的是一個(gè)原始值。它們?cè)趦?nèi)存中還是分開(kāi)的,只是由于內(nèi)部的機(jī)制使它們的值保持了同步。

另外,如果缺少傳參,那這個(gè)形參的值就不會(huì)和 arguments 對(duì)象中的對(duì)應(yīng)值進(jìn)行同步。例如下面這個(gè)例子,只傳了一個(gè)參數(shù),那么arguments 中只有一個(gè)實(shí)參值,這時(shí)候在函數(shù)中把 arguments[1] 設(shè)置為某個(gè)值,這個(gè)值并不會(huì)同步給第二個(gè)形參,例如:

function foo(a,b) {
    arguments[1] = 2;
    console.log(b);
}

foo(1); // 輸出 undefined

這個(gè)例子中,形參 b 沒(méi)有傳入實(shí)參,它的值會(huì)默認(rèn)為 undefined。但如果:

foo(1, undefined); // 輸出 2

手動(dòng)傳入 undefined 時(shí), arguments 數(shù)組中會(huì)出現(xiàn)一個(gè)值為 undefined 的元素,依然能和 b 的值進(jìn)行同步。

嚴(yán)格模式下,arguments 對(duì)象中的值和形參不會(huì)再同步,當(dāng)然,如果傳入的是引用值,它們依然會(huì)互相影響,但這只是引用值的特性而已。因此,在開(kāi)發(fā)中最好不要依賴這種同步機(jī)制,也就是說(shuō)不要同時(shí)使用形參和它在arguments 對(duì)象中的對(duì)應(yīng)值。

箭頭函數(shù)中沒(méi)有 arguments

如果函數(shù)是使用箭頭語(yǔ)法定義的,那么函數(shù)中是沒(méi)有 arguments 對(duì)象的,只能通過(guò)定義的形參來(lái)訪問(wèn)。

let foo = () => {
    console.log(arguments[0]);
}foo(); // 報(bào)錯(cuò),arguments 未定義

在某些情況可能會(huì)訪問(wèn)到 arguments

function fn1(){
    let fn2 = () => {
    	console.log(arguments[0]);
    }
    
    fn2();
}fn1(5);

但這個(gè) arguments,并不是箭頭函數(shù)的,而是屬于外部普通函數(shù)的,當(dāng)箭頭函數(shù)中訪問(wèn) arguments 時(shí),順著作用域鏈找到了外部函數(shù)的arguments

四、將對(duì)象屬性用作實(shí)參

當(dāng)一個(gè)函數(shù)包含的形參有多個(gè)時(shí),調(diào)用函數(shù)就成了一種麻煩,因?yàn)槟憧偸且WC傳入的參數(shù)放在正確的位置上,有沒(méi)有辦法解決傳參順序的限制呢?

由于對(duì)象屬性是無(wú)序的,通過(guò)屬性名來(lái)確定對(duì)應(yīng)的值。因此可以通過(guò)傳入對(duì)象的方式,以對(duì)象中的屬性作為真正的實(shí)參,這樣參數(shù)的順序就無(wú)關(guān)緊要了。

function foo(obj) {
    console.log(obj.name, obj.sex, obj.age);
}

foo({ sex: '男', age: 18, name: '小明' }); // 小明 男 18

五、參數(shù)默認(rèn)值

如果調(diào)用函數(shù)時(shí)缺少提供實(shí)參,那么形參默認(rèn)值為 undefined

有時(shí)候我們想要設(shè)置特定的默認(rèn)值,在 ES6 之前還不支持顯式地設(shè)置默認(rèn)值的時(shí)候,只能采用變通的方式:

function sayHi(name) {
    name = name || 'everyone';
    
	console.log( 'Hello ' + name + '!');
}

sayHi(); // 輸出 'Hello everyone!'

通過(guò)檢查參數(shù)值的方式判斷有沒(méi)有賦值,上面的做法雖然簡(jiǎn)便,但缺點(diǎn)在于如果傳入的實(shí)參對(duì)應(yīng)布爾值為 false ,實(shí)參就不起作用了。需要更精確的話可以用 if 語(yǔ)句或者三元表達(dá)式,判斷參數(shù)是否等于 undefined,如果是則說(shuō)明這個(gè)參數(shù)缺失 :

// if 語(yǔ)句判斷
function sayHi(name) {
	if (name === undefined) {
		name = 'everyone';
	}
    
	console.log( 'Hello ' + name + '!');
}

// 三元表達(dá)式判斷
function sayHi(name) {
	name =  (name !== undefined) ? name : 'everyone';
	
    console.log( 'Hello ' + name + '!');
}

ES6 就方便了許多,因?yàn)樗С至孙@式的設(shè)置默認(rèn)值的方式,就像這樣:

function sayHi(name = 'everyone') { // 定義函數(shù)時(shí),直接給形參賦值
	console.log( 'Hello ' + name + '!');
}

sayHi(); // 輸出 'Hello everyone!' 
sayHi('Tony'); // 輸出 'Hello Tony!' 
sayHi(undefined); // 輸出 'Hello everyone!'

這些結(jié)果表明了,它也是通過(guò)參數(shù)是否等于 undefined 來(lái)判定參數(shù)是否缺失的。

默認(rèn)值不但可以是一個(gè)值,它還可以是任意合法的表達(dá)式,甚至是函數(shù)調(diào)用:

function sayHi(name = 'every'+'one') {
	console.log( 'Hello ' + name + '!');
}

sayHi(); // 輸出 'Hello everyone!' 
//--------------------------------------
function foo() {
    console.log('調(diào)用foo');
    return 'Tony';
}

function sayHi(name = foo()) {
	console.log( 'Hello ' + name + '!');
}
		  
sayHi(); // 輸出 '調(diào)用foo'
         // 輸出 'Hello Tony!' 

sayHi(undefined); // 輸出 '調(diào)用foo'
                  // 輸出 'Hello Tony!' 

sayHi('John'); // 輸出 'Hello John!'

可以看到,函數(shù)參數(shù)的默認(rèn)值只有在函數(shù)調(diào)用時(shí),參數(shù)的值缺失或者是 undefined 才會(huì)求值,不會(huì)在函數(shù)定義時(shí)求值。

參數(shù)默認(rèn)值的位置

通常我們給參數(shù)設(shè)置默認(rèn)值,是為了調(diào)用函數(shù)時(shí)可以適當(dāng)省略參數(shù)的傳入,這里要注意的是,有多個(gè)參數(shù)時(shí),設(shè)置了默認(rèn)值的參數(shù)如果不是放在尾部,實(shí)際上它是無(wú)法省略的。

function fn(x = 1, y) {
	console.log([x, y]);
}

fn(); // 輸出 [1, undefined]
fn(2); // 輸出 [2, undefined]
fn(, 2); // 報(bào)錯(cuò),語(yǔ)法錯(cuò)誤(這里不支持像數(shù)組那樣的空槽)
fn(undefined, 2); // 輸出 [1, 2] (那還不如傳個(gè) 1 方便呢!)

上面例子中,給形參 x 設(shè)置的默認(rèn)值就顯得沒(méi)有任何意義了。因此,設(shè)置默認(rèn)值的參數(shù)放在尾部是最好的做法:

function fn(x, y = 2) {
	console.log([x, y]);
}

fn(); // 輸出 [undefined, 2]
fn(1); // 輸出 [1, 2]
fn(1, 1) // 輸出 [1, 1]

參數(shù)的省略問(wèn)題

在多個(gè)參數(shù)設(shè)置了默認(rèn)值的情況下,那么問(wèn)題又來(lái)了,你并不能省略比較靠前的參數(shù),而只給最后的一個(gè)參數(shù)傳入實(shí)參。

function fn(x, y = 2, z = 3) {
	console.log([x, y, z]);
}

fn(1, , 10) // 報(bào)錯(cuò)

前面我們知道,可以通過(guò)傳入對(duì)象的這種方式去避免參數(shù)順序的限制。那參數(shù)默認(rèn)值如何實(shí)現(xiàn)呢?用 || 、 if 語(yǔ)句或者三元表達(dá)式去判斷也是解決辦法,但這樣就顯得有些落后了。接下來(lái)要討論的是另外兩種 ES6 中的全新方式。

參數(shù)默認(rèn)值和 Object.assign() 結(jié)合使用

function fn(obj = {}) {
    let defaultObj = {
        x: undefined,
        y: 2,
        z: 3
    }
    
    let result = Object.assign(defaultObj, obj);
    
	console.log([result.x, result.y, result.z]);
}

fn(); // 輸出 [undefined, 2, 3]
fn({ x: 1, z: 10 }); // 輸出 [1, 2, 10]

上面的例子中,在函數(shù)中定義了一個(gè)對(duì)象 defaultObj ,變通地利用其中的屬性作為參數(shù)的默認(rèn)值,然后利用 Object.assagin() 把傳入的對(duì)象和默認(rèn)對(duì)象進(jìn)行合并,defaultObj 中的屬性會(huì)被 obj 的相同屬性覆蓋,obj 中如果有其他屬性會(huì)分配給 defaultObj 。這里用一個(gè)變量接收返回的合并對(duì)象。

同時(shí)形參 obj 也設(shè)置了默認(rèn)值為一個(gè)空對(duì)象,防止函數(shù)調(diào)用時(shí)不傳任何參數(shù),因?yàn)檫@會(huì)導(dǎo)致 Object.assign() 接收的第二個(gè)參數(shù)是 undefined ,從而產(chǎn)生報(bào)錯(cuò)。

參數(shù)默認(rèn)值和解構(gòu)賦值結(jié)合使用

函數(shù)調(diào)用時(shí),實(shí)參和形參的匹配實(shí)際上是一個(gè)隱式的賦值過(guò)程,所以,參數(shù)傳遞也可以進(jìn)行解構(gòu)賦值:

function fn({ x, y = 2, z = 3 }) {
    console.log([x, y, z]);
}

fn({}); // 輸出 [undefined, 2, 3]
fn({ x: 1, z: 10 }); // 輸出 [1, 2, 10]

在這個(gè)例子中,使用的只是對(duì)象的解構(gòu)賦值默認(rèn)值,還沒(méi)有使用函數(shù)參數(shù)的默認(rèn)值。如果函數(shù)調(diào)用時(shí)不傳任何參數(shù),也會(huì)產(chǎn)生報(bào)錯(cuò),因?yàn)檫@導(dǎo)致了參數(shù)初始化時(shí)解構(gòu)賦值失敗,相當(dāng)于執(zhí)行了 {x, y = 2, z = 3} = undefined 這樣的代碼。

同樣的,你可以利用參數(shù)默認(rèn)值的語(yǔ)法,給 {x, y = 2, z = 3} 設(shè)置一個(gè)默認(rèn)的解構(gòu)對(duì)象,使得不傳參函數(shù)也能夠順利執(zhí)行:

function fn({ x, y = 2, z = 3 } = {}) {
    console.log([x, y, z]);
}

fn(); // 輸出 [undefined, 2, 3]

這里出現(xiàn)了雙重的默認(rèn)值,可能有些繞,那么用一段偽代碼來(lái)解釋以上的參數(shù)初始化過(guò)程就是:

if( 實(shí)參 === {...} ) { // 當(dāng) fn({...});     
    { x, y = 2, z = 3 } = {...};
                        
} else if ( 實(shí)參 === undefined ){ // 當(dāng) fn();
    { x, y = 2, z = 3 } = {};

}

雙重默認(rèn)值有一點(diǎn)細(xì)節(jié)需要特別注意,就是解構(gòu)賦值默認(rèn)值和函數(shù)參數(shù)默認(rèn)值的差別,看下面例子:

function fn ({ x = 1 } = {}, { y } = { y: 2 }){
    console.log(x, y);
}

fn(); // 輸出 1 2
fn({ x: 10 }, { y: 20 }); // 輸出 10 20
fn({},{}); // 1 undefined

這個(gè)函數(shù)中,有兩組參數(shù)采用了解構(gòu)賦值的方式,看似 x 和 y 都設(shè)置了默認(rèn)值,雖然是不同的兩種形式,但顯然不是任何情況下結(jié)果都相同的。當(dāng)傳入的參數(shù)是{}時(shí),y 并沒(méi)有獲取到默認(rèn)值 2 ,為什么會(huì)這樣呢?結(jié)合前面的偽代碼例子來(lái)看:

fn({ x: 10 }, { y: 20 }); // 初始化時(shí): { x = 1 } = { x: 10 }, { y } = { y: 20 }

fn({},{}); // 初始化時(shí): { x = 1 } = {}, { y } = {}

當(dāng)傳入的參數(shù)是{}時(shí),函數(shù)參數(shù)沒(méi)有缺失也不是 undefined ,所以函數(shù)參數(shù)默認(rèn)值是不起作用的。同時(shí) {} 里面也沒(méi)有 x 和 y 的對(duì)應(yīng)值,x 得到的 1 是解構(gòu)賦值默認(rèn)值,而 y 由于沒(méi)有設(shè)置解構(gòu)賦值默認(rèn)值,所以它默認(rèn)是 undefined。

參數(shù)默認(rèn)值的作用域與暫時(shí)性死區(qū)

還有一個(gè)小細(xì)節(jié),一旦有參數(shù)設(shè)置了默認(rèn)值,那么它們會(huì)形成自己的作用域(包裹在(...)中),因此不能引用函數(shù)體中的變量:

function foo(a = b) {
    let b = 1;
}

foo(); // 報(bào)錯(cuò),b 未定義

但這個(gè)作用域只是臨時(shí)的,參數(shù)初始化完畢后,這個(gè)作用域就不存在了。

它也符合普通作用域的規(guī)則:

let b = 2;

function foo(a = b) {
    let b = 1;
    return a;
}

foo(); // 2

上面例子中,存在一個(gè)全局變量 b,那么形參 a 會(huì)獲取到全局變量 b 的值。

當(dāng)然,如果形參作用域中存在一個(gè)形參 b 的話,它優(yōu)先獲取到的是當(dāng)前作用域的:

let b = 2;

function foo(b = 3 ,a = b) {
    return a;
}

foo(); // 3

給多個(gè)參數(shù)設(shè)置默認(rèn)值,它們會(huì)按順序初始化的,遵循“暫時(shí)性死區(qū)”的規(guī)則,即前面的參數(shù)不能引用后面的參數(shù):

function foo(a = b, b = 2) {
    return a + b;
}

foo(); // 報(bào)錯(cuò),b 在初始化之前不能訪問(wèn)

六、參數(shù)的收集與展開(kāi)

剩余參數(shù)

ES6 提供了**剩余參數(shù)(rest)**的語(yǔ)法(...變量名),它可以收集函數(shù)多余的實(shí)參(即沒(méi)有對(duì)應(yīng)形參的實(shí)參),這樣就不再需要使用 arguments 對(duì)象來(lái)獲取了。形參使用了 ... 操作符會(huì)變成一個(gè)數(shù)組,多余的實(shí)參都會(huì)被放進(jìn)這個(gè)數(shù)組中。

剩余參數(shù)基本用法:

function sum(a, ...values) {
 
    for (let val of values) {
        a += val;
    }
    
    return a;
}

sum(0, 1, 2, 3); // 6

上面例子中,在參數(shù)初始化時(shí),首先根據(jù)參數(shù)位置進(jìn)行匹配,把 0 賦值給 a ,然后剩余的參數(shù) 1、2、3 都會(huì)被放進(jìn)數(shù)組 values 中。

下面是分別用 arguments 對(duì)象和剩余參數(shù)來(lái)獲取參數(shù)的對(duì)比例子:

// arguments 的寫法
function sortNumbers() {
	return Array.prototype.slice.call(arguments).sort();
}

// 剩余參數(shù)的寫法
const sortNumbers = (...numbers) => {
    return numbers.sort();
}

可以看出剩余參數(shù)的寫法更加簡(jiǎn)潔。盡管 arguments 是一個(gè)類數(shù)組,也是可迭代對(duì)象,但它終究不是數(shù)組。它不支持?jǐn)?shù)組方法,當(dāng)我們使用 arguments 時(shí),如果想要調(diào)用數(shù)組方法,就必須使用Array.prototype.slice.call先將其轉(zhuǎn)為數(shù)組。

而剩余參數(shù)它不同于 arguments 對(duì)象,它是真正的 Array 實(shí)例,能夠很方便地使用數(shù)組方法。并且箭頭函數(shù)也支持剩余參數(shù)。

另外,使用剩余參數(shù)不會(huì)影響 arguments 對(duì)象的功能,它仍然能夠反映調(diào)用函數(shù)時(shí)傳入的參數(shù)。

  • 剩余參數(shù)的位置

剩余參數(shù)必須是最后一個(gè)形參,否則會(huì)報(bào)錯(cuò)。

// 報(bào)錯(cuò)
function fn1(a, ...rest, b) {
	console.log([a, b, rest]);
} 

// 正確寫法
function fn2(a, b, ...rest) {
    console.log([a, b, rest]);
}

fn2(1, 2, 3, 4) // 輸出 [1, 2, [3, 4]]

展開(kāi)語(yǔ)法

前面我們知道了如何把多余的參數(shù)收集為一個(gè)數(shù)組,但有時(shí)候我們需要做一些相反的事,例如要把一個(gè)數(shù)組中的元素分別傳入給某個(gè)函數(shù),而不是傳入一個(gè)數(shù)組,像這樣:

function sum(...values) {
    let sum = 0;
    
    for (let val of values) {
        sum += val;
    }
    
    return sum;
}

let arr = [1, 2, 3, 4];

sum(arr); // "01,2,3,4"

上面例子的函數(shù)會(huì)把所有傳進(jìn)來(lái)的數(shù)值累加,如果直接傳入一個(gè)數(shù)組,就得不到我們想要的結(jié)果。

例子中傳入一個(gè)數(shù)組, values 的值會(huì)變成 [[1, 2, 3, 4]],導(dǎo)致數(shù)組 values 中只有一個(gè)元素,而這個(gè)元素的類型是數(shù)組。那么函數(shù)返回值就是數(shù)值 0 和數(shù)組 [1, 2, 3, 4]相加的結(jié)果了,兩者各自進(jìn)行了類型的隱式轉(zhuǎn)換變成字符串,然后再相加,是一個(gè)字符串拼接的效果。

要實(shí)現(xiàn)把數(shù)組拆解傳入給函數(shù),首先不可能一個(gè)個(gè)傳入?yún)?shù)——sum(arr[0], arr[1], arr[2], arr[3]);,因?yàn)椴皇侨魏螘r(shí)候都知道數(shù)組中有多少個(gè)元素的,而且數(shù)組中可能會(huì)非常多的元素,手動(dòng)傳是不明智的。

比較可行的是借助 apply() 方法:

sum.apply(null, arr); // 10

但這還不是最優(yōu)解,那么重點(diǎn)來(lái)了!

ES6 新增的**展開(kāi)語(yǔ)法(spread)**可以幫助我們面對(duì)這種情況。它也是使用 ...變量名 的語(yǔ)法,雖然跟剩余參數(shù)語(yǔ)法一樣,但是用途完全相反,它能夠把一個(gè)可迭代對(duì)象拆分成逗號(hào)分隔的參數(shù)序列。

在函數(shù)調(diào)用時(shí),它的應(yīng)用是這樣子的:

sum(...arr); // 10

// 相當(dāng)于 sum(1,2,3,4);

它甚至可以隨意搭配常規(guī)值使用,沒(méi)有前后位置限制,還可以同時(shí)傳入多個(gè)可迭代對(duì)象:

sum(-1, ...arr); // 9
sum(...arr, 5); // 15
sum(-1, ...arr, 5); // 14
sum(-1, ...arr, ...[5, 6, 7]); // 27

展開(kāi)操作符 ... 相當(dāng)于替我們完成了手動(dòng)分別傳參的操作,函數(shù)只知道接收的實(shí)參是單獨(dú)的一個(gè)個(gè)值,不會(huì)因?yàn)檎归_(kāi)操作符的存在而產(chǎn)生其他影響。

上面的示例雖然都是針對(duì)于數(shù)組的,但展開(kāi)語(yǔ)法能做的還不止這些,其他可迭代對(duì)象例如字符串、字面量對(duì)象都可以展開(kāi),深入了解請(qǐng)參見(jiàn) → 展開(kāi)語(yǔ)法

總結(jié)

  • 形參是函數(shù)中已聲明的局部變量,傳遞給函數(shù)的實(shí)參會(huì)被賦值給形參,函數(shù)參數(shù)傳遞實(shí)際上是一個(gè)隱式的賦值過(guò)程。

  • 形參和實(shí)參的數(shù)量可以不相等:

    ● 缺失實(shí)參的形參會(huì)得到默認(rèn)值 undefined。

    ● 額外的實(shí)參,可以通過(guò) arguments 對(duì)象訪問(wèn),箭頭函數(shù)除外。

  • 可以通過(guò)傳入對(duì)象的方式讓傳參順序不再重要,讓對(duì)象中的屬性作為真正的實(shí)參。

  • ES6 的參數(shù)默認(rèn)值——函數(shù)調(diào)用時(shí)參數(shù)的值缺失或者是 undefined ,才會(huì)獲取默認(rèn)值。

    ● 設(shè)置默認(rèn)值的形參只有放在最后一位才可以省略傳參。

    ● 形參設(shè)置默認(rèn)值不能引用函數(shù)體中的變量,但可以引用前面的形參和外部變量。

    ● 通過(guò) Object.assign() 或者解構(gòu)賦值實(shí)現(xiàn)默認(rèn)值,能讓傳參的方式更加靈活。

  • 剩余參數(shù)和 arguments 的主要區(qū)別:

    ● 剩余參數(shù)只包含那些沒(méi)有對(duì)應(yīng)形參的實(shí)參,而 arguments 對(duì)象包含了傳給函數(shù)的所有實(shí)參。

    ● 剩余參數(shù)是真正的 Array 實(shí)例,而 arguments 只是類數(shù)組對(duì)象。

  • 剩余參數(shù)和展開(kāi)語(yǔ)法都采用 ... 操作符,在函數(shù)的相關(guān)場(chǎng)景中:

    ● 出現(xiàn)在函數(shù)形參列表的最后,它是剩余參數(shù)。

    ● 出現(xiàn)在函數(shù)調(diào)用時(shí),它是展開(kāi)語(yǔ)法。

關(guān)于“JavaScript函數(shù)中的參數(shù)怎么應(yīng)用”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“JavaScript函數(shù)中的參數(shù)怎么應(yīng)用”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI