En JavaScript, las funciones pueden tener propiedades. Todas las funciones tienen una propiedad especial llamada prototype
.
function doSomething() {}
console.log(doSomething.prototype);
// Las funciones en JavaScript siempre tienen una propiedad prototype por defecto
// Excepción: las funciones flecha no tienen la propiedad prototype por defecto
const doSomethingFromArrowFunction = () => {};
console.log(doSomethingFromArrowFunction.prototype);
doSomething()
tiene una propiedad prototype
por defecto. Después de ejecutar el código anterior, la consola mostrará un objeto similar al siguiente:
{ constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
Nota: La consola de Chrome usa [[Prototype]]
para representar el prototipo del objeto, según la terminología de la especificación; Firefox usa .prototype
. Para mantener la consistencia, usaremos [[Prototype]]
.
Podemos agregar propiedades al prototipo de doSomething()
:
function doSomething() {}
doSomething.prototype.foo = "bar";
console.log(doSomething.prototype);
Resultado:
{ foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
Usamos el operador new
para crear una instancia de doSomething()
basada en este prototipo. Llamar a la función con el operador new
devolverá un objeto que es una instancia de la función. Las propiedades pueden agregarse a este objeto.
function doSomething() {}
doSomething.prototype.foo = "bar"; // agregamos una propiedad al prototipo
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // agregamos una propiedad al objeto
console.log(doSomeInstancing);
Resultado:
{ prop: "some value", [[Prototype]]: { foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } } }
El [[Prototype]]
de doSomeInstancing
es doSomething.prototype
. Al acceder a una propiedad de doSomeInstancing
, el tiempo de ejecución primero verifica si doSomeInstancing
tiene esa propiedad.
Si no, el tiempo de ejecución buscará la propiedad en doSomeInstancing.[[Prototype]]
(es decir, doSomething.prototype
). Si doSomeInstancing.[[Prototype]]
tiene la propiedad que se busca, entonces se utilizará esa propiedad.
Si no, se verificará doSomeInstancing.[[Prototype]].[[Prototype]]
. Por defecto, el [[Prototype]]
de la propiedad prototype
de cualquier función es Object.prototype
. Por lo tanto, se buscará en doSomeInstancing.[[Prototype]].[[Prototype]]
(es decir, doSomething.prototype.[[Prototype]]
u Object.prototype
). Este proceso se llama cadena de prototipos.
Si la propiedad no se encuentra en doSomeInstancing.[[Prototype]].[[Prototype]]
, entonces se buscará en doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
. Sin embargo, doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
no existe, ya que Object.prototype.[[Prototype]]
es null
. Después de que se busca en toda la cadena de prototipos, el tiempo de ejecución afirmará que la propiedad no existe y el valor de la propiedad es undefined
.
function doSomething() {}
doSomething.prototype.foo = "bar";
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop: ", doSomeInstancing.prop);
console.log("doSomeInstancing.foo: ", doSomeInstancing.foo);
console.log("doSomething.prop: ", doSomething.prop);
console.log("doSomething.foo: ", doSomething.foo);
console.log("doSomething.prototype.prop:", doSomething.prototype.prop);
console.log("doSomething.prototype.foo: ", doSomething.prototype.foo);
Resultado:
doSomeInstancing.prop: some value
doSomeInstancing.foo: bar
doSomething.prop: undefined
doSomething.foo: undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo: bar