Continúo profundizando en toda la información que he ido recopilando acerca de la creación de objetos en Javascript:
- En la primera entrada de esta serie, me concentré en explicar los mecanismos que usaba el operador
new
y, sobre todo, en comprender la cadena de prototipado. - En la segunda entrada, estuvimos repasando toda la casuística que se me ha ocurrido para manipular la cadena de prototipado de los objetos, para así asentar nuestros conocimientos teóricos (y también que nos sirvan de inspiración para diseñar nuestros propios objetos Javascript).
Tenía pensado cerrar esta serie con esta tercera entrada dedicada a las funciones Javascript para interaccionar con objetos: Object.create
, instanceof
, isPrototypeOf
, … Pero cuando me he puesto a explicar la función Object.create
, ha necesitado una entrada completa para ella sola. Espero que os resulte de interés.
Repaso de los métodos para crear objetos en Javascript
Ya hemos estudiado dos métodos para crear objetos:
- El método «literal»:
- El operador
new
:
1 2 3 4 5 |
var calvin = { name : 'Calvin', sayHello : function() { console.debug('Hello, I am ' + this.name); } }; calvin.sayHello(); // Hello, I am Calvin |
Es el método más directo, permite crear objetos individuales «sobre la marcha».
1 2 3 4 5 6 7 |
var comicCharacter = function(name) { this.name = name; } comicCharacter.prototype.sayHello = function() { console.debug('Hello, I am ' + this.name); }; var calvin = new comicCharacter('Calvin'); calvin.sayHello(); // Hello, I am Calvin |
Permite usar funciones (me gusta llamarlas «constructoras» pero si prefieres llamarlas «clases», lo dejo a tu criterio) que nos sirven parar crear objetos que todos cumplen el mismo patrón.
Recordemos también el diagrama que detalla la cadena de prototipado de nuestro objeto calvin
recién creado:

(La presencia en este diagrama de la función constructora comicCharacter
es meramente informativa. Una vez creado el objeto calvin
, su verdadera cadena de prototipado está formada por el objeto calvin
y el objeto comicCharacter.prototype
)
El operador Object.create
Existe un tercer método para crear objetos: la función Object.create
. Esta función, es muy parecida al operador new
… pero con sus peculiaridades.
Veamos el ejemplo anterior, usando Object.create
:
1 2 3 4 5 6 7 8 9 |
var ComicCharacter = { name : '', init : function(name) { this.name = name; }, sayHello : function() { console.debug('Hello, I am ' + this.name); } }; var calvin = Object.create(ComicCharacter); calvin.init('Calvin'); calvin.sayHello(); // Hello, I am Calvin |
Vamos a repasar qué es lo que hemos hecho:
- Hemos definido un objeto
ComicCharacter
. Este objeto (INSISTO: es un objeto, no es una función) lo usaré como patrón para crear todos mis objetos (si quieres, llámalo «clase»). Verás que he escrito su nombre con la primera letra en mayúsculas (es la notación más recomendada, para distinguirlo como objeto-patrón). Este objeto, tiene todas las propiedades y métodos que deben tener los objetos que voy a crear a partir de él. - Para crear mi nuevo objeto
calvin
a partir del objeto patrónComicCharacter
, lo paso éste como argumento aObject.Create
. - Por fin, puedo operar con el nuevo objeto
calvin
recién creado.
Arquitectura de los objetos creados con Object.create
«Pero, Jose ¿qué ha pasado con la cadena de prototipado? ¿ya no existe? ¿es que ya no hace falta?»
Por supuesto que el objeto calvin
que hemos creado tiene su cadena de prototipado. Y por supuesto que seguimos usándola. Lo que ocurre es que los mecanismos internos que usa Object.create
son distintos de los que hemos estudiado que usa new
:
- Primero, crea un objeto absolutamente limpio.
- A este nuevo objeto, le asigna su propiedad
__proto__
apuntando al objeto que se le pasa como parámetro.
El diagrama de este nuevo objeto calvin
recién creado sería:

Si comparamos este nuevo diagrama con el diagrama previo (cuando creamos el objeto con el operador new
) vemos que no tiene nada que ver. Pero, eso sí: lo importante es que la cadena de prototipado sigue cumpliendo su cometido para dotar a nuestros objetos de un conjunto de propiedades y funciones.
A diferencia de cuando creamos objetos usando new
, no tenemos el equivalente a la inicialización del objeto que realizaba la función constructora. Por eso, he añadido a mi nuevo objeto-patrón ComicCharacter
un método init
. Una vez que invoquemos a este método (calvin.init('Calvin')
) el diagrama será:

