用 Javascript 中的参数替换局部变量是不是会影响性能?

Posted

技术标签:

【中文标题】用 Javascript 中的参数替换局部变量是不是会影响性能?【英文标题】:Is there a performance hit of replacing local variables with arguments in Javascript?用 Javascript 中的参数替换局部变量是否会影响性能? 【发布时间】:2011-05-21 05:21:24 【问题描述】:

编写一个函数以将本地var 语句替换为参数是否会影响性能?示例:

function howManyMatch(arr, pattern, /*ignored:*/ i, l, total) 
  l = arr.length;
  total = 0;
  for (i = 0, i < l; i++) 
    if (pattern.test(arr[i]))
      total++;
  return total;

一些优点:

较小的缩小尺寸:没有var 语句; 尽量减少程序员使用vars 所花费的时间 在一处定义的所有本地变量

...和缺点:

arguments 可以以意想不到的方式改变。见下文 在正文中不太清楚 var 是本地的 对无用的论点感到困惑 如果有人在不知情的情况下删除了它们,您的代码将写入全局变量

对于缩小器来说,自动挤出更多位可能是一种简单的方法。

更新:到目前为止没有提到的一个很大的缺点:如果使用 N 个参数调用函数,arguments 中的前 N ​​个项目将绑定到参数列表中的前 N ​​个标识符 (请参阅last bullet in 10.1.8)。考虑一下:

function processStuff(/*ignored:*/i, j, k) 
    // use i/j/k to loop
    // do stuff with the arguments pseudo-array

在上面的例子中,如果你调用processStuff(stuff1, stuff2),设置ij会分别覆盖arguments[0]arguments[1]

【问题讨论】:

我的问题主要是关于性能影响。我可能不会使用这种做法。许多人似乎没有意识到将所有 var 语句组合成一个会降低可读性:您基本上是在滥用逗号运算符将多个语句塞在一起。看到 Closure Compiler 可以为你做这个优化,每行只有一个 var 声明/赋值更具可读性。 【参考方案1】:

不,不要这样做。这是令人困惑和不必要的。而且我发现您列出的“优势”列表非常似是而非 - 那里的每一项都非常薄就获得的实际收益而言。

如果必须,只需使用 comma operator 并在函数开头的单个语句中声明所有变量(无论如何它们都会被吊到这个位置。

function howManyMatch(arr, pattern) 
  var i, l, total;
  // rest

或者您也可以一次性声明/定义所有内容

function howManyMatch(arr, pattern) 
  var l = arr.length, total = 0, i = 0;
  // rest

【讨论】:

【参考方案2】:

由于您已经知道的许多原因,我不会这样做,我个人不喜欢将参数与变量的语义含义混合在一起的事实,尽管在实现级别,当函数执行时,它们只是当前变量对象的属性,它们在IMO中具有不同的含义。

现在,回答这个问题,我认为不会对性能产生任何影响

让我谈谈Variable Instantiation process,它发生在Function Code,就在函数执行之前(通常称为“提升”),首先,所有的形式参数 为函数描述的函数绑定到当前变量对象(当前作用域),并使用函数调用中传递的值进行初始化,如果未提供 undefined

之后,属于函数内所有var语句的所有标识符都在当前作用域中声明,并以undefined初始化(注意,在此之后进行赋值,函数体实际上并不是正在执行中)。

第三步是FunctionDeclarations,函数声明的所有标识符都绑定到本地作用域,如果之前声明了一个标识符,则替换它的值,例如:

(function (a) 
  return typeof a; // "function", not "string"

  function a () 

)('foo');  // <-- passing a string

我建议在函数顶部使用单个 var 语句:

function howManyMatch(arr, pattern) 
  var l = arr.length,
      total = 0, i;
  for (i = 0, i < l; i++) 
    if pattern.test(arr[i]) && total++;
  return total;

这不仅可以组织您的代码,还可以帮助您防止由于 javascript 的纯函数范围和 var 的“提升”性质而产生的不想要的结果,JSLint encourage 等一些工具也是如此。

【讨论】:

感谢规范链接。我更新了这个问题,添加了另一个缺点,即形式参数如何绑定到arguments。我可能不会每次都使用这个“技巧”。【参考方案3】:

我认为可读性和可维护性比文件大小和微优化更重要。具有var 关键字的代码更容易阅读。除此之外,每个作用域一个 var 语句就足够了(无论如何,这就是 JavaScript 提升它们的地方)。 所有局部变量在局部范围内无处不在可用(无论它们被声明的顺序如何)。因此,所有局部变量都应该声明在同一位置(在局部范围的开头)以获得最佳可读性。 var 语句的这四个字节确实不值得引入可能的错误,因为它允许用户通过使用附加参数调用该函数来设置局部变量的初始值。它破坏了封装(你可以正确地做到这一点,但你最终会得到比省略var所节省的更多字节)。除此之外,任何试图阅读您的代码的人都会感到困惑。

【讨论】:

【参考方案4】:

只需删除 var 关键字即可获得与您相同的优势:

function howManyMatch(arr, pattern) 
  l = arr.length;
  total = 0;
  for (i = 0; i < l; i++) 
    if pattern.test(arr[i]) && total++;
  return total;

无需显式编写 var 关键字,因为在这种情况下,您正在使用值(l = arr.lengthtotal = 0i = 0)定义所有变量。

顺便提一下,您不能通过将变量定义为函数参数来预定义变量。这是不可能的,例如:

function howManyMatch(arr, pattern, i=0, l, total = 0) ... 

所以,我认为你缩小代码的解决方案毕竟不是很有用,因为缺点仍然存在;)


编辑

我没有想到在没有var 关键字的情况下定义变量会将它们变成全局变量。这可能是你根本不想要的……

但是当我再次考虑这个问题时,我不明白为什么要在函数参数中定义变量。您为该方法提供的所有优点在此示例中也基本正确:

function howManyMatch(arr, pattern) 
  var l = arr.length, total = 0, i=0;
  for (; i < l; i++) 
    if pattern.test(arr[i]) && total++;
  return total;

而且这个例子更短。

【讨论】:

省略var 语句会使所有使用的变量(不是参数)都成为隐式全局变量,这非常糟糕。你不应该那样做。 litotal 将在此函数执行后成为全局变量。 @elusive,我没想到,你是对的。调用函数后,变量确实是全局变量。【参考方案5】:

Chistian Johansen 在“Test-Driven Javascript Development”(2011) 中指出:“一般来说,arguments 对象应该只在形参无法解决手头问题的情况下使用,因为使用它会带来性能代价。事实上,仅仅引用该对象会产生一些开销,表明浏览器优化了不使用它的功能。”

【讨论】:

以上是关于用 Javascript 中的参数替换局部变量是不是会影响性能?的主要内容,如果未能解决你的问题,请参考以下文章

Javascript中的作用域问题

javascript中局部变量和全局变量的区别详解

javascript练习题函数

JavaScript 闭包

JavaScript的垃圾回收机制

JavaScript中局部变量与全局变量的不同