js权威指南 this.add.apply(this.argments)。

在重看JS权威指南时,看到这么一段代码 位于原书P217 (JS权威指南第六版,淘宝前端翻译).

function Set(){
    this.values = {};
    this.n = 0;
    this.add.apply(this, arguments);
}

这里有一点疑惑,就是 this.add.apply(this, arguments); 这段代码为什么要写成这样子。
这样难道不是更加简洁么? this.add(arguments);

this.***.(apply|call)(this,...) 

这样的代码难道不是和 this.*(...) 是一样的效果么

阅读 4.7k
6 个回答

对于这个问题,this.add.apply(this, arguments);this.add(arguments);相同的点是在add函数里面的this指向是一样的,不同的点是传给add函数的参数是不一样的,假设我们使用new Set(1, 2);那么arguments[1, 2],使用this.add.apply(this, arguments);调用add函数时其实相当于this.add(1, 2);此时add接收到的参数个数是2;而使用this.add(arguments)调用add函数时,相当于this.add([1, 2]);此时add收到的参数个数是1,参数是数组[1, 2]

Update1:

我觉得和不定参数还是有点儿关系的,比如说add函数的作用就是把传给它的每个参数相加,我们想在Set函数里面计算传给它的参数的总和,假设Set函数接收的参数也不固定,使用this.add.apply(this, arguments);是可以的,如果用this.add方法直接调用,因为Set接收的参数数目不固定,所以我们可能就得按照下面这么写:

var len = arguments.length;
this.add(arguments[0], arguments[1], arguments[2], ..., arguments[len]);

但是因为len是一个不确定的数,我们没法确定...的内容,所以没有办法通过this.add()这种直接调用的方式实现不定参数的调用。

Update2:

var len = arguments.length;
this.add(arguments[0], arguments[1], arguments[2], ..., arguments[len]);

在ES6之前是没有办法通过this.add()这种方法直接调用的,在ES6中可以通过解耦的方式调用this.add(...arguments),这个和this.add.apply(this, arguments);的效果是一样的;
不固定指的是参数的数量。

arguments 是一个类数组,this.add(arguments) add 只接受一个参数,this.add.apply(this, arguments) add 接受 arguments.length 个参数。

现在 ES6 也可以用 this.add(...arguments)

var Util = {

  add: function() {
    let sum = 0;
    for (let i = 0; i < arguments.length; i++) {
      sum += arguments[i];
    }
    return sum;
  },

  range: function(l = 0, s = 1) {
    let list = [];
    for (let i = 0; i < l; i++) {
      list[i] = s + i;
    }
    return list;
  }

};

// 假设你从其他函数中得到了一个数组,你要将数组中的元素累加
let list = Util.range(100);
// 现在list是一个长度为100的数组,如果是你,让你如何利用已知的add方法让数组相加呢?
// 结论:apply的用途就在这种场景下了

大概理解了,原书中的方法,apply的作用是将arguments拆解成一个一个的数传过去,而this.add()是将arguments作为类数组对象传过去,也就是说,如果函数内部想要调用传过来的参数,原方法内调用

arguments[i]

即可,而改写方法则是

arguments[0][i]

还有不太理解的,请将如下代码运行一下,然后在控制台里看看输出是什么

(function(){
        function Set(){
            this.values = {};
            this.n = 0;
            this.add.apply(this, arguments);
            this.add(arguments);
        }
        Set.prototype.add = function(){
            for(var i=0;i<arguments.length;i++){
                console.log(arguments[i]);
            }
        };
        var test = new Set(1,2,3,4,5,6,7);
    })();

其实这跟不定参数没关系,arguments里面包含了所有的参数,JS不用形参也是可以传参数的,也就是说,arguments里面包含了所有传过来的参数,如果直接使用,则在add里面的实参就是一个数组!!!这个数组里面包含了所传的参数。而如果采用apply,apply会解构一层数组,也就是说他会将数组里面的参数一个一个取出来然后传给add,这两种方法都将参数传递过去了!是所有参数都传递过去了

Update 1:

(PS.经过一番讨论,懂了,这是面向对象方面的,从语法上可能是传递的参数不一样,但从功能上,当参数类型不同的时候,不用每次都修改add方法,是有点策略模式的味道么?解耦?感谢各位大佬)

最后总结了一下可惜还是理解岔了,这样好了,教你一个迅速理解的办法:

function Set(){
  this.values = {};
  this.n = 0;
  debugger;
  this.add.apply(this, arguments);
}

// 分别运行以下几句,别一起运行,因为没有 `add` 方法是走不下去的……
Set(1);
Set(1, 2);
Set(1, 2, 3);

打开开发者工具,把上述代码贴到控制台里执行,然后观察每一个断点之后的 arguments。这要是再不明白就……

图片描述

BTW,Set 本应是构造函数,但是我上面的代码也没实现真正的 Set,所以就不去 new 了,领会精神。apply 除了和 call 一样的显式指明函数调用时的上下文之外,另一个用处就是传递不定数量的参数(也就是 arguments。通过这个问题可以看出你对 arguments 也是理解不到位的;不过要注意:apply 不是非得用 arguments 不可,记住这句话在将来的实践中会派上大用场的)。

你这个问题的核心就在后者的不定参数上了。

为什么我觉得这里call和apply的作用相反了呢,apply是传递一个数组,call是传数组中的每一个元素,楼主能帮我解释一下吗

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题