Como podemos ver, a partir de ahora el objeto calvin
tiene su propia propiedad name
de modo que si consultamos calvin.name
el motor Javascript responde directamente «Calvin» (y ya no recorre la cadena de prototipado).
Examinando la nueva cadena de prototipado
Lo mismo que «jugamos» con la cadena de prototipado de los objetos creados con new
, ahora vamos a «jugar» con la cadena de prototipado de nuestros nuevos objetos creados con Object.create
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var ComicCharacter = { name : '', init : function(name) { this.name = name; }, sayHello : function() { console.debug('Hello, I am ' + this.name); } }; var calvin = Object.create(ComicCharacter); calvin.init('Calvin'); calvin.sayHello(); // Hello, I am Calvin var hobbes = Object.create(ComicCharacter); hobbes.init('Hobbes'); hobbes.sayHello(); // hello, I am Hobbes console.debug(calvin.__proto__ == ComicCharacter); // true ComicCharacter.shout = function() { console.debug('Grrr I am ' + this.name); } calvin.shout(); // Grrr I am Calvin hobbes.shout(); // Grrr I am Hobbes |
Vamos a repasar lo que hemos hecho:
- Hemos definido el objeto
ComicCharacter
que nos va a servir como modelo. - Hemos creado dos objetos
calvin
yhobbes
usandoObject.create
. - Hemos comprobado que, tal como he explicado, la propiedad
calvin.__proto__
apunta aComicCharacter
. - Para comprobar (más aún) que dominamos esta cadena de prototipado, le hemos añadido un nuevo método
shout
al objetocomicCharacter
e inmediatamente después hemos comprbado que este nuevo método está accesible (a través de sus cadenas de prototipado) desde nuestros dos objetoscalvin
yhobbes
.
Pero ¿quién es el constructor?
En estas reflexiones sobre Object.create
he omitido intencionadamente cualquier referencia sobre la propiedad constructor
de los nuevos objetos… hasta ahora. Vamos a verlo:
Cuando estudiamos cómo new
creaba nuevos objetos a partir de una función constructor, ya vimos que la propiedad constructor
formaba parte del objeto prototype
de la función constructora. Y, por lo tanto, era accesible desde el objeto creado a través de su cadena de prototipado. Es más fácil verlo que explicarlo:

1 2 3 4 5 6 7 8 9 10 |
var comicCharacter = function(name) { this.name = name; } comicCharacter.prototype.sayHello = function() { console.debug('Hello, I am ' + this.name); }; var calvin = new comicCharacter('Calvin'); console.debug(comicCharacter.prototype.constructor == comicCharacter); // true console.debug(calvin.constructor == comicCharacter); // true console.debug(calvin.constructor); // function(name) |
Pero, cuando usamos Object.construct
, no hay función constructora. Y la cadena de prototipado del nuevo objeto apunta al objeto que hemos usado como patrón. Entonces ¿qué devuelve nuestro nuevo objeto si intentamos consultar su propiedad constructor
?:
1 2 3 4 5 6 7 8 9 10 11 12 |
var ComicCharacter = { name : '', init : function(name) { this.name = name; }, sayHello : function() { console.debug('Hello, I am ' + this.name); } }; var calvin = Object.create(ComicCharacter); console.debug(calvin.constructor == ComicCharacter); // false console.debug(calvin.constructor == ComicCharacter.constructor); // true console.debug(ComicCharacter.__proto__ == Object.prototype); // true console.debug(ComicCharacter.constructor == Object); true console.debug(calvin.constructor == Object); true |
A ver qué hemos hecho:
- Hemos comprobado si
calvin.constructor
valeComicCharacter
… pero no es así. Intuitivamente, podríamos imaginar que sí (ComicCharacter
es el constructor ¿no?). Pero ahora que conocemos la cadena de prototipado, sabemos que la relación entrecalvin
yComicCharacter
es a través de la propiedadcalvin.__proto__
. - Lo que sí que son iguales son
calvin.constructor
yComicCharacter.constructor
. Ya comprendemos por qué: porquecalvin.constructor
busca su valor en su cadena de prototipado… y encuentra el valor deComicCharacter.constructor
. - ¿Y cuál es el valor de
ComicCharacter.constructor
? Pues como es un objeto creado literalmente, su constructor es la función constructoraObject()
.
El diagrama que incluye al constructor sería el siguiente:

