哪种方法更好:function.apply(<this>,args)或者有一个接受<this>作为参数的函数?

mis*_*hik 7 javascript

假设我有一个如下定义的functionA:

functionA = function(myObject, someParams) {
  myObject.save_some_data = someParams;
  myObject.processed = true;
}
Run Code Online (Sandbox Code Playgroud)

然后我可以调用它并传递一个对象来处理functionA(someObject, someParams).

但是,我可以将此示例转换为apply():

functionA = function(someParams) {
  this.save_some_data = someParams;
  this.processed = true;
}

functionA.apply(someObject, [someParams]);
Run Code Online (Sandbox Code Playgroud)

这两种方法似乎都达到了同样的目标,或者我错过了什么?

但是既然apply()JavaScript中存在,那么使用它而不是让我的函数接受this作为第一个参数有什么好处?

Ale*_*xis 2

当您需要在另一个对象 B 的上下文中(就好像它是在其上定义的)调用某个对象 A 的现有方法时,Call / apply 非常有用。

例如:

var john = {
  name: 'John Doe',
  first_name: function() { return this.name.split(' ')[0] }
}

var jane = {
  name: 'Jane Doe',
}

john.first_name.call(jane) // reuse method from john 
                           // as if it was defined for jane 
                           // (will return 'Jane')
Run Code Online (Sandbox Code Playgroud)

[更新]

当然,这只是一个人为的例子来说明这个想法,但是 call / apply 方法有很多合理的应用。下面仅举几个例子:

1. 简化客户端代码

想象一下,您想要fname向对象添加方法jane = {name: 'Jane Doe'},该方法应该让您执行以下操作:

jane.fname(function() { return this.name[0] }) // => J
jane.fname(function() { return this.name.split(' ')[1] }) // => Doe
Run Code Online (Sandbox Code Playgroud)

您可以尝试以下方法之一:

// (1) naive (wrong!) approach:
jane.fname = function(callback) { return callback() } 
// problem: the callback function won't know that `this` == jane
jane.fname(function() { return this.name  }) // => undefined

// (2) straightforward approach:
jane.fname = function(callback) { return callback(this) }
jane.fname(function(self) { return self.name  }) // => Jane Doe

// (3) call / apply approach
jane.fname = function(callback) { return callback.call(this) }
jane.fname(function() { return this.name  }) // => Jane Doe
Run Code Online (Sandbox Code Playgroud)

可以看出,简单的 (2) 和调用/应用 (3) 方法都按预期工作,但最新的方法针对“外部”使用进行了优化,这在库中非常有用(想想 jQuery)

2. 传递数组而不是参数列表

另一个例子。我们有一个数字数组:

var numbers = [5, 6, 2, 3, 7];
Run Code Online (Sandbox Code Playgroud)

我们想找到其中最大的一个。现在,我们确实有一个 Math.max 函数,但我们不能轻松使用它,因为它不接受任意数字数组:Math.max(5, 6)并且Math.max(5, 6, 2, ...)是可能的,但Math.max(numbers)不是。幸运的是,我们可以使用 apply 来规避这个约束:

Math.max.apply(null, numbers); 
// we could use any object instead of null in this case, 
// because Math.max doesn't depend on the this object
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我们可以使用它apply来有效地将数组转换为参数列表。(但要小心大数组:存在超出 JavaScript 引擎的参数长度限制的风险;更多详细信息请参见此处

3. 构造函数链

这可能是一个最重要的用例:

// constructor for a NamedEntity "class":
function NamedEntity(name) {
  this.name = name;
}

function Person(first_name, last_name) {
  this.last_name = last_name;
  // reuse NamedEntity's constructor:
  NamedEntity.call(this, first_name + ' ' + last_name);
}

var john = new Person('John', 'Doe')
john.name // => John Doe
Run Code Online (Sandbox Code Playgroud)

值得一提的是,从 ECMAScript 5 开始,还有一个函数,在语义上与andbind相关,它让我们可以做更多有趣的事情callapply