【问题标题】:class declared inside closure vs standard class without closure在闭包内声明的类与没有闭包的标准类
【发布时间】:2013-08-16 06:26:44
【问题描述】:

通常我使用基于原型的标准 OOP 方法,我的类看起来像这样

var std = function(){
   this.log = function(msg){ console.log("want to be private. " + msg) };
};

std.prototype = {
    logInfo: function(msg){
        this.log(msg);
    }
};

但在这种情况下,log 是公共方法,任何人都可以使用它。但我想将其设为私有,但仍可在原型中声明的方法中使用。为此,我们需要闭包。代码将更改为此

var closureStd = (function(){
var std = function(){};
var log = function(msg){ console.log("I'm really private, youhooo!" + msg) };

std.prototype = {
    logInfo: function(msg){
        log(msg);
    }
};

return std;
})();

所以我的问题是:stdclosureStd 之间有什么区别?我需要付出什么代价才能从原型中调用私有方法?

【问题讨论】:

  • 对您有用的链接:The cost of privacyJavascript module pattern
  • 感谢有趣的文章。它给了我一些分数,但不要回答问题(
  • 抱歉,没有注意到您添加了第 2 篇文章。也需要阅读。

标签: javascript oop closures prototype


【解决方案1】:

std和closureStd有什么区别?

std 构造函数在每次调用时都会创建一个新方法,而 closureStd 不会。你应该成功了

function std(){}
std.prototype = {
    log: function(msg){ console.log("want to be private. " + msg) },
    logInfo: function(msg){ this.log(msg); }
};

当然(你已经知道)closureStd 中的log 函数存储在一个(私有)变量中,而在std 实例上,它可以从外部在每个实例(或在他们的原型上)。在闭包中,它是作用域链中的变量查找(可以假定为静态),而对于方法,它是对象(及其原型链)上的属性查找,它可能是动态的,但在现代引擎中同样优化。

从原型调用私有方法需要付出什么代价?

没有。模块模式常见且便宜,静态链中的变量查找非常快。我宁愿担心内存,因为您在构造函数方法中创建了如此多的方法实例。

【讨论】:

    【解决方案2】:

    我还制作了一些tests 来衡量性能(结果将在控制台中),我发现将类置于闭包中与将方法置于构造函数中相比表现出更好的性能。您还有继承选项。所以现在我没有看到缺点,当我需要私有方法时,我总是会在闭包中使用类。

    【讨论】:

      【解决方案3】:

      请注意,link Bondye 提供的模块模式不包含私有 instance 属性。它适用于函数属性,因为函数不会因每个实例而改变,但对于值属性的工作有点不可预测,如下代码所示:

      var module = (function () {
          // private variables and functions
          var foo = 'bar';
          // constructor
          var module = function (name) {
            // instance variables
            this.name=name;
          };
          // prototype
          module.prototype = {
              constructor: module,
              something: function () {
                // notice we're not using this.foo
                console.log("foo in "+this.name+" is:",foo);
                foo="new value";
              }
          };
          // return module
          return module;
      })();
      
      var m1 = new module("m1");
      var m2 = new module("m2");
      m1.something();// foo in m1 is: bar
      m1.something();// foo in m1 is: new value
      m2.something();// foo in m2 is: new value
      

      正如您在最后一行代码中看到的那样,m1 和 m2 实例共享一个名为 foo 的私有变量(所有实例都将指向相同的值)。

      我可以从 Bondye 发布的 other link 中看出,很难在 JavaScript 中模拟私有性,您可以考虑使用命名约定来指示属性是私有的。您可以使用 google 闭包编译器之类的东西并注释您的 JS 代码以指示私有;但是使用闭包编译器有它自己的缺点(除非与闭包编译器兼容并且代码必须采用某种格式才能在高级模式下编译,否则无法编译使用的库)。

      另一种选择是完全抛弃原型(至少对于使用私有属性的所有内容)并将所有内容放在构造函数的主体中(或使用返回对象的函数)。

      // function returning an object
      
      function makeObj(name){
        // private vars:
        var foo =  "bar";
        return {
          name:name,
          something:function(){
            console.log("foo in "+this.name+" is:",foo);
            foo="new value";
          }
        }  
      }
      var m1=makeObj("m1");
      var m2=makeObj("m2");
      m1.something();// foo in m1 is: bar
      m1.something();// foo in m1 is: new value
      m2.something();// foo in m2 is: bar
      
      // constructor with everything in the constructor's body:
      
      function Obj(name){
        // private vars:
        var foo =  "bar";
        this.name=name;
        this.something=function(){
          console.log("foo in "+this.name+" is:",foo);
          foo="new value";
        }
      }
      Obj.prototype.someOtherFunction=function(){
        // anything here can't access the "private" variables
      }
      var m1=new Obj("m1");
      var m2=new Obj("m2");
      m1.something();// foo in m1 is: bar
      m1.something();// foo in m1 is: new value
      m2.something();// foo in m2 is: bar
      

      使用私有实例值可能会遇到的另一个问题是它们只能在实例中访问。当你想克隆一个对象并在你的对象中定义一个克隆函数来创建一个新实例时,你必须编写公共访问器函数来设置私有值,因为你不能像Java private fields那样直接设置它们。

      在此处使用构造函数的更多信息:Prototypical inheritance - writing up

      【讨论】:

      • 感谢您的回答。第一个例子非常有趣,我花了一些时间来理解为什么会这样。现在我看到闭包对于私有字段来说并不是完美的变体,但最初的问题是关于方法的。在我看来,将所有东西都放在构造函数中并不是一个好习惯,主要是因为性能。
      • @silent_coder 您是对的,第一个示例适用于私有函数,它是来自 Bondye 提供的链接的代码。由于 SO 在 google 上的得分很高,我认为值得一提的是私有实例值并不那么容易。
      猜你喜欢
      • 2012-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-03
      • 1970-01-01
      • 1970-01-01
      • 2016-05-05
      相关资源
      最近更新 更多