OO Javascript: хороший способ объединить наследование прототипа с частными переменными? - PullRequest
4 голосов
/ 27 ноября 2009

В OO шаблон конструктора Javascript: нео-классический против прототипа , я узнал, что конструкторы, использующие наследование прототипа, могут быть в 10 раз быстрее (или более), чем конструкторы, использующие так называемый шаблон neo-classical с замыканиями предложенный Крокфордом в его книге «Хорошие детали» и презентациях.

По этой причине кажется, что предпочтение наследования прототипа в общем-то кажется правильным.

Вопрос Есть ли способ объединить наследование прототипа с шаблоном модуля, чтобы разрешить закрытые переменные при необходимости?

Я думаю следующее:

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

})();

Это не совсем верно. Но это иллюстрирует суть.

Есть ли способ сделать это и стоит ли это того?

Ответы [ 4 ]

5 голосов
/ 28 ноября 2009

предпочитать наследование прототипа в общем-то кажется правильным, в общем

Ну ... это более естественная вещь в 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._v2 и this._inc().

1 голос
/ 28 ноября 2009

Я не совсем уверен, что понимаю ваш вопрос. Но исходя из того, что я думаю, я понимаю ...

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 */
0 голосов
/ 10 июля 2015

Лично я предпочитаю следующий синтаксис:

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());
0 голосов
/ 22 мая 2013

Вы можете взглянуть на https://github.com/riga/jclass

Я думаю, это то, что вы ищете.

...