javascript - type - Usando “Object.create” en lugar de “nuevo”




type object javascript (9)

Resumen:

  • Object.create() es una función de Javascript que toma 2 argumentos y devuelve un nuevo objeto.
  • El primer argumento es un objeto que será el prototipo del objeto recién creado.
  • El segundo argumento es un objeto que será las propiedades del objeto recién creado.

Ejemplo:

const proto = {
  talk : () => console.log('hi')
}

const props = {
  age: {
    writable: true,
    configurable: true,
    value: 26
  }
}


let Person = Object.create(proto, props)

console.log(Person.age);
Person.talk();

Aplicaciones prácticas:

  1. La principal ventaja de crear un objeto de esta manera es que el prototipo se puede definir explícitamente . Al usar un objeto literal, o la new palabra clave, no tiene control sobre esto (sin embargo, puede sobrescribirlos, por supuesto).
  2. Si queremos tener un prototipo La new palabra clave invoca una función constructora. Con Object.create() no hay necesidad de invocar o incluso declarar una función constructora .
  3. Básicamente, puede ser una herramienta útil cuando desea crear objetos de una manera muy dinámica. Podemos hacer una función de fábrica de objetos que crea objetos con diferentes prototipos dependiendo de los argumentos recibidos.

Javascript 1.9.3 / ECMAScript 5 presenta Object.create , que Douglas Crockford, entre otros, ha advocating durante mucho tiempo. ¿Cómo reemplazo el código new a continuación con Object.create ?

var UserA = function(nameParam) {
    this.id = MY_GLOBAL.nextId();
    this.name = nameParam;
}
UserA.prototype.sayHello = function() {
    console.log('Hello '+ this.name);
}
var bob = new UserA('bob');
bob.sayHello();

(Supongamos que MY_GLOBAL.nextId existe).

Lo mejor que se me ocurre es:

