在闭包与标准类中声明的类没有闭包

时间:2013-08-14 10:34:49

标签: javascript oop closures prototype

通常我使用基于原型的标准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之间有什么区别?为了能够从原型中调用私有方法我需要付出多少代价?

3 个答案:

答案 0 :(得分:2)

  

std和closureStd有什么区别?

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

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

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

  

为了能够从原型中调用私有方法,我需要付出多少代价?

无。模块模式是常见且廉价的,静态链中的变量查找非常快。我宁愿担心内存,因为你在构造函数方法中创建了很多方法实例。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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中模拟私有性,您可以考虑使用命名约定来指示属性是私有的。您可以使用谷歌闭包编译器之类的东西,并注释您的JS代码来表示私有;但是使用闭包编译器有它自己的缺点(除非Closure Compiler兼容并且代码必须采用某种格式才能在高级模式下编译,否则无法编译使用过的库。)

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

// 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