【问题标题】:using .call vs. passing "this" in JavaScript使用 .call 与在 JavaScript 中传递“this”
【发布时间】:2013-12-11 19:24:05
【问题描述】:

我知道,如果你有一些 javascript 函数并且你想调用它,以便在其中使用 this 不会引用直接调用它的对象,你可以使用 func.call(thatObject,param,and,more,params...)

但假设你是func 的作者,而 func 的唯一用途是通过 func.call,
你为什么不把它定义为:

function func(that,param,and,more,params...) {
   //and in here use *that* and not *this*
}

是的,它看起来不那么“酷”,因为它不是对象的方法, 但是,嘿,如果 func 的唯一用途是通过 func.call,这一切似乎都只是额外的代码和开销。

我在这里遗漏了什么吗?还是我在其中看到这种模式的源代码只是“过度 OOed”?

【问题讨论】:

  • 为什么这被标记为“性能”? “优化”子问题在哪里?
  • @Tomalak,因为可能存在性能差异,一个是调用一个额外的函数来获取正确的范围,而另一个是发送一个带有指向对象的指针的变量。其实你有130k+rep,我为什么要和你争论……
  • @RustyToms:那将是过早的优化。带有性能标签的唯一有效问题是包含分析器报告的问题。
  • 直接调用函数和通过.call().apply() 调用函数没有性能差异。为什么你这么想?你量过吗?有关系吗?你确定你真的对性能影响感兴趣,或者你真的在问为什么.call()/.apply()首先存在?
  • @ABFORCE:所有 JavaScript 函数可以使用callapply 调用。它是基本的语言特性之一。

标签: javascript performance oop optimization


【解决方案1】:

似乎存在很大的性能差异。使用

func(){
  //code here, this.something
}
func.call(thatObject)

根据前几次测试,比使用慢大约 8 倍

func(that){
  //code here, that.something
}
func(thatObject)

自己测试一下,JSPerf here

但最终,速度本身很少是我们使用代码的最重要因素。代码是为人设计的,就像为计算机设计的一样,我们需要清楚地向两者传达我们的意图。让代码最干净的就是最好的,只要可行,我们就应该遵循约定。我个人更喜欢这里的第二种选择,但我认为一般约定是第一种。所以我认为您在大多数情况下都使用call,除非您需要尽可能快的代码或约定发生变化。

【讨论】:

  • @ZenMaster,这些测试都不包括 func(thatObject) 测试。
  • 他们现在在最新版本中。
  • 另外,在你全力以赴之前 - webreflection.blogspot.co.il/2010/02/…
  • 在那个 webreflection 博客中他们说“这意味着即使可以避免它,使用这种机制我们也会感觉更酷”......奇怪的是他们还说“CROME:......否则call 比 apply 快 4 倍,显然,甚至比直接调用还要快”(现在似乎不是这样)。
【解决方案2】:

您的模式可以在这种情况下工作,但总的来说它有一些问题......

  1. 构造函数 -> 我们使用 new 关键字创建对象,其中“this”对象是自动创建并返回的(默认),它还具有与函数原型的链接。

  2. 调用函数时,有人可能会忘记传递“那个”对象,您的代码将失败。在调用的情况下,如果您传递 null,它将被重置为全局对象(常见的非严格模式)

【讨论】:

  • 构造函数必须使用这个......它在上下文中是适当的并且没有其他方法......至于忘记传递'that' - 你可以忘记许多其他事情......但是@ 987654321@ 比doSomething.call(usingThisContext,likeThat) 更短且更易读(恕我直言),更不用说您最终可能会忘记 .call 并尝试使用doSomething(usingThisContext,likeThat) 或(如果您认为有人可能忘记传递'that`doSomething.call(likeThat)
【解决方案3】:

func.call 旨在在需要提供函数的替代(或任何)上下文时使用。

您的建议很奇怪,因为当您使用此模式定义函数时:**

function func(that,param,and,more,params...) {
   //and in here use *that* and not *this*
}

要么是:

不属于任何对象

在这种情况下,传递一个对象作为this 没有任何意义,因为不应该以this.someThing 调用开头

属于不同的对象或被定义为对象的插件

在这种情况下,它完全做了func.call 会做的事情,同时用冗余参数污染了函数的定义。

更新:

以这种方式考虑第二个示例 - 假设某些对象集(您将从同一个“类”中调用的对象)允许注入任意函数,例如迭代对象的所有属性和一些摘要或操纵或你有什么。

在 Java 中,常见的模式是创建一个迭代器并传递它,它的主要目的是充当一种占位符,以便可以调用 nexthasNext 方法 -因为 Java 并没有真正的无对象函数。 (当然还有一些额外的逻辑,但为了讨论,让我们先不管它)。

JavaScript 不需要这个!所有这些callapply 方法都需要有一些额外的 Iterator 对象来保存它们。它们可以被定义为与任何对象“分离”,同时仍然打算在一个上下文中使用(因此在他们的代码中使用this)并注入到知道接受此类函数的代码中。

“主机”代码只需要callapply 他们,知道this 将引用它自己——这个非常“主机”对象。

这导致了更简洁、可重用和可移植的代码 IMO。

更新 2:

查看更多here

【讨论】:

  • 也许我选择调用第一个参数that 不是一个好方法:) 但关键是它是一个适用于某些对象的函数。但不属于它。这就是为什么我觉得奇怪的是,该函数的作者选择在其中使用this,然后在其中使用.call,而不是仅仅将对象作为参数传递。
  • “JavaScript 确实需要这个”?还是“没有”?我知道它可以通过这种方式完成(即“独立”函数在其代码中引用 this 以及使用这些函数的对象,就好像它们拥有 via 调用一样。我尊重您的意见,但我没有找到此代码模式更加简洁,可重用或便携。此外,看到性能差异以及我认为我会使用直接调用模式的事实......
  • 如果你不明白它是如何更简洁或可重用的,我帮不了你。我发现你的编码方式非常难看,我认为你根本没有办法注意到现实世界和正常用例中的调用之间的区别。此外,我让浏览器供应商处理性能问题并担心编写好的代码。优化从 10 ns 到 1 ns 的一些函数调用是不值得的(而且没有对所述性能结果的有效性进行冗长的讨论)。
  • 我还查看了您更新 2 中的链接,有趣的是,他们用来演示 .call 使用的 _.first 方法实际上是为了被调用而设计的,而不是 .call()ed如果确实使用 .call 非常棒,那么为什么不设计_.first.call([1,3,4],2)(你的方式)而不是_.first([1,3,4],2)(我的方式)的使用模式?
猜你喜欢
  • 1970-01-01
  • 2012-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-20
  • 2014-07-29
  • 2011-05-31
相关资源
最近更新 更多