var userB = {
    init: function(nameParam) {
        this.id = MY_GLOBAL.nextId();
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};
var bob = Object.create(userB);
bob.init('Bob');
bob.sayHello();

No parece haber ninguna ventaja, así que creo que no lo estoy consiguiendo. Probablemente estoy siendo demasiado neoclásico. ¿Cómo debo usar Object.create para crear el usuario 'bob'?


A veces no puede crear un objeto con NEW pero aún puede invocar el método CREATE.

Por ejemplo: si desea definir un elemento personalizado, debe derivar de HTMLElement.

proto = new HTMLElement  //fail :(
proto = Object.create( HTMLElement.prototype )  //OK :)
document.registerElement( "custom-element", { prototype: proto } )

Creo que el punto principal en cuestión es entender la diferencia entre los enfoques new y los de Object.create . De acuerdo con esta respuesta y este video, la new palabra clave hace lo siguiente:

  1. Crea nuevo objeto.

  2. Enlaza el nuevo objeto a la función constructora ( prototype ).

  3. Hace que this variable apunte al nuevo objeto.

  4. Ejecuta la función de constructor usando el nuevo objeto y la ejecución implícita return this ;

  5. Asigna el nombre de la función del constructor al nuevo constructor propiedades del objeto.

Object.create realiza solo los pasos 1st y 2nd !

En el ejemplo de código proporcionado en cuestión, no es gran cosa, pero en el siguiente ejemplo es:

var onlineUsers = [];
function SiteMember(name) {
    this.name = name;
    onlineUsers.push(name);
}
SiteMember.prototype.getName = function() {
    return this.name;
}
function Guest(name) {
    SiteMember.call(this, name);
}
Guest.prototype = new SiteMember();

var g = new Guest('James');
console.log(onlineUsers);

Como resultado de efectos secundarios será:

[ undefined, 'James' ]

debido a Guest.prototype = new SiteMember();
Pero no necesitamos ejecutar el método del constructor principal, solo necesitamos que el método getName esté disponible en Guest. Por lo tanto tenemos que usar Object.create .
Si reemplaza Guest.prototype = new SiteMember();
to Guest.prototype = Object.create(SiteMember.prototype); resultado sea:

[ 'James' ]

La ventaja es que Object.create suele ser más lento que new en la mayoría de los navegadores

En este ejemplo de jsperf , en un Chromium, el new navegador es 30 veces más rápido que Object.create(obj) aunque ambos son bastante rápidos. Todo esto es bastante extraño porque lo nuevo hace más cosas (como invocar a un constructor) donde Object.create debería estar creando un nuevo Objeto con el objeto pasado como un prototipo (enlace secreto en Crockford-habla)

Tal vez los navegadores no hayan alcanzado el Object.create hacer que Object.create más eficiente (quizás lo están basando en lo new bajo las coberturas ... incluso en el código nativo)


Otro uso posible de Object.create es clonar objetos inmutables de una manera económica y efectiva .

var anObj = {
    a: "test",
    b: "jest"
};

var bObj = Object.create(anObj);

bObj.b = "gone"; // replace an existing (by masking prototype)
bObj.c = "brand"; // add a new to demonstrate it is actually a new obj

// now bObj is {a: test, b: gone, c: brand}

Notas : El fragmento de código anterior crea un clon de un objeto de origen (es decir, no es una referencia, como en cObj = aObj). Se beneficia sobre el método de propiedades de copia (ver 1 ), ya que no copia las propiedades de los miembros objeto. Más bien, crea otro objeto de destino con su conjunto de prototipos en el objeto de origen. Además, cuando las propiedades se modifican en el objeto dest, se crean "sobre la marcha", enmascarando las propiedades del prototipo (src). Esto constituye una forma rápida y efectiva de clonar objetos inmutables.

La advertencia aquí es que esto se aplica a los objetos de origen que no deben modificarse después de la creación (inmutable). Si el objeto de origen se modifica después de la creación, también se modificarán todas las propiedades desenmascaradas del clon.

Toque aquí ( http://jsfiddle.net/y5b5q/1/ ) (necesita Object.create un navegador capaz).


Podrías hacer que el método init devuelva this , y luego encadenar las llamadas, así:

var userB = {
    init: function(nameParam) {
        this.id = MY_GLOBAL.nextId();
        this.name = nameParam;
        return this;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};

var bob = Object.create(userB).init('Bob');

Realmente no hay ninguna ventaja en el uso de Object.create(...) sobre un new object .

Quienes abogan por este método generalmente declaran ventajas bastante ambiguas: "scalability" o " más natural para JavaScript ", etc.

Sin embargo, todavía tengo que ver un ejemplo concreto que muestra que Object.create tiene alguna ventaja sobre el uso de new . Por el contrario hay problemas conocidos con él. Sam Elsamman describe lo que sucede cuando hay objetos anidados y se Object.create(...) :

var Animal = {
    traits: {},
}
var lion = Object.create(Animal);
lion.traits.legs = 4;
var bird = Object.create(Animal);
bird.traits.legs = 2;
alert(lion.traits.legs) // shows 2!!!

Esto ocurre porque Object.create(...) aboga por una práctica en la que los datos se utilizan para crear nuevos objetos; Aquí el dato Animal convierte en parte del prototipo de lion y bird , y causa problemas cuando se comparte. Cuando se usa nuevo, la herencia prototípica es explícita:

function Animal() {
    this.traits = {};
}

function Lion() { }
Lion.prototype = new Animal();
function Bird() { }
Bird.prototype = new Animal();

var lion = new Lion();
lion.traits.legs = 4;
var bird = new Bird();
bird.traits.legs = 2;
alert(lion.traits.legs) // now shows 4

Con respecto a los atributos de propiedad opcionales que se pasan a Object.create(...) , estos pueden agregarse usando Object.defineProperties(...) .


Si bien Douglas Crockford solía ser un entusiasta defensor de Object.create () y básicamente es la razón por la que este constructo está en JavaScript, ya no tiene esta opinión.

Dejó de usar Object.create, porque dejó de usar esta palabra clave por completo, ya que causa demasiados problemas. Por ejemplo, si no tiene cuidado, puede apuntar fácilmente al objeto global, lo que puede tener muy malas consecuencias. Y afirma que sin usar este Object.create ya no tiene sentido.

Puedes ver este video de 2014 donde habla en Nordic.js:

https://www.youtube.com/watch?v=PSGEjv3Tqo0


Tienes que hacer una función personalizada Object.create() . Una que aborde las preocupaciones de Crockford y también llame a su función de inicio.

Esto funcionará:

var userBPrototype = {
    init: function(nameParam) {
        this.name = nameParam;
    },
    sayHello: function() {
        console.log('Hello '+ this.name);
    }
};


function UserB(name) {
    function F() {};
    F.prototype = userBPrototype;
    var f = new F;
    f.init(name);
    return f;
}

var bob = UserB('bob');
bob.sayHello();

Aquí UserB es como Object.create, pero ajustado a nuestras necesidades.

Si quieres, también puedes llamar al:

var bob = new UserB('bob');




object-create