我們知道,參數(shù)分為形參(parameter)和實(shí)參(argument),形參是指函數(shù)定義時的參數(shù),實(shí)參是指真正傳入函數(shù)的參數(shù)。下面,我們將從多個角度來分析,ES6中,參數(shù)的一些行為。
參數(shù)的默認(rèn)值####
函數(shù)中參數(shù)的默認(rèn)值這一設(shè)定尤為重要,不僅可以避免一些不必要的錯誤,還能簡化過程,好處很多。下面我們來看看,在ES5中,參數(shù)的默認(rèn)值是如何設(shè)定的:
function foo(num1,num2){
num1 = num1||10;
num2 = num2||5;
console.log(num1,num2);
}
foo(1,2) //1,2
foo(1) //1,5
foo(undefined,2) //10,2
通常是用||運(yùn)算符來做默認(rèn)值處理,判斷傳入的參數(shù)的真假,如果為真,用傳入的參數(shù),為假值,用默認(rèn)的參數(shù)。當(dāng)然,這種方式有一定的漏洞的,比如當(dāng)我們傳入的參數(shù)是0的時候。
foo(0,2) //10,2
就有問題了。其實(shí)得到這個結(jié)果也是意料之中的,0的隱式轉(zhuǎn)換為布爾值的結(jié)果就是false,那么應(yīng)該如何改進(jìn)呢?在ES5中,人們是這么處理的:
function foo(num1,num2){
num1 = num1 !== undefined?num1:10;
num2 = num2 !== undefined?num2:5;
console.log(num1,num2);
}
foo(1,2) //1,2
foo(1) //1,5
foo(undefined,2) //10,2
foo(0,2) //0,2
確實(shí),這樣寫看起來就萬無一失了,但是顯得太臃腫了不是嗎,在ES6中,我們可以直接使用如下方式設(shè)置默認(rèn)值:
function foo(num1=10,num2=5){
console.log(num1,num2);
}
foo(1,2) //1,2
foo(1) //1,5
foo(undefined,2) //10,2
foo(0,2) //0,2
下面,讓我們來思考一個問題,默認(rèn)值到底是如何影響參數(shù)的呢?傳入的參數(shù)我們知道,可以通過arguments對象獲得,那么,如果傳入的參數(shù)在函數(shù)體內(nèi)被改變,那么對應(yīng)的arguments也會改變嗎?請看下面的例子:
function foo(num1,num2){
console.log(num1 === arguments[0]);
console.log(num2 === arguments[1]);
num1 = 'a';
num2 = 'b';
console.log(num1 === arguments[0]);
console.log(num2 === arguments[1]);
}
foo(1,2); //true
//true
//true
//true
由此可以看出,在參數(shù)被修改的時候,arguments也會跟著修改,并且保持與修改的參數(shù)一致。這是在非嚴(yán)格模式下,那么在嚴(yán)格模式下呢?請看如下例子:
'use strict'
function foo(num1,num2){
console.log(num1 === arguments[0]);
console.log(num2 === arguments[1]);
num1 = 'a';
num2 = 'b';
console.log(num1 === arguments[0]);
console.log(num2 === arguments[1]);
}
foo(1,2); //true
//true
//false
//false
正如你所看到的,在嚴(yán)格模式下,arguments并沒有修改。那么,在ES6中的默認(rèn)值呢?看如下例子:
function foo(num1,num2=2){
console.log(arguments.length);
console.log(num1 === arguments[0]);
console.log(num2 === arguments[1]);
num1 = 'a';
num2 = 'b';
console.log(num1 === arguments[0]);
console.log(num2 === arguments[1]);
}
foo(1); //1
//true
//false
//false
//false
在嚴(yán)格模式下:
'use strict'
function foo(num1,num2=2){
console.log(arguments.length);
console.log(num1 === arguments[0]);
console.log(num2 === arguments[1]);
num1 = 'a';
num2 = 'b';
console.log(num1 === arguments[0]);
console.log(num2 === arguments[1]);
}
foo(1); //1
//true
//false
//false
//false
通過arguments.length,傳入的參數(shù)個數(shù)是1,我們可以明白,其實(shí)arguments真正收集的不是默認(rèn)值,而是輸入的參數(shù),并且只要加入了默認(rèn)值,無論是在嚴(yán)格模式,還是非嚴(yán)格模式,參數(shù)arguments都不會改變,只會保留初始值,也就是函數(shù)調(diào)用的時候傳入的最初的值。
不僅如此
ES6默認(rèn)值的用法不僅如此,它不僅可以傳入原始值,還可以是函數(shù)調(diào)用,甚至是參數(shù)本身,具體例子如下:
function add(num1,num2){
return num1+num2
}
function foo(num1=10,num2=add(num1,5)){
console.log(num1,num2);
}
foo(1,2) //1,2
foo(1) //1,6
foo(undefined,1) //10,1
foo() //10,15
當(dāng)然,如果調(diào)用的默認(rèn)值是不存在的,那么會報(bào)錯。比如:
function add1(num1,num2){
return num1+num2
}
function foo(num1=10,num2=add(num1,5)){ //報(bào)錯,ReferenceError,add不存在
console.log(num1,num2);
}
當(dāng)然,還有一種不太明顯的,就是第一個參數(shù)把第二個參數(shù)當(dāng)默認(rèn)值:
function foo(num1=num2,num2=10){
console.log(num1,num2);
}
foo(1,2) //1,2
foo(undefined,2) //報(bào)錯,ReferenceError,num2不存在
因?yàn)闀簳r性死區(qū),在使用num2的時候,并沒有申明,其實(shí)上述的代碼可以大概理解為在函數(shù)內(nèi)部是這樣的:
function foo(num1,num2){
let num1;
num1 = arguments[0] !== undefined?arguments[0]:num2;
let num2;
num2 = arguments[1] !== undefined?arguments[1]:10;
console.log(num1,num2);
}
由此,可以看出,使用num2的時候并沒有申明,所以會報(bào)錯。
還有一點(diǎn)值得注意,函數(shù)參數(shù)的有自己單獨(dú)的作用域和自己的暫時性死區(qū)(TDZ),和函數(shù)本身的作用域是分開的。也就是說,參數(shù)的默認(rèn)值不能使用函數(shù)內(nèi)部申明的變量。
參數(shù)的默認(rèn)值就告一段落了,下面說一個ES6引入的新的東西,作用和arguments類似,但是又不完全相同
剩余參數(shù)(Rest Parameters)####
剩余參數(shù)的申明是使用...3個點(diǎn)加在參數(shù)名前。我們知道,在JavaScript中,參數(shù)的個數(shù),傳入的多少并不重要,需求2個參數(shù),但是傳入了3個參數(shù),那么最后一個參數(shù)就會被舍去;需求2個參數(shù),但是傳入了1個參數(shù),第二個參數(shù)就是undefined,這個本身不會報(bào)錯。那么剩余參數(shù)的作用就是類似于arguments,把剩下的參數(shù)以數(shù)組的形式收集起來,具體操作如下:
function foo(num1,...num2){
console.log(num2);
}
foo(0,1,2,3,4,5); //[1,2,3,4,5]
正如你說看到的,num1是0,剩下的參數(shù)1,2,3,4,5全部被num2以數(shù)組的形式收集了起來。
剩余參數(shù)的概念并不復(fù)雜,但是還是需要注意2點(diǎn):
-
剩余參數(shù)后面不能再跟參數(shù),否則會報(bào)錯:
function foo(num1,...num2,num3){ //報(bào)錯,SyntaxError,后面不能再跟參數(shù)
console.log(num2);
}
也就是說,剩余參數(shù)只能是函數(shù)的最后一個參數(shù)。
-
剩余參數(shù)不能運(yùn)用在對象的setter方法中。例如:
let a = {
set name(...value) { //報(bào)錯,SyntaxError
// do something
}
}
至于理由,很簡單,因?yàn)?code>setter方法本身只能接受單個數(shù),而剩余參數(shù)是一個數(shù)組,不符合要求,當(dāng)然會報(bào)錯。
最后再講一點(diǎn),如果一個函數(shù)的參數(shù)只有剩余參數(shù),那么此時,剩余參數(shù)和arguments表達(dá)的差不多是一個意思,只不過剩余參數(shù)是一個更純粹的數(shù)組。arguments還背負(fù)著自己的歷史使命,比如callee,之類的。