有关从JavaScript中的闭包返回对象文字的详细信息

Kar*_*arl 5 javascript closures google-closure-compiler

背景:我想重写一个库(我没有写过),以避免Closure Compiler使用Advanced选项生成警告。对于这个问题,JavaScript“ this”关键字和Closure Compiler警告,答案是使用闭包重写代码。目的是避免使用关键字this(生成编译器警告)。

由于该库具有许多函数,所以我最好使新闭包返回一个对象文字。我想了解这是如何工作的以及可能产生的后果。因此,我编写了以下(无意义的)示例作为学习技巧(也在此处:jsFiddle):

  var CurrencyObject = function(Amount) {
        var money = Amount;
        return {
              "toCents": function() {
                    money *= 100;
                    return money;
              },
              "toDollars": function() {
                    money /= 100;
                    return money;
              },
              "currentValue": money  // currentValue is always value of Amount
        };
  }; // end currencyObject

  var c1 = CurrencyObject(1.99); // what's the difference if the syntax includes `new`?

  alert('cents = ' + c1.toCents() + '\ncurrent money = ' + c1.currentValue + '\ndollars = ' + c1.toDollars() + '\ncurrent money = ' + c1.currentValue);

  var c2 = CurrencyObject(2.99);

  alert('cents = ' + c2.toCents() + '\ncurrent money = ' + c2.currentValue + '\ndollars = ' + c2.toDollars() + '\ncurrent money = ' + c2.currentValue);

  alert('cents = ' + c1.toCents() + '\ncurrent money = ' + c1.currentValue + '\ndollars = ' + c1.makeDollars() + '\ncurrent money = ' + c1.currentValue);
Run Code Online (Sandbox Code Playgroud)

问题1:为什么在toCents调用后currentValue没有更新?(我想这是因为currentValue是一个literal(?),它是在第一次执行CurrencyObject时初始化的。如果是这种情况,还返回属性currentValue的语法是什么?)

问题2:此语法(带有newvar c1 = new CurrencyObject(1.99);不会以我可以检测到的方式更改代码的行为,但我认为存在区别。它是什么?

Q3:实例化c2时,正在创建的函数副本还是c1c2共享相同的(函数)代码?(如果正在创建功能的副本,我应该做哪些更改以避免这种情况?)

TIA

顺便说一句:如果有人想知道,对象文字中的符号会加引号,以避免Closure-Compiler重命名它们。

Eli*_*gem 1

更新:
对你的 fiddle 进行了分支,并添加了一些所有实例实际上共享的方法(即:不会为每个实例创建新的函数对象):例如,将金额转换为欧元或英镑的方法。我还省略了该money变量,因为它根本没有必要。为了避免必须尽可能多地使用this,我还将返回的对象分配给闭包范围内的变量,以便所有方法都可以通过该变量名称引用其父对象,而不必依赖于this.
然而,共享方法仍然需要this偶尔使用,因为它们是在“更高”的作用域中声明的,并且无法访问该returnObject变量,仅仅是因为它不存在于它们的作用域中。如果您想知道,取消returnObject变量声明并不是解决方案,因为您很快就会发现无法创建超过 1 个货币对象实例。最后,我将“构造函数”
的名称更改为以小写字母开头。从技术上讲,您的函数不再是构造函数,并且约定是它因此不能以大写字母开头...如果您仍然不清楚我在这里解释的任何内容或我建议的任何更改,请告诉我。

currentValue更新,因为您money通过执行以下操作更改了变量的值:money *= 100;。该语句将money值相乘并将其分配给同一个变量。由于这是一个原语,currentValue有它自己的该值的副本,因此它们不以任何方式链接。
建议:用于return money/100;保持值money恒定。现在看来,调用该toCents方法两次相当于将原始金额乘以 10,000。要将每次调用更新/设置currentValue为您想要的任何内容,请添加:this.currentValue = money*100;,这有点危险,或者通过使用命名引用让您的闭包访问其自己的文字(这更安全,但更冗长) :

var someClosure = function (amount)
{
    var money = amount;//redundant: you can use amount, it's preserved in this scope, too
    var me = {currentValue:amount,
              toCents: function()
              {
                  me.currentValue = money*100;//<-- use me to refer to the object itself
                  return amount/100;
              }
      };
      return me;
}
Run Code Online (Sandbox Code Playgroud)

没有理由使用关键字:由这个“构造函数”new创建的对象是一个对象文字,它只有 1 个原型(,并且没有显式构造函数名称)。添加将指向函数本身中的一个新对象,但由于您没有使用它,并且您的函数返回另一个对象,因此该对象永远不会返回。Object.prototypenewthis

严格来说:一些JS引擎会为每个新实例创建新的函数对象。一些现代对象对此进行了优化,实际上只会创建 1 个函数对象。为了安全起见,您可以在事物周围包装另一个闭包,以确保只有 1 个函数,无论什么引擎运行您的代码:

var someObjectGenerator = (function()
{
    var oneOffFunction = function()
    {
        console.log('this function will only be created once');
    }
    return function(amount)
    {
        return {    foo:oneOffFunction,
                    toCents: function()
                    {
                        return amoutn/100;
                    }
                };
    };
};
Run Code Online (Sandbox Code Playgroud)

money当然,在具有or变量的作用域之上创建的函数amount将无法访问该变量,因此在这种情况下,您将不得不创建新函数...但是 JS 对象非常便宜,所以不用担心。
同样,大多数引擎都能很好地处理这个问题。