Moraleja: si tienes que analizar el origen de un objeto Javascript, puedes fiarte de su cadena de prototipado pero yo no me fiaría de su propiedad constructor
.
Creando tu propio Object.create
La función Object.create
es un estándar ECMAScript 5.1. Como tal, se puede usar en todos los navegadores modernos… excepto en IE8 y anteriores (podéis consultar esta tabla de compatibilidad).
Lo que sí podemos, es construir nuestra propia versión para usarla en IE8. Vamos a estudiar la versión que han desarrollado en es5-shim:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
Object.create = function create(prototype, properties) { var object; var Type = function Type() {}; // An empty constructor. if (prototype === null) { object = createEmpty(); } else { if (typeof prototype !== 'object' && typeof prototype !== 'function') { // In the native implementation `parent` can be `null` // OR *any* `instanceof Object` (Object|Function|Array|RegExp|etc) // Use `typeof` tho, b/c in old IE, DOM elements are not `instanceof Object` // like they are in modern browsers. Using `Object.create` on DOM elements // is...err...probably inappropriate, but the native version allows for it. throw new TypeError('Object prototype may only be an Object or null'); // same msg as Chrome } Type.prototype = prototype; object = new Type(); // IE has no built-in implementation of `Object.getPrototypeOf` // neither `__proto__`, but this manually setting `__proto__` will // guarantee that `Object.getPrototypeOf` will work as expected with // objects created using `Object.create` /*eslint-disable no-proto */ object.__proto__ = prototype; /*eslint-enable no-proto */ } if (properties !== void 0) { Object.defineProperties(object, properties); } return object; }; |
Vamos a estudiar qué es lo que hace (me voy a concentrar en lo esencial):
- Define la propiedad
Object.create
como un objeto función que espera dos parámetros,protype
yproperties
. Ahora, nos vamos a concentrar en el primer parámetroprototype
y dejamos el segundo parámetroproperties
para explicarlo más adelante. - En la línea 3 crea una función vacía
Type
. - Después de algunas comprobaciones, en la línea 16 asigna manualmente la propiedad
Type.prototype
al objetoprototype
que se le pasó como parámetro. - En la línea 17 crea un objeto
object
a partir de la función constructoraType
. - En la línea 31, devuelve este objeto
object
como resultado.
Veamos el diagrama del nuevo objeto object
:

Vemos que, efectivamente, nuestro nuevo objeto object
tiene en su cadena de prototipado al objeto prototype
que pasamos como patrón (el resultado es exactamente lo mismo que si hubiéramos usado la función Object.create
original).
Veréis que también he dibujado el objeto Type.prototype
. Este objeto ha quedado «huérfano» y sin utilidad desde el momento que asignamos manualmente la propiedad Type.prototype
para que apuntara a nuestro objeto prototype
.
No olvidemos que la función type
y el objeto type.prototype
son locales a la función y por lo tanto «morirán» cuando la función devuelva el objeto object
.
El segundo parámetro de Object.construct
Tal como hemos visto, la función Object.create
tiene un segundo parámetro properties
que, hasta ahora, no había mencionado.
Este segundo parámetro permite añadir al objeto que se cree nuevas propiedades adicionales a las que aporta el objeto patrón que se pasa como primer parámetro. Además, estas propiedades adicionales se pueden definir con un nivel de precisión de su comportamiento potentísimo (propiedades de sólo lectura, propiedades enumerables, que tienen funciones setters y getters, …).
¿Por qué no lo he mencionado antes? Porque, desafortunadamente, IE8 y anteriores no soportan este segundo parámetro porque se basa en la función Object.defineProperties
que no es soportada (podéis consultarlo de nuevo en la tabla de compatibilidad que mencioné antes).
Las posibilidades de configuración que nos permite este segundo parámetro son tan potentes que necesitaría una entrada específica para estudiar a fondo todas sus posibilidades.
¿Conclusiones?
Mi intención era terminar con esta tercera entrada mis reflexiones sobre la creación de objetos y la cadena de prototypado en Javascript. Pero, cuando me encontré todas las cosas que tenía que contar sobre la función Object.create
, no he tenido más remedio que dedicarle una entrada exclusivamente para ella.
Lo bueno que ha tenido esta entrada es que nos ha permitido (además de conocer a Object.create
) seguir trabajando nuestro dominio de la cadena de prototipado. Así, incluso hemos estudiado un sustituto de Object.create
y cómo podemos manipular la cadena de prototipado del objeto que creamos para que la simulación sea correcta.
Insisto que la cadena de prototipado es el concepto fundamental para organizar nuestros objetos en Javascript. Y que potenciaremos todavía más cuando lleguemos a la entrada específica sobre la «herencia» de clases. Y que ya os adelanto que consistirá en aumentar la cadena de prototipado:
- En los objetos que hemos creado, la cadena de prototipado sólo tenía dos niveles (el propio objeto y el objeto apuntado por su propiedad
__proto__
). - Para simular la «herencia», lo que haremos será aumentar los niveles de la cadena de prototipado.
Pero eso ya es tema para otra entrada…