【问题标题】:Are closures in javascript recompiledjavascript中的闭包是否重新编译
【发布时间】:2010-05-10 20:42:46
【问题描述】:

假设我们有这段代码(暂时忘记原型):

function A(){
  var foo = 1;
  this.method = function(){
    return foo;
  }
}
var a = new A();

每次运行函数 A 时,是否重新编译内部函数?或者这样做更好(以及为什么):

function method = function(){ return this.foo; }
function A(){
  this.foo = 1;
  this.method = method;
}
var a = new A();

或者 javascript 引擎是否足够聪明,不会每次都创建一个新的“方法”函数?特别是 Google 的 v8 和 node.js。

此外,欢迎就何时使用哪种技术提出一般性建议。在我的具体示例中,使用第一个示例确实很适合我,但我知道外部函数会被实例化很多次。

【问题讨论】:

  • 有趣的问题。不过,我愿意打赌 JavaScript 解释器比这更聪明。
  • 说实话,如果您担心性能,这不应该是您的瓶颈。我不会担心的。
  • 您想在一个私有变量上实现一个 getter,该变量不会在每个构造函数调用中复制?
  • 不完全是。我只是想知道 javascript 引擎如何处理这种特殊情况。

标签: javascript performance closures


【解决方案1】:

据我了解,与其说是“编译”函数,不如说它每次执行时都有不同的“范围”。

您使用的第二种方法将始终具有来自同一范围的method

第一种方法将method 放在A() 函数调用的范围内。因此,该范围内的任何信息(var foo、函数参数等)都存储在函数范围的该实例中。因此,每次都会引用相同的函数代码,但它会在不同的范围内(因此是不同的“对象”)。

【讨论】:

  • +1,最好和最简洁的答案在这里(你实际上回答了这个问题!)。
【解决方案2】:

是的,您在 A 对象的每次实例化时都创建了一个新的 Function 对象。您可以按如下方式进行演示:

function A(){
  var foo = 1;
  this.method = function(){
    return foo;
  }
}
var a = new A();
var b = new A();
alert(a.method == b.method); // Returns false; two different Function objects

如果您想重用同一个 Function 对象,请将方法设为原型的属性,而不是实例。

function B() {
  this.foo = 1;
}

B.prototype.method = function() {
  return this.foo;
}

var a = new B();
var b = new B();
alert(a.method == b.method); // Returns true; it's the same Function object

编辑:据我所知,除了在 JavaScript 对象中创建私有变量外,没有理由做类似第一个版本的事情。在原始示例中,foo 是私有的。没有任何东西可以直接从对象外部访问它。不幸的是,当您使用这种技术实例化大量对象时,它可能会对性能和内存占用产生影响。

在我的代码中,我使用命名约定来区分“公共”和“私有”属性。我用下划线作为第一个字符来命名私有属性。所以如果我看到类似myObject._someMethod() 的东西,我就知道有问题了。


Edit2:从http://code.google.com/apis/v8/design.html 开始,我认为 V8 在创建包含 method 属性的隐藏类时编译了一次闭包。

【讨论】:

  • 这很有趣。莫非是相等运算符故意区分这两种方法,但实际上是同一种?我知道我可以使用原型,但我想在这种情况下忽略它。
  • @Discodancer:这两个方法的代码相同,但它们是不同的对象,就像你做a = {}; b = {}; alert(a == b); // false之类的事情一样。
  • 在现代解释器中使用本地(“私有”)变量对性能的影响可以忽略不计。我不知道内存,但我敢打赌它还不足以产生巨大的影响。
  • 在大多数情况下,你可能是正确的,音乐怪胎。在我的工作中,不幸的是,我们必须支持一些非现代解释器(cough IE),它们会在最轻微的刺激下泄漏内存。此外,我工作的 Web 应用程序非常庞大,可能涉及数千个非常复杂的 JS 对象实例。在这种情况下,任何故意增加闭包的方法都可能会带来数周(或数月)的内存泄漏寻找乐趣。但是对于日常开发来说,通过闭包实现私有属性就可以了。
【解决方案3】:

方法没有重新编译。

每次调用外部方法时,Javascript 解释器都会创建一个包含内部方法和局部变量的新闭包对象。

具体实现取决于 Javascript 引擎。

【讨论】:

    【解决方案4】:

    我猜它只编译一次...因为“this”关键字指的是执行上下文...所以编译器不需要了解太多函数来解释它。

    此外,当你在函数的底部声明一个变量时,它仍然可以在顶部访问:

    function test()
    {
      alert(hello);
      // ...
      var hello = 2;
    }
    

    函数也是一样。考虑到这两点,我相信它不会在每次调用 A 时都重新编译。

    迈克

    【讨论】:

    • 实际上 - 这应该提醒undefined。变量声明被提升到顶部,但 hello 直到警报之后才设置为 2
    【解决方案5】:

    想象一个函数只是另一个对象,然后当你创建一个新对象时,它只是旧对象的副本,其中一些数据变量发生了变化。您无需重新解析对象的来源即可。一个很好的类比是 C++ 中的 functionoids,Lua 中的函数对象,而我真的不知道很多其他语言。

    【讨论】:

      【解决方案6】:
      function A(){
        var foo = 1;
        this.method = function(){
          return foo;
        }
      }
      

      无法编译成

      function method = function(){ return this.foo; }
      function A(){
        this.foo = 1;
        this.method = method;
      }
      

      因为功能会改变。第一个示例有一个变量“foo”,它只对构造函数“A”和名为“method”的函数可见,而在第二个示例中,所有有权访问“A”实例的东西都可以访问。

      可以编译成

      function method = function(){ return 1; }
      function A(){ 
        this.method = method;
      }
      

      但我认为任何 javascript 引擎都不会走那么远,但如果您能够预处理您的文件并愿意付出额外的努力,google closure compiler 可能会走得很远在高级模式下使用。

      【讨论】:

      • 我的问题是第一个示例是否会在我每次运行 A() 时创建函数的新实例,而不是它是否可以编译到另一个示例中。
      猜你喜欢
      • 2011-04-18
      • 2016-04-21
      • 2015-08-31
      • 1970-01-01
      • 1970-01-01
      • 2021-03-30
      • 1970-01-01
      • 2021-05-01
      • 2012-01-29
      相关资源
      最近更新 更多