Usando " Objeto.crear" en lugar de "nuevo"


Javascript 1.9.3 / ECMAScript 5 introduce Object.create, que Douglas Crockford, entre otros, ha estado defendiendo durante mucho tiempo. ¿Cómo sustituyo new en el siguiente código por 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();

(Asume 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'?

Author: hippietrail, 2010-04-25

13 answers

Con solo un nivel de herencia, su ejemplo puede no permitirle ver los beneficios reales de Object.create.

Este método le permite implementar fácilmente herencia diferencial, donde los objetos pueden heredar directamente de otros objetos.

En su ejemplo userB, no creo que su método init deba ser público o incluso existir, si vuelve a llamar a este método en una instancia de objeto existente, las propiedades id y name cambiarán.

Object.create le permite inicializar las propiedades del objeto usando su segundo argumento, por ejemplo:

var userB = {
  sayHello: function() {
    console.log('Hello '+ this.name);
  }
};

var bob = Object.create(userB, {
  'id' : {
    value: MY_GLOBAL.nextId(),
    enumerable:true // writable:false, configurable(deletable):false by default
  },
  'name': {
    value: 'Bob',
    enumerable: true
  }
});

Como puede ver, las propiedades se pueden inicializar en el segundo argumento de Object.create, con un objeto literal usando una sintaxis similar a la utilizada por los métodos Object.defineProperties y Object.defineProperty.

Le permite establecer los atributos de propiedad(enumerable, writable, o configurable), que puede ser realmente útil.

 236
Author: CMS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-25 20:18:42

No hay realmente ninguna ventaja en usar Object.create(...) sobre new object.

Los que defienden este método generalmente declaran ventajas bastante ambiguas: "escalabilidad", o " más natural para JavaScript", etc.

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

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 donde los datos se utilizan para crear nuevos objetos; aquí el datum Animal se convierte en parte del prototipo de lion y bird, y causa problemas a medida que 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

En cuanto a los atributos de propiedad opcionales que se pasan a Object.create(...), estos se pueden agregar usando Object.defineProperties(...).

 45
Author: Noel Abrahams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-12 12:27:21

Objeto.crear aún no es estándar en varios navegadores, por ejemplo IE8, Opera v11.5, Konq 4.3 no lo tienen. Puedes usar la versión de Douglas Crockford de Object.crear para esos navegadores, pero esto no incluye el segundo parámetro 'objeto de inicialización' utilizado en la respuesta de CMS.

Para el código de navegador cruzado una forma de obtener la inicialización de objetos mientras tanto es personalizar el Objeto de Crockford.crear. Aquí hay un método: -

Object.build = function(o) {
   var initArgs = Array.prototype.slice.call(arguments,1)
   function F() {
      if((typeof o.init === 'function') && initArgs.length) {
         o.init.apply(this,initArgs)
      }
   }
   F.prototype = o
   return new F()
}

Esto mantiene el prototipo de Crockford herencia, y también comprueba cualquier método init en el objeto, luego lo ejecuta con sus parámetros, como por ejemplo new man('John','Smith'). Su código se convierte entonces en: -

MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}  // For example

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

Así que bob hereda el método sayHello y ahora tiene sus propias propiedades id=1 y name='Bob'. Estas propiedades son tanto escribibles como enumerables, por supuesto. Esta es también una forma mucho más sencilla de inicializar que para el objeto ECMA.crear especialmente si usted no está preocupado acerca de la escritura, enumerable y configurable atributo.

Para la inicialización sin un método init se podría usar el siguiente mod de Crockford: -

Object.gen = function(o) {
   var makeArgs = arguments 
   function F() {
      var prop, i=1, arg, val
      for(prop in o) {
         if(!o.hasOwnProperty(prop)) continue
         val = o[prop]
         arg = makeArgs[i++]
         if(typeof arg === 'undefined') break
         this[prop] = arg
      }
   }
   F.prototype = o
   return new F()
}

Esto llena las propiedades propias de UserB, en el orden en que se definen, utilizando el Objeto.parámetros gen de izquierda a derecha después del parámetro UserB. Utiliza el bucle for(prop in o) por lo que, según los estándares ECMA, no se puede garantizar el mismo orden de enumeración de propiedades que el orden de definición de propiedades. Sin embargo, varios ejemplos de código probados en (4) los principales navegadores muestran que son los mismos, siempre que se utilice el filtro hasOwnProperty, y a veces incluso si no.

MY_GLOBAL = {i: 1, nextId: function(){return this.i++}};  // For example

var userB = {
   name: null,
   id: null,
   sayHello: function() {
      console.log('Hello '+ this.name);
   }
}

var bob = Object.gen(userB, 'Bob', MY_GLOBAL.nextId());

Diría que algo más simple que Object.build since UserB no necesita un método init. También UserB no es específicamente un constructor, pero se parece a un objeto singleton normal. Así que con este método puede construir e inicializar desde objetos normales.

 41
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-21 01:30:23

