javascript - objeto - ¿Ventajas del uso del prototipo, frente a la definición de métodos directamente en el constructor?
javascript prototype constructor (4)
Esta pregunta ya tiene una respuesta aquí:
- ¿Uso de ''prototipo'' vs. ''este'' en JavaScript? 14 respuestas
Me pregunto si hay alguna ventaja de usar alguno de estos sobre el otro, y ¿hacia dónde debo ir?
Enfoque del constructor:
var Class = function () {
this.calc = function (a, b) {
return a + b;
};
};
Enfoque prototipo:
var Class = function () {};
Class.prototype.calc = function (a, b) {
return a + b;
};
No me gusta eso, usando el prototipo, las definiciones de métodos están separadas de la clase, y no sé si hay alguna razón específica por la que deba usar esto solo en el primer enfoque.
Además, ¿hay algún beneficio de usar una función literal para definir una "clase", sobre solo la definición de la función:
var Class = function () {};
vs
function Class () {};
¡Gracias!
En primer lugar debes usar el objeto literal como este:
var Class = {
calc: function (a, b) {
return a + b;
}
};
Esta notación es más limpia y también hace que sea obvio que, en Javascript, los objetos son solo hashes, no algo respaldado por una receta, como una clase predefinida.
La diferencia entre las definiciones es que si agrega un método al prototipo, solo habrá un método creado en la memoria para todas las instancias. Entonces, si tiene un método genérico y un objeto que se crea / utiliza en varias instancias, debe agregar el método al prototipo.
La ventaja del enfoque prototipo es la eficiencia. Hay un objeto de función calc()
compartido entre todos Class
objetos de la Class
(me refiero a los objetos creados al llamar al constructor de la Class
). La otra forma (asignando métodos dentro del constructor) crea un nuevo objeto de función para cada objeto de Class
, usando más memoria y tomando más tiempo de procesamiento al llamar al constructor de la Class
. Sin embargo, este enfoque tiene una ventaja: el método calc()
tiene acceso a las variables locales dentro del constructor, que puede utilizar para su ventaja:
function Class() {
var calcCallCount = 0;
this.calc = function (a, b) {
++calcCallCount;
alert("Calc called " + calcCallCount + " times");
return a + b;
};
};
Con respecto a var Class = function() {...}
versus function Class() {...}
, generalmente prefiero esto último porque significa que la función tiene un nombre, lo que puede ser útil para la depuración. La otra diferencia es que la última versión (una declaración de función ) se alza, lo que significa que está disponible en todas partes dentro del alcance en el que se define, no solo después de la definición. Sin embargo, algunas personas prefieren usar la primera (una expresión de función ) en todas partes.
Los métodos que se heredan a través de la cadena del prototipo se pueden cambiar universalmente para todas las instancias, por ejemplo:
function Class () {}
Class.prototype.calc = function (a, b) {
return a + b;
}
// Create 2 instances:
var ins1 = new Class(),
ins2 = new Class();
// Test the calc method:
console.log(ins1.calc(1,1), ins2.calc(1,1));
// -> 2, 2
// Change the prototype method
Class.prototype.calc = function () {
var args = Array.prototype.slice.apply(arguments),
res = 0, c;
while (c = args.shift())
res += c;
return res;
}
// Test the calc method:
console.log(ins1.calc(1,1,1), ins2.calc(1,1,1));
// -> 3, 3
Observe cómo cambiar el método aplicado a ambas instancias? Esto se debe a que ins1
y ins2
comparten la misma función calc()
. Para hacer esto con los métodos públicos creados durante la construcción, tendría que asignar el nuevo método a cada instancia que se haya creado, lo que es una tarea incómoda. Esto se debe a que ins1
y ins2
tendrían sus propias funciones calc()
creadas individualmente.
Otro efecto secundario de la creación de métodos dentro del constructor es un peor rendimiento. Cada método debe crearse cada vez que se ejecuta la función constructora. Los métodos en la cadena del prototipo se crean una vez y luego "heredan" cada instancia. En la otra cara de la moneda, los métodos públicos tienen acceso a variables "privadas", lo que no es posible con métodos heredados.
En cuanto a su function Class() {}
vs var Class = function () {}
question, la primera se "eleva" a la parte superior del alcance actual antes de la ejecución. Para este último, la declaración de la variable se eleva, pero no la asignación. Por ejemplo:
// Error, fn is called before the function is assigned!
fn();
var fn = function () { alert("test!"); }
// Works as expected: the fn2 declaration is hoisted above the call
fn2();
function fn2() { alert("test!"); }
var YourClass = function(){
var privateField = "somevalue";
this.publicField = "somevalue";
this.instanceMethod1 = function(){
//you may access both private and public field from here:
//in order to access public field, you must use "this":
alert(privateField + "; " + this.publicField);
};
}
YourClass.prototype.instanceMethod2 = function(){
//you may access only public field 2 from this method, but not private fields:
alert(this.publicField);
//error: drawaback of prototype methods:
alert(privateField);
};
Ventajas de los métodos prototipo:
Cuando define métodos a través de prototipo, se comparten entre todas las instancias de YourClass. Como resultado, el tamaño total de tales instancias es <que si define métodos en el constructor; Hay pruebas que muestran cómo la definición de método a través del prototipo reduce el tamaño total de la página html y, como resultado, la velocidad de su carga.
otra ventaja de los métodos, definidos a través de prototipo, es que cuando utiliza clases heredadas, puede anular dichos métodos y, en el método de anulación de la clase derivada, puede invocar el método de la clase base con el mismo nombre, pero con métodos definidos en el constructor. no puedes hacer esto.