2012-04-28 16 views
6

Creo que la diferencia ha hecho clic en mi cabeza, pero me gustaría estar seguro.En Javascript, la diferencia entre 'Object.create' y 'new'

En la página de Douglas Crockford Prototypal Inheritance in JavaScript, dice

En un sistema de prototipos, objetos heredan de objetos. JavaScript, sin embargo, carece de un operador que realice esa operación. En su lugar, tiene un nuevo operador, de modo que new f() produce un nuevo objeto que hereda de f.prototype.

No entendí realmente lo que estaba tratando de decir en esa oración, así que realicé algunas pruebas. Me parece que la diferencia clave es que si creo un objeto basado en otro objeto en un sistema prototípico puro, entonces todos los padres miembros deben estar en el prototipo del nuevo objeto, no en el nuevo objeto en sí.

Aquí está la prueba:

var Person = function(name, age) { 
     this.name = name; 
     this.age = age; 
} 
Person.prototype.toString = function(){return this.name + ', ' + this.age}; 

// The old way... 
var jim = new Person("Jim",13); 
for (n in jim) { 
    if (jim.hasOwnProperty(n)) { 
     console.log(n); 
    } 
} 
// This will output 'name' and 'age'. 

// The pure way... 
var tim = Object.create(new Person("Tim",14)); 
for (n in tim) { 
    if (tim.hasOwnProperty(n)) { 
     console.log(n); 
    } 
} 
// This will output nothing because all the members belong to the prototype. 
// If I remove the hasOwnProperty check then 'name' and 'age' will be output. 

es mi entendimiento correcto que la diferencia sólo se hace evidente cuando las pruebas de los miembros en el objeto en sí mismo?

+3

ver http://stackoverflow.com/questions/4166616/understanding-the-difference-between-object-create-and-new-somefunction-in-j –

+0

Ayer había visto esa pregunta, creo, pero el la respuesta no estaba clara para mí. Ahora que realicé mis exámenes y escribí mi pregunta, está claro. – Jules

Respuesta

3

Sus suposiciones son correctas, pero hay otro patrón del que Douglas no habla mucho: el prototipo también se puede usar para las propiedades. La clase de persona que podría haber sido escrito como:

var Person = function(name, age) { 
    this.name = name; 
    this.age = age; 
} 
Person.prototype.name = null; //default value if you don't init in ctor 
Person.prototype.age = null; 
Person.prototype.gender = "male"; 
Person.prototype.toString = function(){return this.name + ', ' + this.age;}; 

En este caso, interactuando sobre propiedades de una instancia de esta clase, como lo hace en su ejemplo, generaría ninguna salida para la propiedad 'de género'.

EDIT 1: La asignación del nombre y la edad en el constructor hacen que las propiedades visibles por hasOwnProperty (@ Matt, gracias por recordarme de esto). La propiedad de género no asignada no sería visible hasta que alguien lo establezca en la instancia.

EDIT 2: Para añadir aún más a este, presento un patrón de herencia alternativa - que yo he utilizado para proyectos de gran envergadura:

var inherits = function(childCtor, parentCtor) { 
    function tempCtor() {}; 
    tempCtor.prototype = parentCtor.prototype; 
    childCtor.superclass = parentCtor.prototype; 
    childCtor.prototype = new tempCtor(); 
    childCtor.prototype.constructor = childCtor; 
}; 

var Person = function(name){ 
    this.name = name; 
} 
Person.prototype.name = ""; 
Person.prototype.toString = function(){ 
    return "My name is " + this.name; 
} 

var OldPerson = function(name, age){ 
    OldPerson.superclass.constructor.call(this); 
    this.age = age 
}; 
inherits(OldPerson, Person); 
OldPerson.prototype.age = 0; 
OldPerson.prototype.toString = function(){ 
    var oldString = OldPerson.superclass.toString.call(this); 
    return oldString + " and my age is " + this.age; 
} 

Este es un patrón bastante común con un pequeño giro - la clase principal se adjunta al niño a través de la propiedad "superclase" que le permite acceder a los métodos/propiedades anulados por el niño. Técnicamente, podría reemplazar OldPerson.superclass con Person, sin embargo, eso no es lo ideal. Si alguna vez cambiaste OldPerson para heredar de una clase que no sea Person, tendrías que actualizar todas las referencias a Person también.

