1. 程式人生 > >不定引數(rest 引數 ...)

不定引數(rest 引數 ...)

不定引數

如何實現不定引數

使用過 underscore.js 的人,肯定都使用過以下幾個方法:


_.without(array, *values) //返回一個刪除所有values值後的array副本
_.union(*arrays) //返回傳入的arrays(陣列)並集
_.difference(array, *others)//返回來自array引數陣列,並且不存在於other 陣列
...

這些方法都有一個共同點,就是可以傳入不定數量的引數,例如,我想刪除掉 array 中的 value1,value2 ,可以這樣使用 , _.without(array,value1,value2);

那麼,這個需要怎樣才能做到呢?

我們知道,js 中 function 裡面,有一個 arguments 引數,它是一個類陣列,裡面包含著呼叫這個方法的所有引數,所以可以這樣處理:


_.without = function() {
    if (arguments.length > 0) {
        var array = arguments[0];
        var values = [];
        for (var i = 1; i < arguments.length; i++) {
            values.push(arguments[i]);
        }
        //這樣得到了array,和values陣列,便可以進一步處理了
    }
};

上面只是打個比方,想要支援不定引數,我們要做的就是把固定引數和動態引數從 arguments 中分離出來。
但是,我們這樣寫的話,需要在每個支援不定引數的函式裡,都 copy 這樣一段程式碼,這樣實在不是很優雅。所以需要封裝成一個通用的函式。
我們直接看看 underscore 是封裝的好了。

restArgs 原始碼


var restArgs = function(func, startIndex) {
    //startIndex ,表示幾個引數之後便是動態引數
    startIndex = startIndex == null ? func.length - 1 : +startIndex;
    return function() {
        var length = Math.max(arguments.length - startIndex, 0);
        //處理arguments,將動態引數儲存進rest陣列
        var rest = Array(length);
        for (var index = 0; index < length; index++) {
            rest[index] = arguments[index + startIndex];
        }
        //處理0,1,2三種情況,這裡要單獨處理,是想優先使用call,因為,call的效能比apply要好一點
        switch (startIndex) {
            case 0:
                return func.call(this, rest);
            case 1:
                return func.call(this, arguments[0], rest);
            case 2:
                return func.call(this, arguments[0], arguments[1], rest);
        }

        //如果startIndex不是0,1,2三種情況,則使用apply呼叫方法,將args作為引數,args將為陣列[固定引數 ,rest];
        var args = Array(startIndex + 1);
        for (index = 0; index < startIndex; index++) {
            args[index] = arguments[index];
        }
        args[startIndex] = rest;
        return func.apply(this, args);
    };
};

//這裡without主要的邏輯處理方法,作為引數,傳給restArgs,在restArgs中處理完引數後,使用call或apply呼叫邏輯處理方法
// 這時候接受到引數otherArrays,已經是一個數組了,包含了之前的動態引數。
_.without = restArgs(function(array, otherArrays) {
    //處理without具體事件
});

underscore.js 中利用 js 高階函式的特性,巧妙的實現了動態引數

如果要使某函式支援不定引數,只需要將該函式作為引數,傳入 restArgs 中即可,例如:


function addByArray(values) {
    var sum = 0;
    for (var i = 0; i < values.length; i++) {
        sum += values[i];
    }
    return sum;
}
var add = restArgs(addByArray);
//呼叫:
addByArray([2, 5, 3, 6]); //16
add(2, 5, 3, 6); //16

ES6 不定引數 (...)

ES6 引入了 rest 引數,(形式為"...變數名"),用於獲取多餘引數,這樣就不需要使用 arguments 物件來實現了


function add(...values) {
    let sum = 0;
    for (var val of values) {
        sum += val;
    }
    return sum;
}
add(2, 5, 3); // 10

總結

在 underscore 中,restArgs 只是為了支援不定引數。實際使用中,也許我們都是直接使用 ES6,或用 babel 將 ES6 轉成 ES5 來支援不定引數

不過,如果是在非 es6 的環境下,知道有這麼一種實現方式,也是挺好的。

:)

來源:https://segmentfault.com/a/1190000017467086