當函數調用函數時候傳入的實參比函數聲明時候制定的形參要少時候,剩余的形參就設置成了undefined。例如
function getPropertyNames(o,/*optional*/a){
var a = a||[];
for(var property in o){
a.push(property);
}
return a;
}
getPropertyNames({x:1,y:2,z:3}); //["x", "y", "z"]
getPropertyNames({x:1,y:2,z:3},[1,2,3]); //[1, 2, 3, "x", "y", "z"]
當函數調用函數時候傳入的實參比函數聲明時候制定的形參要多時候,我們可以考慮實參對象,即arguments對象。
一、先來看看到底是個啥?
arguments對象在JS中應該算數比較特殊的對象。不能顯示的創建,只在當前函數調用的時候才可以使用,長得有點像數組,但絕對不是Array的實例。

幾點說明:
1.1、arguments實際上是當前函數的一個內置屬性,在當前函數內,函數名.arguments等價於arguments,在函數外單獨使用arguments是沒有意義的;
1.2、arguments對象的長度由實際參數個數決定。

1.3、函數中的形參和實參是可以相互影響的。
可以看出,當傳入的實參個數少於形參個數的時候,以實參為准。
實際上,函數的實參已經存入到arguments對象中了。
從上面可以看出,arguments對象確實長得像數組,但是並不是正在的數組,其實是個array-like object。
顧名思義,就是像數組的對象,當然,數組本身就是對象嘛!稍微有點基礎的同學,一定知道 arguments 就是 Array-Like Objects 的一種,能像數組一樣用 [] 去訪問 arguments 的元素,有 length 屬性,但是卻不能用一些數組的方法,如 push,pop,等等。
二、怎么把這個arrat-like object編程real array?
Array.prototype.slice.call(arguments);
[].slice.call(arguments);//推薦做法
可以怎么理解上面的方法,arguments本身不是數組,所以沒有數組的slice方法,但是它里面有length屬性,這樣可以用call()方法強行調用Array原型上的方法。關於call()的解釋,稍后簡單說一下。
vue源碼中是這么做的:
/**
* Convert an Array-like object to a real Array.
*/
function toArray (list, start) {
start = start || 0;
var i = list.length - start;
var ret = new Array(i);
while (i--) {
ret[i] = list[i + start];
}
return ret
}
二、那我們可以怎么使用這個arguments對象呢?
2.1、檢查傳入的參數數量是否正確?
function func1(a,b,c){
if(arguments.length!=3){
trow new Error('參數數量不正確')
}
/*****dosomthing*******/
}
以此類推,我們還可以限制傳入參數的類型。
2.2 模擬實現重載。
js不能像java一樣通過不同的參數類型或者個數來實現函數的重載,why??? 因為函數其實也是對象,函數名就是函數的標識,壓根就是一個變量,參數只是這個函數的屬性。靠定義參數數量不同去實現函數的重載是不行的。
函數在被調用的時候,js是通過函數名去找到對應的函數,然后根據函數定義時候的參數,和表達式參數列表按順序匹配,多余的參數舍棄,不夠的用undefined補全,那么既然函數在調用的時候回產生一個arguments對象,我們是不是就可以利用這一點模擬函數的重載???
//根據參數模擬
function func2(arg1, agr2, arg3){
var sum;
if(arguments.length == 1){
sum = arg1+100;
}else{
sum =arg1+arg2+arg3;
}
return sum;
}
//根據參數的值
//根據參數類型模擬
function func2(arg){
if(typeof arg1 =='number'){
/**操作1**/
}else if(typeof arg1 =='string'){
/**操作2**/
}
}
-------------------------------------------------------------------------
三、最后arguments還有一個callee屬性,從上面打印的結果看,callee返回的是正在被執行的Function對象,也就是制定對象的正文。
那么這么一說,callee還是有長度的,實際上arguments.callee.length==形參長度。說了這么多,那一般什么時候使用呢???
3.1可以利用arguments.callee.length==形參長度來驗證形參個實參的個數是不是一致。
3.2利用callee是指向正在執行函數的指針的特點,處理匿名函數遞歸問題。
//求1~100的和,只是一個例子。
//1、假如我們使用一般的遞歸函數
var sum = function(n){
if(1==n){
return 1;
}else{
return n+sum(n-1);
}
}
//現在使用callee
var sum = function (n) {
if (n <= 0)
return 1;
else
return n + arguments.callee(n - 1)
}
有啥區別?
前者包含了對sum自身的調用,var sum相當於一個全局變量,每次調用都相當於調用一個全局變量。后者並沒有這樣,而是利用了callee的這個屬性,說來說去還不是遞歸(小心如下圖),_^^_。說實話,平時應該很少人這么去寫吧。

但是:在嚴格模式下,不能通過腳本訪問arguments.callee,訪問做個這個會導致錯誤。對於遞歸我們還可以使用命名函數表達式的方法
var func =(function f(num){
if(n<=1){
return 1;
}else{
return num*f(num-1);
}
})
//創建了一個名為f()的命名函數表達式, 返回賦值給變量func。這樣即使把函數賦值給另外一個變量,函數的名字f仍然有效。適用於嚴格和飛嚴格模式。
四、箭頭函數
注意:es6中的箭頭函數中是沒有arguments這個對象的!提供了一個替代品.
function foo (...args) {
// 這里 args 就是是真正的 Array 了
}