Datos 3: sólo para traer este punto de partida, que aquí hay una versión de la función "hereda" que se aprovecha de Object.create y funciona exactamente igual que anteriormente he descrito:

var inherits = function(childCtor, parentCtor) { 
    childCtor.prototype = Object.create(parentCtor.prototype); 
    childCtor.superclass = parentCtor.prototype; 
}; 
3

EDITAR : Esta respuesta fue originalmente una respuesta a la respuesta de @ jordancpaul, la cual ha corregido desde entonces.Voy a dejar la parte de mi respuesta que ayuda a explicar la diferencia importante entre las propiedades de prototipos y propiedades de la instancia:

En algunos casos, las propiedades son compartida entre todas las instancias y tienes que ser muy cuidadoso cuando usted está declarando propiedades en el prototipo Considere este ejemplo:

Person.prototype.favoriteColors = []; //Do not do this!

Ahora, si se crea una nueva instancia persona utilizando Object.create o new, no funciona como se podría esperar ...

var jim = new Person("Jim",13); 
jim.favoriteColors.push('red'); 
var tim = new Person("Tim",14); 
tim.favoriteColors.push('blue'); 

console.log(tim.favoriteColors); //outputs an array containing red AND blue! 

Esto no tiene' Esto significa que no puede declarar propiedades en el prototipo, pero si lo hace, usted y todos los desarrolladores que trabajan en su código deben ser conscientes de este peligro. En un caso como éste, si lo prefiere declarar propiedades en el prototipo por cualquier razón, se podría hacer:

Person.prototype.favoriteColors = null

Y inicializarlo a una matriz vacía en el constructor:

var Person = function(name, age) { 
    ... 
    this.favoriteColors = []; 
} 

El La regla general al usar este método es que los valores predeterminados para propiedades literales simples (cadenas, números, booleanos) se pueden establecer en el prototipo directamente, pero cualquier propiedad que herede de Object (incluyendo matrices y fechas) debe establecerse en nulo y luego inicializarse en el constructor

La manera más segura es declarar solo los métodos en el prototipo y declarar siempre las propiedades en el constructor.

De todos modos, la pregunta fue sobre Object.create ...

El primer argumento pasado a Object.create se establece como el prototipo de la nueva instancia. Un mejor uso sería:

var person = { 
    initialize: function(name, age) { 
     this.name = name; 
     this.age = age; 
     return this; 
    }, 

    toString: function() { 
     return this.name + ', ' + this.age; 
    } 
}; 

var tim = Object.create(person).initialize("Tim",14); 

Ahora la salida será la misma que en su primer ejemplo.

Como puede ver, es un enfoque filosófico diferente del estilo más clásico de OOP en Javascript. Con Object.create, el énfasis está en crear nuevos objetos a partir de objetos existentes, en lugar de en el constructor. La inicialización se convierte en un paso separado.

Personalmente, tengo sentimientos encontrados sobre el enfoque Object.create; es muy bueno para la herencia debido al segundo parámetro que puede usar para agregar propiedades adicionales a un prototipo existente, pero también es más detallado y lo hace para que las verificaciones ya no funcionen (la alternativa en este ejemplo sería marcar person.isPrototypeOf(tim)) .

La razón principal por la que digo Object.create es prolija es debido a que el segundo parámetro, pero hay algunas librerías útiles por ahí que la dirección que:

https://github.com/Gozala/selfish

https://github.com/Raynos/pd

(y otros)

¡Espero que haya sido más instructivo que confuso!

+1

Acabo de escribir una biblioteca * muy * simple para ayudar con la herencia que sigue más el método de herencia prototípico tradicional, pero utiliza Object.create al extender "clases": https://github.com/mbrowne/simpleoo. js –

+0

Sí, víctima del 'Class.prototype.property = []' trampa. Actualicé mi publicación original con un poco más de información, tal vez hayas visto este patrón antes. – jordancpaul

Cuestiones relacionadas