Pregunta

Vi una charla de Douglas Crockford en las partes buenas en Javascript y mis ojos fueron abiertos. En un momento dado, dijo algo así como "Javascript es el único lenguaje en el que los buenos programadores creen que pueden usarlo de manera efectiva, sin aprenderlo". Entonces me di cuenta, yo soy ese chico.

En esa charla, hizo algunas declaraciones que, para mí, fueron bastante sorprendentes y perspicaces. Por ejemplo, JavaScript es el lenguaje de programación más importante del planeta. O es el idioma más popular del planeta. Y, que se rompe de muchas maneras serias.

La declaración más sorprendente que hizo, para mí, era "nuevo es peligroso". Él ya no lo usa. Él no usa esto tampoco.

Presentó un patrón interesante para un constructor en Javascript, uno que permite variables de miembros públicos y privados, y no se basa en new , ni en this . Se parece a esto:

// neo-classical constructor
var container =  function(initialParam) {
    var instance = {}; // empty object 

    // private members
    var privateField_Value = 0;
    var privateField_Name = "default";

    var privateMethod_M1 = function (a,b,c) {
        // arbitrary
    }; 

    // initialParam is optional
    if (typeof initialParam !== "undefined") {
        privateField_Name= initialParam;
    }

    // public members
    instance.publicMethod = function(a, b, c) {
        // because of closures,
        // can call private methods or
        // access private fields here. 
    };

    instance.setValue = function(v) {
        privateField_Value = v;
    };

    instance.toString = function(){
        return "container(v='" + privateField_Value + "', n='" + privateField_Name + "')";
    };

    return instance;
}


// usage
var a = container("Wallaby");
WScript.echo(a.toString()); 
a.setValue(42);
WScript.echo(a.toString()); 

var b = container();
WScript.echo(b.toString()); 

EDIT : código actualizado para cambiar a nombre de clase en minúscula.

Este patrón ha evolucionado de modelos de uso anteriores de Crockford .

Pregunta: ¿Utiliza este tipo de patrón de constructor? ¿Te parece comprensible? ¿Tienes uno mejor?

¿Fue útil?

Solución

Esto se parece a la versión no individual del patrón del módulo , mediante el cual se pueden simular variables privadas aprovechando los cierres de " JavaScript " ;.

Me gusta ( un poco ... ). Pero realmente no veo la ventaja en las variables privadas realizadas de esta manera, especialmente cuando significa que cualquier método nuevo agregado (después de la inicialización) no tiene acceso a las variables privadas.

Además, no aprovecha el modelo prototípico de JavaScript. Todos sus métodos y propiedades deben inicializarse CADA vez que se llame al constructor, esto no sucede si tiene métodos almacenados en el prototipo del constructor. El hecho es que usar el patrón de constructor / prototipo convencional es mucho más rápido. ¿De verdad crees que las variables privadas hacen que valga la pena el rendimiento?

Este tipo de modelo tiene sentido con el patrón del módulo porque solo se está inicializando una vez (para crear un pseudo-singleton), pero no estoy tan seguro de que tenga sentido aquí.

  

¿Utiliza este tipo de patrón de constructor?

No, aunque sí uso su variante de singleton, el patrón del módulo ...

  

¿Te parece comprensible?

Sí, es legible y bastante claro, pero no me gusta la idea de agrupar todo dentro de un constructor como ese.

  

¿Tienes uno mejor?

Si realmente necesitas variables privadas, entonces quédate con ellas por todos los medios. De lo contrario, solo use el patrón de constructor / prototipo convencional ( a menos que comparta el miedo de Crockford a la nueva / esta combo ):

function Constructor(foo) {
    this.foo = foo;
    // ...
}

Constructor.prototype.method = function() { };

Otras preguntas similares relacionadas con las opiniones de Doug sobre el tema:

Otros consejos

Evito este patrón ya que a la mayoría de las personas les cuesta más leer. En general, sigo dos enfoques:

  1. Si solo tengo uno de algo, entonces uso objetos anónimos:

    var MyObject = {
        myMethod: function() {
            // do something
        }
    };
    
  2. Para más de una cosa, uso la herencia prototípica de javascript estándar

    var MyClass = function() {
    };
    MyClass.prototype.myMethod = function() {
        // do something
    };
    
    var myObject = new MyClass();
    

(1) es mucho más fácil de leer, entender y escribir. (2) es más eficiente cuando hay múltiples objetos. El código de Crockford creará una nueva copia de las funciones dentro del constructor cada vez. El cierre también tiene la desventaja de ser más difícil de depurar.

Aunque pierdas variables verdaderamente privadas, prefieres los miembros supuestos a ser privados con _ como convención.

este es un problema admitido difícil en javascript, pero se puede solucionar utilizando .call y .apply para configurarlo correctamente. También a menudo uso var self = this; para crear una variable de cierre para usar como this dentro de las funciones definidas dentro de una función miembro.

MyClass.prototype.myMethod = function() {
    var self = this;

    // Either
    function inner1() {
        this.member();
    }
    inner1.call(this);

    // Or
    function inner2() {
        self.member();
    }
    inner2();
};
  

¿Utiliza este tipo de patrón de constructor?

No

  

¿Te parece comprensible?

Sí, es muy sencillo.

  

¿Tienes uno mejor?

Todavía no he visto la conversación, pero lo estaré tratando en breve. Hasta entonces, no veo el peligro de usar new y this , y he aquí por qué:

