Constructores en objetos JavaScript

Resuelto Ali asked hace 15 años • 19 respuestas

¿Pueden las clases/objetos de JavaScript tener constructores? ¿Cómo se crean?

Ali avatar Jul 11 '09 23:07 Ali
Aceptado

Usando prototipos:

function Box(color) // Constructor
{
    this.color = color;
}

Box.prototype.getColor = function()
{
    return this.color;
};

Ocultar "color" (se parece un poco a una variable miembro privada):

function Box(col)
{
   var color = col;

   this.getColor = function()
   {
       return color;
   };
}

Uso:

var blueBox = new Box("blue");
alert(blueBox.getColor()); // will alert blue

var greenBox = new Box("green");
alert(greenBox.getColor()); // will alert green
Nick avatar Jul 12 '2009 08:07 Nick

Aquí hay una plantilla que a veces uso para un comportamiento similar a la programación orientada a objetos en JavaScript. Como puede ver, puede simular miembros privados (tanto estáticos como de instancia) mediante cierres. Lo que new MyClass()devolverá es un objeto con sólo las propiedades asignadas al thisobjeto y en el prototypeobjeto de la "clase".

var MyClass = (function () {
    // private static
    var nextId = 1;

    // constructor
    var cls = function () {
        // private
        var id = nextId++;
        var name = 'Unknown';

        // public (this instance only)
        this.get_id = function () { return id; };

        this.get_name = function () { return name; };
        this.set_name = function (value) {
            if (typeof value != 'string')
                throw 'Name must be a string';
            if (value.length < 2 || value.length > 20)
                throw 'Name must be 2-20 characters long.';
            name = value;
        };
    };

    // public static
    cls.get_nextId = function () {
        return nextId;
    };

    // public (shared across instances)
    cls.prototype = {
        announce: function () {
            alert('Hi there! My id is ' + this.get_id() + ' and my name is "' + this.get_name() + '"!\r\n' +
                  'The next fellow\'s id will be ' + MyClass.get_nextId() + '!');
        }
    };

    return cls;
})();

Me preguntaron sobre la herencia usando este patrón, así que aquí va:

// It's a good idea to have a utility class to wire up inheritance.
function inherit(cls, superCls) {
    // We use an intermediary empty constructor to create an
    // inheritance chain, because using the super class' constructor
    // might have side effects.
    var construct = function () {};
    construct.prototype = superCls.prototype;
    cls.prototype = new construct;
    cls.prototype.constructor = cls;
    cls.super = superCls;
}

var MyChildClass = (function () {
    // constructor
    var cls = function (surName) {
        // Call super constructor on this instance (any arguments
        // to the constructor would go after "this" in call(…)).
        this.constructor.super.call(this);

        // Shadowing instance properties is a little bit less
        // intuitive, but can be done:
        var getName = this.get_name;

        // public (this instance only)
        this.get_name = function () {
            return getName.call(this) + ' ' + surName;
        };
    };
    inherit(cls, MyClass); // <-- important!

    return cls;
})();

Y un ejemplo para usarlo todo:

var bob = new MyClass();
bob.set_name('Bob');
bob.announce(); // id is 1, name shows as "Bob"

var john = new MyChildClass('Doe');
john.set_name('John');
john.announce(); // id is 2, name shows as "John Doe"

alert(john instanceof MyClass); // true

Como puede ver, las clases interactúan correctamente entre sí (comparten la identificación estática de MyClass, el announcemétodo usa el get_namemétodo correcto, etc.)

Una cosa a tener en cuenta es la necesidad de ocultar las propiedades de la instancia. De hecho, puede hacer que la inheritfunción revise todas las propiedades de la instancia (usando hasOwnProperty) que sean funciones y agregue automáticamente una super_<method name>propiedad. Esto le permitiría llamar this.super_get_name()en lugar de almacenarlo en un valor temporal y llamarlo vinculado usando call.

Para los métodos del prototipo, no necesita preocuparse por lo anterior; si desea acceder a los métodos del prototipo de la superclase, simplemente puede llamar a this.constructor.super.prototype.methodName. Si desea hacerlo menos detallado, por supuesto puede agregar propiedades de conveniencia. :)

Blixt avatar Jul 11 '2009 16:07 Blixt

Me parece que la mayoría de ustedes están dando ejemplos de captadores y definidores, no de un constructor, es decir, http://en.wikipedia.org/wiki/Constructor_(object- Oriented_programming) .

lunched-dan estaba más cerca pero el ejemplo no funcionó en jsFiddle.

Este ejemplo crea una función constructora privada que solo se ejecuta durante la creación del objeto.

var color = 'black';

function Box()
{
   // private property
   var color = '';

   // private constructor 
   var __construct = function() {
       alert("Object Created.");
       color = 'green';
   }()

   // getter
   this.getColor = function() {
       return color;
   }

   // setter
   this.setColor = function(data) {
       color = data;
   }

}

var b = new Box();

alert(b.getColor()); // should be green

b.setColor('orange');

alert(b.getColor()); // should be orange

alert(color); // should be black

Si quisiera asignar propiedades públicas, entonces el constructor podría definirse como tal:

var color = 'black';

function Box()
{
   // public property
   this.color = '';

   // private constructor 
   var __construct = function(that) {
       alert("Object Created.");
       that.color = 'green';
   }(this)

   // getter
   this.getColor = function() {
       return this.color;
   }

   // setter
   this.setColor = function(color) {
       this.color = color;
   }

}

var b = new Box();

alert(b.getColor()); // should be green

b.setColor('orange'); 

alert(b.getColor()); // should be orange

alert(color); // should be black
Jon avatar Jul 09 '2012 04:07 Jon