Herencia de JavaScript: Object.create vs new

123

En JavaScript, ¿cuál es la diferencia entre estos dos ejemplos?

Requisito previo:

function SomeBaseClass(){
}

SomeBaseClass.prototype = {
    doThis : function(){
    },

    doThat : function(){
    }
}

Ejemplo de herencia A usando Object.create:

function MyClass(){
}

MyClass.prototype = Object.create(SomeBaseClass.prototype);

Ejemplo de herencia B usando la nueva palabra clave

function MyClass(){
}

MyClass.prototype = new SomeBaseClass();

Ambos ejemplos parecen hacer lo mismo. ¿Cuándo elegirías uno sobre el otro?

Una pregunta adicional: considere el código en el siguiente enlace (línea 15), donde se almacena una referencia al propio constructor de la función en el prototipo. ¿Por qué es útil esto?

https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js

Extracto (si no desea abrir el enlace):

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader
}
ChrisRich
fuente
23
¿Por qué diablos está marcado como duplicado? La otra pregunta, y las respuestas, ni siquiera mencionan Object.create. Esto es un error y debe reabrirse.
Scott Rippey
2
En todo caso, es un duplicado de stackoverflow.com/questions/4166616/…
Scott Rippey
1
13 votos en ese comentario y aún no se reabrieron ..?!
TJ

Respuestas:

111

En su pregunta ha mencionado que Both examples seem to do the same thing, no es cierto en absoluto, porque

Tu primer ejemplo

function SomeBaseClass(){...}
SomeBaseClass.prototype = {
    doThis : function(){...},
    doThat : function(){...}
}
function MyClass(){...}
MyClass.prototype = Object.create(SomeBaseClass.prototype);

En este ejemplo, solo está heredando, SomeBaseClass' prototypepero ¿qué pasa si tiene una propiedad en su SomeBaseClassgusto?

function SomeBaseClass(){ 
    this.publicProperty='SomeValue'; 
}

y si lo usas como

var obj=new MyClass();
console.log(obj.publicProperty); // undefined
console.log(obj);​

El objobjeto no tendrá publicPropertypropiedades como en este ejemplo .

Tu segundo ejemplo

MyClass.prototype = new SomeBaseClass();

Está ejecutando la constructorfunción, haciendo una instancia SomeBaseClassy heredando todo el SomeBaseClassobjeto. Entonces, si usas

    var obj=new MyClass();
    console.log(obj.publicProperty); // SomeValue
    console.log(obj);​

En este caso, su publicPropertypropiedad también está disponible para el objobjeto como en este ejemplo .

Dado Object.createque no está disponible en algunos navegadores antiguos, en ese caso puede usar

if(!Object.create)
{
    Object.create=function(o){
        function F(){}
        F.prototype=o;
        return new F();
    }
}

El código anterior solo agrega Object.createfunción si no está disponible para que pueda usar la Object.createfunción y creo que el código anterior describe lo que Object.createrealmente hace. Espero que ayude de alguna manera.

El alfa
fuente
66
Hola jeque Gracias por tus esfuerzos en esto. Sí, la diferencia es que el constructor se ejecuta en el segundo ejemplo pero no en el primero. (lo cual es deseable en mi caso). Con respecto a la propiedad pública indefinida que no se hereda de la implementación súper, solo necesita llamar a súper en el constructor del niño: SomeBaseClass.call (this). Compruebe este violín: jsfiddle.net/NhQGB
ChrisRich
He estado buscando una forma súper simple de herencia adecuada en JS sin usar ninguna biblioteca / framework. Creo que este ejemplo (en mi violín anterior) es el mejor enfoque para los navegadores modernos. ¿Quizás el polyfill Object.Create podría agregar soporte para navegadores heredados?
ChrisRich
básicamente, para resumir, si haces .prototype = new, entonces estás heredando cualquier valor que le hayas asignado a esto en la clase base, y cuando haces object.create, solo estás heredando lo que está en el prototipo en la clase base, ¿verdad?
davidjnelson
¿Esto "MyClass.prototype = new SomeBaseClass ()" significa que se crea una nueva instancia de SomeBaseClass cuando la instancia de MyClass aún no se ha creado?
No, solo cuando crea el objeto principal, el prototipo se establece en eso SomeBaseClass.
The Alpha
39

Ambos ejemplos parecen hacer lo mismo.

Eso es cierto en tu caso.

¿Cuándo elegirías uno sobre el otro?