Sin haber escuchado sus puntos, solo puedo asumir que él sugiere mantenerse alejado de tales cosas debido a la naturaleza de this , y cómo es propenso a cambiar dependiendo del contexto en el que se encuentre El método se ejecuta (directamente sobre el objeto original o como devolución de llamada, etc.). Como educador, puede estar enseñando a evitar estas palabras clave debido a la demografía de desarrolladores en gran medida inconscientes e inexpertos que incursionan en JavaScript sin asimilar primero la naturaleza del lenguaje. Para los desarrolladores experimentados que están íntimamente familiarizados con el lenguaje, no estoy convencido de que sea necesario evitar esta característica del lenguaje, que le otorga una increíble cantidad de flexibilidad (que es completamente diferente a evitar cosas como con ). Dicho todo esto, lo estaré viendo ahora.

En cualquier caso, cuando no uso algún tipo de marco que admita la herencia automágica (como dojo.declare ), o cuando escribo un objeto independiente del marco, actualmente tomo el siguiente enfoque.

Definition:

var SomeObject = function() {
    /* Private access */
    var privateMember = "I am a private member";
    var privateMethod = bindScope(this, function() {
        console.log(privateMember, this.publicMember);
    });

    /* Public access */
    this.publicMember = "I am a public member";

    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod();
    }
};

Uso :

var o = new SomeObject();
o.privateMethodWrapper();

Donde bindScope es una función de utilidad similar a dojo.hitch de Dojo o Function.prototype.bind de Prototype.

En su libro se llama herencia funcional (página 52). Todavía no lo uso. Es comprensible si aprendes javascript de Douglas. No creo que haya un mejor enfoque. Este es bueno porque:

  • permite a un programador crear miembros privados

  • protege a los miembros privados y elimina este a diferencia de la supuesta privacidad (los miembros privados comienzan con _)

  • hace que la herencia sea fluida y sin códigos innecesarios

Sin embargo, tiene algunos inconvenientes. Puede leer más sobre esto aquí: http://www.bolinfest.com/javascript/inheritance. php

  

¿Utilizas este tipo de patrón de constructor?

He usado este patrón antes cuando estaba aprendiendo JavaScript por primera vez y me topé con la literatura de Douglas Crockford.

  

¿Te parece comprensible?

Mientras entiendas los cierres, este método es claro.

  

¿Tienes uno mejor?

Depende de lo que estés tratando de lograr. Si está intentando escribir una biblioteca que sea lo más idónea posible, entonces puedo entender completamente el uso de variables privadas. Puede ayudar a evitar que los usuarios perturben inadvertidamente la integridad de su objeto. Sí, el costo en el tiempo podría ser un poco mayor (aproximadamente 3 veces más grande), pero el costo del tiempo es un argumento discutible hasta que realmente afecta su aplicación. Noté que la prueba mencionada en una publicación anterior ( prueba ) no tiene en cuenta el tiempo que lleva acceder Las funciones de un objeto.

Descubrí que el método prototipo / constructor generalmente conduce a un tiempo de construcción más rápido, sí, pero no necesariamente ahorra tiempo en la recuperación. Hay un costo adicional de usar la cadena prototipo para encontrar una función en lugar de tener una función asociada directamente al objeto que está usando. Entonces, si está llamando a muchas funciones de prototipo y no está construyendo muchos objetos, podría tener más sentido utilizar el patrón de Crockford.

Sin embargo, tiendo a no usar variables privadas si no estoy escribiendo mi código para una gran audiencia. Si estoy con un equipo de personas que son todos los codificadores capaces, generalmente puedo confiar en que sabrán cómo usar mi código si codifico y hago comentarios. Luego uso algo parecido al patrón de Crockford sin miembros / métodos privados.

Si necesitamos crear siempre el mismo tipo de objeto, prefiero usar el patrón de constructor de prototipos , ya que permite compartir métodos y propiedades a través de delegación automática a través de cadena de prototipos .

También podemos mantener los objetos privados; mira este enfoque:

var ConstructorName = (function() { //IIFE
    'use strict';

    function privateMethod (args) {}

    function ConstructorName(args) {
        // enforces new (prevent 'this' be the global scope)
        if (!(this instanceof ConstructorName)) {
            return new ConstructorName(args);
        }
        // constructor body
    }

    // shared members (automatic delegation)
    ConstructorName.prototype.methodName = function(args) {
        // use private objects
    };

    return ConstructorName;
}());

Recomiendo revisar esta respuesta, donde podemos encontrar una forma interesante de crear una función constructora: Forma diferente de crear un Javascript Objeto

Este es un ejemplo en el que puede utilizar el enfoque de constructor de prototipos: ¿Cómo creo un error personalizado en JavaScript?

Creo que puede ayudar a la discusión a informar aquí cuál es el punto principal sobre este patrón, como lo explica Douglas Crockford en su video de presentación de "JavaScript avanzado".

El punto principal es evitar el uso del nuevo operador. Porque cuando alguien olvida usar el nuevo operador cuando llama a una función de constructor de objetos, los miembros de objetos agregados en el constructor terminan en el espacio de nombres global. Tenga en cuenta que, en este caso, no hay ninguna advertencia o error en tiempo de ejecución que informe del error, el espacio de nombres global simplemente se contamina. Y esto ha demostrado tener serias implicaciones de seguridad y ser la fuente de muchos errores difíciles de diagnosticar.

Ese es el punto principal de este patrón de Powerconstructor.

La parte privada / privilegiada es secundaria. Está bien hacerlo de una manera diferente. Así es que no se utilizan miembros prototipo.

HTH luKa

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top