underScore专题-剩余参数-restArguments

es6中剩余参数

在es6中,可以这样来使用函数的参数

    function test(a, ...rest) {
        console.log(a, rest)
    }
    test(1, 23, 4, 3)

它支持将剩下的参数以数组的形式放入形参的最后一个参数中。

underScore中

但是在underScore中,不能这样直接来使用后,因为不确定执行的环境,underScore内部自己实现了一个restArguments方法,就是用es5的方式来实现,可以让我们随心所欲的使用剩余参数语法。

看一下这个函数怎么来使用:

    function test(a, rest) {
        console.log(a, rest)
    }

    const restTest = _.restArguments(test);
    restTest(1, 2, 3, 4)

restArguments接受一个函数并返回一个函数,返回的函数传递的参数,可以支持剩余参数。看下源码实现:

function restArguments(func, startIndex) {
        startIndex = startIndex == null ? func.length - 1 : +startIndex;
        return function() {
            var length = Math.max(arguments.length - startIndex, 0),
                rest = Array(length),
                index = 0;
            for (; index < length; index++) {
                rest[index] = arguments[index + startIndex];
            }
            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);
            }
            var args = Array(startIndex + 1);
            for (index = 0; index < startIndex; index++) {
                args[index] = arguments[index];
            }
            args[startIndex] = rest;
            return func.apply(this, args);
        };
    }

看起来很复杂,没关系,我们一点一点的来分析:

我们默认使用传入的函数的最后一个参数储存剩余的参数

先来思考一下,如果我要把剩余参数都给func的最后一个参数,那是不是需要找到func的最后一参数,如何拿到呢?

函数的length属性

 

代码中有一个func.length,每一个函数都有一个length属性,返回的是函数形参的个数,注意是形参的个数,不是实参的个数,func.length-1就找到了最后参数所在的位置,用startIndex存下来

    function test(a, b, rest) {
        console.log(a, rest)
    }

    console.log(test.length) // 3
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

如果有剩余参数,会忽略 

    function test(a, b, ...rest) {
    }
    console.log(test.length) // 2
    test(1) 
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 arguments

接下来需要知道实参有几个,也就是调用的过程中到底传递了几个参数,restTest调用这个函数就相当于调用restArguments中return的函数,实参个数可以通过arguments来获取。

就拿上面来举例

arguments.length是4, fun.length是2。那test的length就是3。restArguments函数可以接受两个参数,也就是可以指定开始位置。默认会将用最后一个参数存贮剩余参数,从哪个位置开始。

如果有传递开始位置,就使用传递的,没有传递就使用形参个数减去1。startIndex也可以理解为其它参数的个数。也就是除了rest之外的参数的个数这样会更好理解一点。

startIndex = startIndex == null ? func.length - 1 : +startIndex;
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

参数的总个数减去其它参数的个数存储在length中,就是剩余参数的长度,和0取最大值,是为了防止出现负值的情况。

 var length = Math.max(arguments.length - startIndex, 0),
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

这种情况下会出现负值, arguments.length是2,fun.length-1是3。

 function test(a, b, c, rest) {
        console.log(a, rest)
    }

    const restTest = _.restArguments(test);
    restTest(1, 2)
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

这里就拿到了剩余参数数组:

 rest[index] = arguments[index + startIndex];
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

下面要做的就是把参数传递给fun了,这里先分出了3中情况,使用的call调用,传递参数,最后一个是数组,但是如果剩余参数前面的参数比较多,肯定不能这样一个一个传递。

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)
}
 
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

还好有另外一种方式,可以使用apply,传递一个数组就ok了。但是需要把剩余参数(数组)和剩余参数前面的参数放在一个数组里面。下面的代码就做了这样的事情:

 var args = Array(startIndex + 1);
 for (index = 0; index < startIndex; index++) {
     args[index] = arguments[index];
 }
 args[startIndex] = rest;
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

写到这里,是不是觉得上面的switch有点没有必要,直接走下面的代码不久可以了,干嘛要那么啰嗦。这里是underScore做的一个性能优化,考虑到call的性能比apply的高,所有在这里多做了判断。

你可能感兴趣的:(underScore专题)