Cuando SomeBaseClasstiene un cuerpo de función, esto se ejecuta con la newpalabra clave. Esto generalmente no es intencionado: solo desea configurar la cadena de prototipos. En algunos casos, incluso podría causar problemas graves porque realmente crea una instancia de un objeto, cuyas variables de ámbito privado son compartidas por todas las MyClassinstancias ya que heredan los mismos métodos privilegiados. Otros efectos secundarios son imaginables.

Por lo tanto, generalmente debería preferir Object.create. Sin embargo, no es compatible con algunos navegadores heredados; esa es la razón por la que ve el newenfoque demasiado frecuente, ya que a menudo no hace daño (obvio). También eche un vistazo a esta respuesta .

Bergi
fuente
Esta es la mejor respuesta, bien explicada
spex
8

La diferencia se vuelve obvia si se usa Object.create()como se pretende. En realidad, oculta completamente la prototypepalabra de su código, hará el trabajo bajo el capó. Usando Object.create(), podemos ir como

var base =  {
    doThis : function(){
    },

    doThat : function(){
    }
};

Y luego podemos extender / heredar otros objetos de este

var myObject = Object.create( base );
// myObject will now link to "base" via the prototype chain internally

Así que este es otro concepto, una forma de inserción más "orientada a objetos". No hay una "función de constructor" fuera de la caja usando, Object.create()por ejemplo. Pero, por supuesto, podría crear y llamar a una función constructora autodefinida dentro de esos objetos.

Un argumento para el uso Object.create()es que puede tener un aspecto más natural, a mezclar / * * heredan de otros objetos, que el uso de Javascripts manera predeterminada .

jAndy
fuente
77
Object.createrealmente no puede reemplazar el enfoque clásico con newcuando se necesita una función de constructor
Bergi
1
Definitivamente puede @Bergi. Mira esta publicación de blog: davidwalsh.name/javascript-objects-deconstruction . También puede pasar un objeto de inicialización como segundo parámetro a Object.create ().
LocalPCGuy
@LocalPCGuy: No, no puede, porque Object.createno llama a una función que sería necesaria para crear cierres. Por supuesto, con Object.createusted puede poner un initmétodo en sus objetos y llamarlo de inmediato, y tal vez incluso regrese thispara que obtenga una sintaxis concisa, pero espere, entonces ese es un constructor.
Bergi
1
Llamar a una función init funciona de manera similar a un constructor, pero no es un constructor. Y ese método le permite reemplazar exactamente el enfoque clásico con Object.create. Y el modelo mental de la vinculación de objetos es mucho más simple que el creado a través de 'nuevo'.
LocalPCGuy
Bueno, mi modelo mental de newusos "enlace de objeto", por lo que no hay mucha diferencia. De hecho, solo hay dos cosas: .constructorse llama .init, y esa función no tiene una .prototypepropiedad que apunte hacia su objeto prototipo. El resto es solo sintaxis, y prefiero new Xsobre Object.create(x).init().
Bergi
0

No soy un experto en Java Script, pero aquí hay un ejemplo simple para entender la diferencia entre "Object.create" y "new".

Paso 1: crea la función padre con algunas propiedades y acciones.

function Person() {

this.name = 'venkat';

this.address = 'dallas';

this.mobile='xxxxxxxxxx'

}

Person.prototype.func1 = function () {

    return this.name + this.address;
}

Paso 2: cree una función secundaria (PersonSalary) que se extienda por encima de la función Persona usando la nueva palabra clave.

function PersonSalary() {
    Person.call(this);
}
PersonSalary.prototype = new Person();

PersonSalary();

paso 3: crea la segunda función secundaria (PersonLeaves) que se extiende por encima de la función Person usando la palabra clave Object.create ..

function PersonLeaves() {
 Person.call(this);
}
PersonLeaves.prototype = Object.create(Person.prototype);


PersonLeaves();

// Ahora verifique ambos prototipos de funciones secundarias.

PersonSalary.prototype
PersonLeaves.prototype

ambas funciones secundarias se vincularán al prototipo Persona (función principal) y pueden acceder a sus métodos, pero si crea una función secundaria usando new, devolverá un objeto completamente nuevo con todas las propiedades principales que no necesitamos y también cuando cree cualquier objeto o función usando "Nuevo" que se ejecuta la función padre que no queremos ser.

Aquí están los platos para llevar

si solo desea delegar a algunos métodos en la función principal y no desea que se cree un nuevo objeto, usar Object.create es la mejor manera.

Venkat
fuente