TL; DR:

new Computer() invocará la función constructor Computer(){} por una vez, mientras que Object.create(Computer.prototype) no lo hará.

Todas las ventajas se basan en este punto.

Aunque por algunas razones de optimización del motor interno, Object.create puede ser más lento, lo cual no es intuitivo.

 18
Author: Nami WANG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-06 08:19:28

Puedes 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');
 13
Author: samfrances,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-03 16:37:59

Otro posible uso de Object.crear es clonar objetos inmutables de una manera barata 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 fuente (también conocido como no una referencia, como en cObj = aObj). Se beneficia del método copy-properties (ver 1), en el sentido de que no copia las propiedades de los miembros de objetos. Más bien crea otro objeto-destino con su prototipo establecido 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 eficaz de clonar objetos inmutables.

La advertencia aquí es que esto se aplica a los objetos fuente 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.

Toca aquí ( http://jsfiddle.net/y5b5q/1 / ) (necesita Objeto.crear capaz navegador).

 7
Author: basos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-24 12:56:43

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

  1. Crea un nuevo objeto.

  2. Vincula el nuevo objeto a la función constructor (prototype).

  3. Hace que la variable this apunte al nuevo objeto.

  4. Ejecuta la función constructor usando el nuevo objeto y el perform implícito return this;

  5. Asigna el nombre de la función del constructor a la propiedad del nuevo objeto constructor.

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

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 del efecto secundario será:

[ undefined, 'James' ]

Debido a Guest.prototype = new SiteMember();
Pero no necesitamos ejecutar el método constructor padre, solo necesitamos hacer que el método getName esté disponible en Guest. Por lo tanto tenemos que utilizar Object.create.
Si se sustituye Guest.prototype = new SiteMember();
a Guest.prototype = Object.create(SiteMember.prototype); el resultado es:

[ 'James' ]
 6
Author: Vladimir Kovpak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-27 19:39:38

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 } )
 5
Author: Supersharp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-04 09:34:42

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

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

Tal vez los navegadores no han alcanzado en hacer Object.create más eficiente (tal vez lo están basando en new debajo de las cubiertas ... incluso en código nativo)

 3
Author: frooble,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-13 12:41:21

Tienes que hacer una función personalizada Object.create(). Uno que se ocupa Crockfords preocupaciones y también llama a su función init.

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.crear, pero ajustado a nuestras necesidades.

Si lo desea, también puede llamar a:

var bob = new UserB('bob');
 2
Author: edwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-25 22:37:26

Resumen:

  • Object.create() es una función 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án 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 el prototipo se puede definir explícitamente . Al usar un literal de objeto, o la palabra clave new, no tienes control sobre esto (sin embargo, puedes sobrescribirlos, por supuesto).
  2. Si queremos tener un prototipo la palabra clave new 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.
 1
Author: Willem van der Veen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-19 08:41:20

Mientras que Douglas Crockford solía ser un celoso defensor del Objeto.create () y él es básicamente la razón por la que esta construcción en realidad está en javascript, ya no tiene esta opinión.

Dejó de usar Object.crear, 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 consecuencias realmente malas. Y afirma que sin usar este Objeto.crear ya no tiene sentido.

Podéis ver este vídeo del 2014 donde habla en Nordic.js:

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

introduzca la descripción de la imagen aquí

 0
Author: Vojtech Ruzicka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-01 19:30:35

Prefiero un enfoque de cierre.

Todavía uso new. No uso Object.create. No uso this.

Todavía uso new como me gusta la naturaleza declarativa de la misma.

Considere esto para la herencia simple.

window.Quad = (function() {

    function Quad() {

        const wheels = 4;
        const drivingWheels = 2;

        function getWheelCount() {
            return wheels;
        }

        function getDrivingWheelCount() {
            return drivingWheels;
        }
        return Object.freeze({
            getWheelCount,
            getDrivingWheelCount
        });
    }

    return Object.freeze(Quad);
})();

window.Car4wd = (function() {

    function Car4wd() {
        const quad = new Quad();

        const spareWheels = 1;
        const extraDrivingWheels = 2;

        function getSpareWheelCount() {
            return spareWheels;
        }

        function getDrivingWheelCount() {
            return quad.getDrivingWheelCount() + extraDrivingWheels;
        }

        return Object.freeze(Object.assign({}, quad, {
            getSpareWheelCount,
            getDrivingWheelCount
        }));
    }

    return Object.freeze(Car4wd);
})();

let myQuad = new Quad();
let myCar = new Car4wd();
console.log(myQuad.getWheelCount()); // 4
console.log(myQuad.getDrivingWheelCount()); // 2
console.log(myCar.getWheelCount()); // 4
console.log(myCar.getDrivingWheelCount()); // 4 - The overridden method is called
console.log(myCar.getSpareWheelCount()); // 1

Se alienta la retroalimentación.

 0
Author: p0wdr.com,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-08 19:43:47