【问题标题】:OO Javascript: good way to combine prototypal inheritance with private vars?OO Javascript:将原型继承与私有变量结合起来的好方法?
【发布时间】:2009-11-27 20:37:21
【问题描述】:

OO Javascript constructor pattern: neo-classical vs prototypal 中,我了解到使用原型继承的构造函数可以比使用所谓的neo-classical 模式和闭包的构造函数快10 倍(或更多),正如Crockford 在他的“Good Parts”一书和演示文稿中提出的那样。

出于这个原因,一般来说,更喜欢原型继承似乎是正确的事情。

问题有没有办法将原型继承与模块模式结合起来,以便在必要时允许私有变量?

我的想法是:

// makeClass method - By John Resig (MIT Licensed)
function makeClass(){
  return function(args){
    if ( this instanceof arguments.callee ) {
      if ( typeof this.init == "function" )
        this.init.apply( this, args.callee ? args : arguments );
    } else
      return new arguments.callee( arguments );
  };
}


// =======================================================

var User = makeClass();

// convention; define an init method and attach to the prototype
User.prototype.init = function(first, last){
  this.name = first + " " + last;
};


User.prototype.doWork = function (a,b,c) {/* ... */ }; 

User.prototype.method2= (function (a,b,c) {

    // this code is run once per class

    return function(a,b,c) {
        // this code gets run with each call into the method 
        var _v2 = 0;
        function inc() {
            _v2++;
        }

        var dummy = function(a,b,c) {
            /* ... */
            inc();
            WScript.echo("doOtherWork(" + this.name + ") v2= " + _v2);
            return _v2;
        };

        var x = dummy(a,b,c);
        this.method2 = dummy; // replace self
        return x;
    };

})();

这不太对。但它说明了这一点。

有没有办法做到这一点,值得吗?

【问题讨论】:

    标签: javascript oop class-design


    【解决方案1】:

    一般来说,首选原型继承似乎是正确的选择

    嗯...当然,这是在 JavaScript 中做的更自然、更自然的事情。但是 JavaScript 做的这么多是错误的,这不一定是恭维!

    当然,当性能不是问题时,拥有自己的每个方法的绑定副本的对象比共享其方法的对象更容易处理,因为您可以传递对 object.method 的引用而不必闭包委托或 function.bind。

    有没有办法将原型继承与模块模式结合起来,以便在必要时允许私有变量?

    你想从私有变量中得到什么?如果它是通过封装实现的某种 Java 风格的安全理念,我会放弃它,而只是按照 Python 的方式来做:在成员名称的开头加上下划线,任何想要从外部使用的人都会被适当地警告它不受支持,可能会搞砸。在同一页面上执行的 JavaScript 代码中从来没有安全边界可以保证您的隐私真正保密。

    如果您想要避免在调用方法时必须找到 this 的正确副本,您可以在初始化程序中手动绑定方法方法:

    var Thing= makeClass();
    Thing.prototype.init= function(a) {
        this._a= a;
        this.showA= this.showA.bind(this);
    };
    Thing.prototype.showA= function() {
        alert(this._a);
    };
    
    thing= new Thing(3);
    setTimeout(thing.showA, 1000); // will work as `thing` has its own bound copy of `showA`
    

    (function.bind 是未来的 JavaScript,您现在可以破解 Function.prototype 直到浏览器支持它。)

    这自然会失去一些基于原型的对象的轻量级特性,但至少您仍然可以让它们共享不是方法的成员,以及永远不会用作委托的方法,只要清楚即可并且您始终可以记住哪些方法是您可以通过这种方式使用的方法。

    如果您只是希望能够键入私有变量名称而不必一直输入this.,那么,是的,您必须使用闭包来完成。您的示例世界可能会从初始化程序调用而不是使用首次自我编写更清晰:

    var User= makeClass();
    User.prototype.init= function(first, last){
        this.name= first+' '+last;
        this.method2= this._method2factory();
    };
    User.prototype._method2factory= function() {
        var _v2= 0;
        function inc() {
            _v2++;
        }
    
        return function method2(a,b,c) {
            /* ... */
            inc();
            WScript.echo('doOtherWork('+this.name+') v2= '+_v2);
            return _v2;
        };
    };
    

    但我不确定这是否比写 this._v2this._inc() 更能让你受益。

    【讨论】:

      【解决方案2】:

      我不确定我是否理解您的问题。但从我认为我理解的内容出发......

      function Foo () { /*constructor*/
          var counter = 0;  /* private variable */
          this.incrementCounter=function () {  /*privileged function */
              counter++
          }
          this.getCounter=function () {   /*privileged function */
              return counter;
          }
      
      }
       /*public functions. Note: this pattern destroys constructor property. 
       Lesson: Don't depend on the constructor property! */
      Foo.prototype = {
         toString: function () {
                var string = "";
                var count = this.getCounter();
                while(count--) {
                   string+="*"
                }
                return string;
           }  
      
      }
      
      var bar = new Foo();
      bar.incrementCounter();
      bar.incrementCounter();
      bar.toString();  /* in theory, this returns "**".. haven't tested code */
      

      【讨论】:

      • toString: function(){ return Array(this.getCounter()+1).join('*'); }
      【解决方案3】:

      你可以看看 https://github.com/riga/jclass

      我想这就是你要找的。​​p>

      【讨论】:

      • 请添加实际答案。
      【解决方案4】:

      就个人而言,我更喜欢以下语法:

      var keyValueStore = (function() {
          // Singleton private properties
          var count = 0;
      
          var kvs = function() {
              // Instance private / privileged properties
              count++;
          };
      
          kvs.prototype = {
              // Instance public properties
              'data' : {},
              'get' : function(key) { return this.data[key]; },
              'set' : function(key, value) { this.data[key] = value; },
              'delete' : function(key) { delete this.data[key]; },
              'getLength' : function() {
                  var l = 0;
                  for (p in this.data) l++;
                  return l;
              }
          };
      
          return  {
              // Singleton public properties
              'create' : function() { return new kvs(); },
              'count' : function() { return count; }
          };
      })();
      

      使用这种语法,您可以拥有一个单例对象,可以使用原型继承创建实例,并且可以在多个级别定义私有属性。

      你这样使用它:

      kvs = keyValueStore.create();
      kvs.set('Tom', "Baker");
      kvs.set('Daisy', "Hostess");
      var profession_of_daisy = kvs.get('Daisy');
      kvs.delete('Daisy');
      console.log(keyValueStore.count());
      

      【讨论】:

        猜你喜欢
        • 2015-03-19
        • 2011-04-06
        • 2016-07-19
        • 2011-12-10
        • 2017-04-21
        • 2012-10-31
        • 2014-11-17
        • 2012-03-06
        • 2013-06-18
        相关资源
        最近更新 更多