OO Javascript: ¿una buena forma de combinar la herencia prototípica con las variables privadas?

StackOverflow https://stackoverflow.com/questions/1810556

  •  05-07-2019
  •  | 
  •  

Pregunta

En OO patrón de constructor de Javascript: neoclásico vs prototípico , aprendí que los constructores que usan la herencia prototípica pueden ser 10 veces más rápidos (o más) que los constructores que usan el llamado patrón neoclásico con cierres, como lo propone Crockford en su "Good Parts". Libro y presentaciones.

Por esa razón, parece que preferir la herencia prototípica parece ser lo correcto, en general.

Pregunta ¿Hay alguna forma de combinar la herencia prototípica con el patrón del módulo para permitir variables privadas cuando sea necesario?

Lo que estoy pensando es:

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

})();

Eso no está del todo bien. Pero ilustra el punto.

¿Hay una manera de hacer esto y vale la pena?

¿Fue útil?

Solución

  

preferir la herencia prototípica parece ser lo correcto, en general

Bueno ... es, sin duda, lo más natural y nativo que se puede hacer en JavaScript. ¡Pero tanto JavaScript hace mal es que esto no es necesariamente un cumplido!

Ciertamente, cuando el rendimiento no es un problema, los objetos que obtienen sus propias copias encuadernadas de cada método son más fáciles de manejar que los objetos que comparten sus métodos, ya que solo puede pasar una referencia a object.method sin tener que hacer un delegado de cierre o function.bind.

  

¿Hay alguna forma de combinar la herencia prototípica con el patrón del módulo para permitir variables privadas cuando sea necesario?

¿Qué quieres de las variables privadas? Si se trata de una idea de seguridad de estilo Java por encapsulación, renuncio a eso y simplemente lo hago a la manera de Python: ponga un subrayado en el inicio del nombre del miembro y cualquier persona que quiera usar desde el exterior recibirá una advertencia adecuada de que no es compatible y puede arruinar. Nunca hay un límite de seguridad dentro del código JavaScript que se ejecute en la misma página que justifique mantener a sus privados realmente privados

Si lo que desea es evitar tener que localizar la copia correcta de this cuando se llama al método, puede enlazar métodos de métodos manualmente en el inicializador:

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 es JavaScript en el futuro que puedes hackear en el prototipo Function. hasta que los navegadores lo admitan)

Esto naturalmente pierde parte de la naturaleza liviana de los objetos basados ??en prototipos, pero al menos todavía puede hacer que compartan miembros que no son métodos y métodos que nunca se usarán como delegados, siempre que esté claro. y siempre puedes recordar qué métodos son los que puedes usar de esta manera.

Si simplemente desea poder escribir un nombre de variable privada sin tener que poner esto. todo el tiempo, sí, tendrá que hacerlo con un cierre. Es posible que su mundo de ejemplo sea un poco más claro desde el inicializador en lugar de utilizar la autoescritura por primera vez:

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

Pero no estoy realmente seguro de que esto te ofrezca mucho en comparación con solo escribir this._v2 y this._inc () .

Otros consejos

No estoy totalmente seguro de entender tu pregunta. Pero a partir de lo que creo que entiendo ...

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

Puedes echar un vistazo a https://github.com/riga/jclass

Creo que eso es lo que estás buscando.

Personalmente, prefiero la siguiente sintaxis:

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; }
    };
})();

Con esta sintaxis, tiene un objeto singleton, la posibilidad de crear instancias con herencia de prototipo y la posibilidad de definir propiedades privadas en varios niveles.

Lo usas así:

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());
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top