Logo Studenta

Programación -clsases y prototipos

¡Este material tiene más páginas!

Vista previa del material en texto

JavaScript 
Clases y prototipo
Clases
Muchas veces cuando creamos un objeto, estamos creando una plantilla. En lugar de copiar esa plantilla una y otra vez, Javascript nos da acceso a lo que llamamos un constructor o class. Las clases comparten gran parte de la misma funcionalidad que los objetos normales, pero también se expande mucho en esa funcionalidad. Las clases son útiles para crear muchos objetos que comparten algunas de las mismas propiedades y métodos (como los usuarios en un sitio web).
Clase e instanciación pseudo-clásica
Si tienes experiencia en un lenguaje orientado a objetos (como Java o C#), probablemente estés arraigado con el concepto de clases. Si bien Javascript no proporciona un "verdadero" sistema de clases, hay algo muy familiar. En aras de la discusión, llamaremos a nuestros objetos de clase 'clases'. Se instancia de manera pseudo clásica, usando la palabra clave new, y puede tomar argumentos.
En este ejemplo crearemos una clase Gato. La convención para las clases consiste en dar un nombre en mayúscula al nombre de todo lo que se pueda instanciar con la palabra clave new. Cuando usamos la palabra clave new, Javascript hace un gran trabajo detrás de escena para nosotros y crea y devuelve un objeto automáticamente.
function Gato(nombre) {
 // El nuevo operador crea un objeto, "this"
 this.nombre = nombre;
 this.maullar = function() {
 return 'Mi nombre es ' + this.nombre + ' ... Meow!';
 }
 // Devuelve el objeto "this"
}
const sam = new Gato('Sam');
const kitty = new Gato('Kitty');
console.log(sam.maullar()); // 'Mi nombre es Sam ... Meow!'
console.log(kitty.maullar()); // 'Mi nombre es Kitty ... Meow!'
this en las clases
La palabra clave this puede comenzar a volverse muy confusa cuando comenzamos a usarla en clases. En el último ejemplo lo usamos en el método de los maullidos. Una buena regla general si no está seguro de a qué se refiere this, es observar dónde se llama el método y el objeto a la izquierda del 'punto'. Ese es el objeto al que se refiere this.
Prototipo
La creación de funciones es costosa (refiriéndonos a la capacidad de memoria de una computadora) y cada vez que creamos un nuevo objeto de clase con métodos, estamos recreando esos métodos en la memoria. Puede imaginar que si estamos creando miles de objetos de clase a partir de una clase con docenas de métodos, la memoria se acumulará rápidamente (20.000 - 40.000 métodos). Las clases tienen una forma única de establecer un método una vez y dar acceso a cada objeto de esa clase a esos métodos. Esto se llama el prototype. Cada clase tiene un prototipo de propiedad , que luego podemos establecer en métodos:
function Usuario(nombre, github) {
 this.nombre = nombre;
 this.github = github;
}
Usuario.prototype.introduccion = function(){
 return 'Mi nombre es ' + this.nombre + ', mi usuario de Github es ' + 
this.github + '.';
}
let juan = new Usuario('Juan', 'juan.perez');
let antonio = new Usuario('Antonio', 'ato');
console.log(juan.introduccion()); // Mi nombre es Juan, mi usuario de Github es
 juan.perez.
console.log(antonio.introduccion()); // Mi nombre es Antonio, mi usuario de 
Github es ato.
Los métodos de prototype tienen acceso a la palabra clave this y, al igual que antes, siempre apuntará al objeto (a la izquierda del punto) que lo está llamando.
Hasta ahora siempre que usamos que crear un objeto nuevo declaramos un object literal, pero vamos a ver que hay otros métodos que nos da el prototipo de Object para cumplir esa tarea
Objeto.crear
El método create de los objetos nos permite crear un nuevo objeto a partir de un prototipo especificado.
// creo un objecto con un objeto vacio como proto
> var obj = Object.create({})
> obj
< Object {}
// creo un objeto a partir de un proto de Objeto
> var obj = Object.create(Object.prototype)
// que es lo mismo que crear un objeto vacio literal
> var obj = {}
Objeto.asignar
El metodo assign de los objetos te permite agregar propiedades a un objeto pasado por parámetro
> var obj = {}
// No hace falta guardar el resultado porque los objetos se pasan por `referencia`
> Object.assign(obj, {nombre:'Emi', apellido:'Chequer'})
> obj.nombre
< 'Emi' 
Herencia Clásica
En el paradigma de Programación Orientada a Objetos un tema muy importante es la Herencia y Polimorfismo y de las clases (los vamos a llamar constructores por ahora).
Cuando hacemos referencia a Herencia nos referimos a la capacidad de un constructor de heredar y métodos de otro constructor, así como un Gato es Mamífero propiedades antes que Gato, y hereda sus 'propiedades' (nace, se reproduce y muere).
Cuando hablamos de Polimorfismo nos referimos a la capacidad de que objetos llamados distintos pueden responder a un igual de acuerdo a su propia naturaleza.
Herencia en JavaScript
En JS a diferencia de la herencia clásica nos manejamos con prototipos, que van a tomar los métodos pasados ​​por sus 'padres' mediante la Prototype Chain.
Cuando generamos un arreglo nuevo podemos acceder a métodos como mapo slice gracias a que los heredamos del Objeto Array que está vinculado en la propiedad __proto__y es el siguiente en el Prototype Chain.
Nosotros también podemos generar nuestros propios constructores que de los cuales heredar. Creemos un constructor de cual puede haber variantes.
> function Persona(nombre,apellido,ciudad) {
 this.nombre = nombre;
 this.apellido = apellido;
 this.ciudad = ciudad;
 }
> Persona.prototype.saludar = function() {
 console.log('Soy '+this.nombre+' de '+this.ciudad);
 }
> var Emi = new Persona('Emi', 'Chequer', 'Buenos Aires');
> Emi.saludar()
< 'Soy Emi de Buenos Aires'
Ahora todo Alumno antes de Alumno es una Persona, así que pudimos decir que un Alumno hereda las propiedades de ser Persona.
> function Alumno(nombre,apellido,ciudad,curso) {
 // podría copiar las mismas propiedades de Persona acá adentro
 this.nombre = nombre;
 this.apellido = apellido;
 this.ciudad = ciudad;
 this.curso = curso
 }
Constructores Anidados
Pero en este caso estaríamos repitiendo código, y si en un futuro quisiera cambiar una propiedad tendría que hacerlo en ambos constructores. Descartemos esta opción.
// lo que nosotros queremos es poder reutilizar las propiedades de persona,
> function Alumno(nombre, apellido, ciudad, curso) {
 // usemos nuestro constructor Persona dentro del de Alumno
 Persona.call(this, nombre, apellido, ciudad);
 // vamos a necesitar el call porque queremos que las propiedades de persona, queden en bajo el objeto que va a devolver Alumno, y no uno nuevo del constructor Persona.
 // luego le paso los valores que quiero que reciba el constructor de Alumno
 
 // finalmente le agrego los puntos propios de Alumno
 this.curso = curso;
 this.empresa = 'Aprende programacion';
 }
> var toni = new Alumno('Toni', 'Tralice', 'Tucuman', 'Web Full Stack')
// Ahora si tenemos nuestra instancia creada a partir de ambos constructores
> toni.curso
< Web Full Stack
> toni.apellido
< Tralice
> toni.saludar()
< Uncaught TypeError: toni.saludar is not a 'function'
// que paso?
Como podemos ver los métodos de Personas no fueron pasados ​​a nuestros Alumnos . Veamos un poco el porqué.
El constructor del __proto__ esta ligado a Alumno y luego al Object Object de JS. Pero el método saludar esta en el objeto prototype de Personas... , y esta perfecto, así es como debería funcionar, las instancias acceden al __proto__ que fue vinculado por el constructor para ver que métodos tienen. Nuestro problema es que al llamar a Persona con callen vez de con el método new no se está haciendo ese vínculo en el que Persona.prototype se mete en nuestro Prototype Chain, y entonces las instancias de Alumno no tienen acceso a los métodos de Persona
Vamos a solucionar ese problema agregando al prototipo los métodos de Persona, para esto vamos a usar el método Object.create.
// usamos `Object.create` porque este guardaba el argumento pasado como `__proto__` del objeto a retornar
> Alumno.prototype= Object.create(Persona.prototype)
// si recuerdan el objeto prototype siempre tenía una propiedad constructor que hacía referencia a la función en sí, con la asignación que hicimos arriba lo pisamos, por lo que deberíamos volver a agregarlo.
> Alumno.prototype.constructor = Alumno
> var Franco = new Alumno('Franco','Etcheverri','Montevideo','Bootcamp')
> Franco.saludar()
< 'Soy Franco de Montevideo'
Clases
Las clases son una plantilla para crear objetos. Encapsulan datos con código para trabajar en esos datos. Las clases en JS se basan en prototipos , pero también tienen una sintaxis y una semántica que son exclusivas de las clases.
Descripción
Definición de clases
Las clases son, de hecho, " funciones especiales ", y así como puede definir expresiones de funciones y declaraciones de funciones , una clase se puede definir de dos maneras: una expresión de clase o una declaración de clase .
// Declaration
class Rectangle {
 constructor(height, width) {
 this.height = height;
 this.width = width;
 }
}
// Expression; the class is anonymous but assigned to a variable
const Rectangle = class {
 constructor(height, width) {
 this.height = height;
 this.width = width;
 }
};
// Expression; the class has its own name
const Rectangle = class Rectangle2 {
 constructor(height, width) {
 this.height = height;
 this.width = width;
 }
};
Al igual que las expresiones de función, las expresiones de clase pueden ser anónimas o tener un nombre diferente al de la variable a la que están asignadas. Sin embargo, a diferencia de las declaraciones de función, las declaraciones de clase tienen las mismas restricciones de zona muerta temporal let  o const y se comportan como si no estuvieran izadas .
Cuerpo de clase
El cuerpo de una clase es la parte que está entre llaves {}. Aquí es donde define los miembros de la clase, como métodos o constructor.
El cuerpo de una clase se ejecuta en modo estricto incluso sin la "use strict" directiva.
Un elemento de clase se puede caracterizar por tres aspectos:
Tipo: getter, setter, método o campo
Ubicación: estática o instancia
Visibilidad: Pública o privada
Juntos, suman hasta 16 combinaciones posibles. Para dividir la referencia de manera más lógica y evitar la superposición de contenido, los diferentes elementos se presentan en detalle en diferentes páginas:
Definiciones de métodos
Método de instancia pública
adquiridor
Captador de instancias públicas
setter
Establecedor de instancias públicas
Campos de clase pública
Campo de instancia pública
static
Método estático público, getter, setter y campo
Características de la clase privada
Todo lo que es privado
Nota: Las características privadas tienen la restricción de que todos los nombres de propiedad declarados en la misma clase deben ser únicos. Todas las demás propiedades públicas no tienen esta restricción: puede tener varias propiedades públicas con el mismo nombre y la última sobrescribe a las demás. Este es el mismo comportamiento que en los inicializadores de objetos .
Además, hay dos sintaxis de elementos de clase especiales: constructor y bloques de inicialización estáticos , con sus propias referencias.
Constructor
El constructor método es un método especial para crear e inicializar un objeto creado con una clase. Solo puede haber un método especial con el nombre "constructor" en una clase: SyntaxError se lanza una si la clase contiene más de una aparición de un constructor método.
Un constructor puede usar el super palabra clave para llamar al constructor de la superclase.
Puede crear propiedades de instancia dentro del constructor:
class Rectangle {
 constructor(height, width) {
 this.height = height;
 this.width = width;
 }
}
Alternativamente, si los valores de las propiedades de su instancia no dependen de los argumentos del constructor, puede definirlos como campos de clase .
Bloques de inicialización estáticos
Los bloques de inicialización estática permiten la inicialización flexible de propiedades estáticas , incluida la evaluación de declaraciones durante la inicialización, al tiempo que otorgan acceso al ámbito privado.
Se pueden declarar múltiples bloques estáticos, y estos se pueden intercalar con la declaración de campos y métodos estáticos (todos los elementos estáticos se evalúan en orden de declaración).
Métodos
Los métodos se definen en el prototipo de cada instancia de clase y son compartidos por todas las instancias. Los métodos pueden ser funciones simples, funciones asíncronas, funciones generadoras o funciones generadoras asíncronas. Para obtener más información, consulte definiciones de métodos .
class Rectangle {
 constructor(height, width) {
 this.height = height;
 this.width = width;
 }
 // Getter
 get area() {
 return this.calcArea();
 }
 // Method
 calcArea() {
 return this.height * this.width;
 }
 *getSides() {
 yield this.height;
 yield this.width;
 yield this.height;
 yield this.width;
 }
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
console.log([...square.getSides()]); // [10, 10, 10, 10]
Métodos y campos estáticos
La static palabra clave define un método o campo estático para una clase. Las propiedades estáticas (campos y métodos) se definen en la propia clase en lugar de cada instancia. Los métodos estáticos a menudo se usan para crear funciones de utilidad para una aplicación, mientras que los campos estáticos son útiles para cachés, configuración fija o cualquier otro dato que no necesite replicarse entre instancias.
class Point {
 constructor(x, y) {
 this.x = x;
 this.y = y;
 }
 static displayName = "Point";
 static distance(a, b) {
 const dx = a.x - b.x;
 const dy = a.y - b.y;
 return Math.hypot(dx, dy);
 }
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance; // undefined
p2.displayName; // undefined
p2.distance; // undefined
console.log(Point.displayName); // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755
Declaraciones de campo
Con la sintaxis de declaración de campo de clase, el ejemplo del constructor se puede escribir como:
class Rectangle {
 height = 0;
 width;
 constructor(height, width) {
 this.height = height;}
 this.width = width;
 }
}
Los campos de clase son similares a las propiedades de los objetos, no a las variables, por lo que no usamos palabras clave para const declararlos. En JavaScript, las funciones privadas usan una sintaxis de identificador especial, por lo que tampoco se deben usar palabras clave modificadoras como publicy .private
Como se vio anteriormente, los campos se pueden declarar con o sin un valor predeterminado. Los campos sin valores predeterminados tienen como valor predeterminado undefined. Al declarar los campos por adelantado, las definiciones de clase se vuelven más autodocumentadas y los campos siempre están presentes, lo que ayuda con las optimizaciones.
Ver  los campos de clase pública para obtener más información.
Características de la clase privada
Usando campos privados, la definición se puede refinar como se muestra a continuación.
class Rectangle {
 #height = 0;
 #width;
 constructor(height, width) {
 this.#height = height;
 this.#width = width;
 }
}
Es un error hacer referencia a campos privados desde fuera de la clase; solo se pueden leer o escribir dentro del cuerpo de la clase. Al definir cosas que no son visibles fuera de la clase, se asegura de que los usuarios de sus clases no puedan depender de elementos internos, que pueden cambiar de una versión a otra.
Los campos privados solo se pueden declarar por adelantado en una declaración de campo. No se pueden crear más tarde asignándoles, de la misma forma que se pueden crear las propiedades normales.
Para obtener más información, consulta las características de las clases privadas .
Herencia
La extends palabra clave se usa en declaraciones de clase o expresiones de clase para crear una clase como hijo de otro constructor (ya sea una claseo una función).
class Animal {
 constructor(name) {
 this.name = name;
 }
 speak() {
 console.log(`${this.name} makes a noise.`);
 }
}
class Dog extends Animal {
 constructor(name) {
 super(name); // call the super class constructor and pass in the name parameter
 }
 speak() {
 console.log(`${this.name} barks.`);
 }
}
const d = new Dog("Mitzie");
d.speak(); // Mitzie barks.
Si hay un constructor presente en la subclase, primero debe llamar super() antes de usar this. La super palabra clave también se puede usar para llamar a los métodos correspondientes de la superclase.
class Cat {
 constructor(name) {
 this.name = name;
 }
 speak() {
 console.log(`${this.name} makes a noise.`);
 }
}
class Lion extends Cat {
 speak() {
 super.speak();
 console.log(`${this.name} roars.`);
 }
}
const l = new Lion("Fuzzy");
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.
Orden de evaluación
Cuando se evalúa una classdeclaración o classexpresión , sus diversos componentes se evalúan en el siguiente orden:
La extends cláusula, si está presente, se evalúa primero. Debe evaluarse como una función constructora válida o null, o TypeError se lanza un .
El constructor método se extrae y se sustituye por una implementación predeterminada si constructor no está presente. Sin embargo, debido a que la constructor definición es solo una definición de método, este paso no es observable.
Las claves de propiedad de los elementos de la clase se evalúan en el orden de declaración. Si se calcula la clave de propiedad, se evalúa la expresión calculada y el this valor se establece en el this valor que rodea a la clase (no la clase en sí). Ninguno de los valores de propiedad ha sido evaluado todavía.
Los métodos y accesores se instalan en el orden de declaración. Los métodos y accesores de instancia se instalan en la prototype propiedad de la clase actual, y los métodos y accesores estáticos se instalan en la clase misma. Los métodos de instancia privada y los accesores se guardan para instalarse en la instancia directamente más tarde. Este paso no es observable.
La clase ahora se inicializa con el prototipo especificado por extends y la implementación especificada por constructor. Para todos los pasos anteriores, si una expresión evaluada intenta acceder al nombre de la clase, ReferenceErrorse lanza un porque la clase aún no se ha inicializado.
Los valores de los elementos de clase se evalúan en el orden de declaración:
Para cada campo de instancia (público o privado), se guarda su expresión inicializadora. El inicializador se evalúa durante la creación de instancias, al comienzo del constructor (para clases base) o inmediatamente antes de que super()regrese la llamada (para clases derivadas).
Para cada campo estático (público o privado), su inicializador se evalúa con this el conjunto de la propia clase y la propiedad se crea en la clase.
Los bloques de inicialización estáticos se evalúan con this el conjunto de la propia clase.
La clase ahora está completamente inicializada y se puede usar como una función constructora.
Para saber cómo se crean las instancias, consulte la constructor referencia.
Ejemplos
Enlazando esto con instancias y métodos estáticos.
Cuando se llama a un método estático o de instancia sin un valor para this, como al asignar el método a una variable y luego llamarlo, el this valor estará undefined dentro del método. Este comportamiento es el mismo incluso si la "use strict" directiva no está presente, porque el código dentro del class cuerpo siempre se ejecuta en modo estricto.
class Animal {
 speak() {
 return this;
 }
 static eat() {
 return this;
 }
}
const obj = new Animal();
obj.speak(); // the Animal object
const speak = obj.speak;
speak(); // undefined
Animal.eat(); // class Animal
const eat = Animal.eat;
eat(); // undefined
Si reescribimos lo anterior usando la sintaxis tradicional basada en funciones en modo no estricto, entonces this las llamadas a métodos se vinculan automáticamente a globalThis. En modo estricto, el valor de this permanece como undefined.
function Animal() {}
Animal.prototype.speak = function () {
 return this;
};
Animal.eat = function () {
 return this;
};
const obj = new Animal();
const speak = obj.speak;
speak(); // global object (in non–strict mode)
const eat = Animal.eat;
eat(); // global object (in non-strict mode)
Objeto
El Object tipo representa uno de los tipos de datos de JavaScript . Se utiliza para almacenar varias colecciones con clave y entidades más complejas. Los objetos se pueden crear usando el Object() constructor o el inicializador de objetos/sintaxis literal .
Descripción
Casi todos los objetos en JavaScript son instancias de Object; un objeto típico hereda propiedades (incluidos los métodos) de Object.prototype, aunque estas propiedades pueden estar sombreadas (también conocidas como anuladas). Los únicos objetos que no heredan de Object.prototype son aquellos con nullprototipo o que descienden de otros null objetos prototipo.
Todos Object.prototype los objetos ven los cambios en el objeto a través del encadenamiento de prototipos, a menos que las propiedades y los métodos sujetos a esos cambios se anulen más adelante en la cadena de prototipos. Esto proporciona un mecanismo muy poderoso, aunque potencialmente peligroso, para anular o extender el comportamiento de los objetos. Para hacerlo más seguro, es el único objeto en el lenguaje principal de JavaScript que tiene un prototipo inmutable : el prototipo siempre es y no se puede cambiar.Object.prototypeObject.prototypenull
Propiedades del prototipo de objeto
Debe evitar llamar a cualquier Object.prototype método, especialmente a aquellos que no pretenden ser polimórficos (es decir, solo su comportamiento inicial tiene sentido y ningún objeto descendente podría anularlo de manera significativa). Todos los objetos que descienden de Object.prototype pueden definir una propiedad propia personalizada que tiene el mismo nombre, pero con una semántica completamente diferente de lo que espera. Además, estas propiedades no las heredan los nullobjetos prototipo . Todas las utilidades modernas de JavaScript para trabajar con objetos son estáticas . Más específicamente:
valueOf(), toString() y toLocaleString() existen para ser polimórficos y debe esperar que el objeto defina su propia implementación con comportamientos sensibles, para que pueda llamarlos como métodos de instancia. Sin embargo, valueOf() y toString() generalmente se llaman implícitamente a través de la conversión de tipo y no necesita llamarlos usted mismo en su código.
__defineGetter__(), __defineSetter__(), __lookupGetter__()y __lookupSetter__() están en desuso y no deben utilizarse. Utilice las alternativas estáticas Object.defineProperty()y Object.getOwnPropertyDescriptor() en su lugar.
La __proto__ propiedad está en desuso y no debe utilizarse. Las alternativas Object.getPrototypeOf()y Object.setPrototypeOf()son métodos estáticos.
Los métodos propertyIsEnumerable()y hasOwnProperty() se pueden reemplazar por los métodos estáticos Object.getOwnPropertyDescriptor()y Object.hasOwn(), respectivamente.
El isPrototypeOf() método generalmente se puede reemplazar con instanceof, si está verificando la prototype propiedad de un constructor.
En caso de que no exista un método estático semánticamente equivalente, o si realmente desea utilizar el Object.prototype método, debe dirigir call() el Object.prototype método en su objeto de destino para evitar que el objeto tenga una propiedad anulada que produzca resultados inesperados.
const obj = {
 foo: 1,
 // You should not define such a method on your own object,
 // but you may not be able to prevent it from happening if
 // you are receiving the object from external input
 propertyIsEnumerable() {
 return false;
 },
};
obj.propertyIsEnumerable("foo"); // false; unexpected result
Object.prototype.propertyIsEnumerable.call(obj, "foo"); // true; expected result
Eliminar una propiedad de un objeto
No hay ningún método en un Objetoen sí mismo para eliminar sus propias propiedades (como Map.prototype.delete()). Para hacerlo, se debe usar el operador de eliminación .
objetos de prototipo nulo
Casi todos los objetos en JavaScript heredan en última instancia Object.prototype (ver herencia y la cadena de prototipos ). Sin embargo, puede crear null objetos prototipo usando Object.create(null)o la sintaxis del inicializador de objetos con __proto__: null(nota: la __proto__clave en los literales de objetos es diferente de la Object.prototype.__proto__propiedad obsoleta). También puede cambiar el prototipo de un objeto existente nullllamando a Object.setPrototypeOf(obj, null).
const obj = Object.create(null);
const obj2 = { __proto__: null };
Un objeto con un null prototipo puede comportarse de formas inesperadas, porque no hereda ningún método de objeto de Object.prototype. Esto es especialmente cierto durante la depuración, ya que las funciones comunes de utilidad de detección/conversión de propiedad de objeto pueden generar errores o perder información (especialmente si se usan trampas de error silenciosas que ignoran los errores).
Por ejemplo, la falta de Object.prototype.toString() a menudo hace que la depuración sea intratable:
const normalObj = {}; // create a normal object
const nullProtoObj = Object.create(null); // create an object with "null" prototype
console.log(`normalObj is: ${normalObj}`); // shows "normalObj is: [object Object]"
console.log(`nullProtoObj is: ${nullProtoObj}`); // throws error: Cannot convert object to primitive value
alert(normalObj); // shows [object Object]
alert(nullProtoObj); // throws error: Cannot convert object to primitive value
Otros métodos fallarán también.
normalObj.valueOf(); // shows {}
nullProtoObj.valueOf(); // throws error: nullProtoObj.valueOf is not a function
normalObj.hasOwnProperty("p"); // shows "true"
nullProtoObj.hasOwnProperty("p"); // throws error: nullProtoObj.hasOwnProperty is not a function
normalObj.constructor; // shows "Object() { [native code] }"
nullProtoObj.constructor; // shows "undefined"
Podemos toString volver a agregar el método al objeto de prototipo nulo asignándole uno:
nullProtoObj.toString = Object.prototype.toString; // since new object lacks toString, add the original generic 
one back
console.log(nullProtoObj.toString()); // shows "[object Object]"
console.log(`nullProtoObj is: ${nullProtoObj}`); // shows "nullProtoObj is: [object Object]"
A diferencia de los objetos normales, en los que toString() se encuentra sobre el prototipo del objeto, el toString() método aquí es una propiedad propia de nullProtoObj. Esto se debe a nullProtoObj que no tiene ( null) prototipo.
En la práctica, los objetos con null prototipo se suelen utilizar como un sustituto económico de los mapas . La presencia de Object.prototype propiedades causará algunos errores:
const ages = { alice: 18, bob: 27 };
function hasPerson(name) {
 return name in ages;
}
function getAge(name) {
 return ages[name];
}
hasPerson("hasOwnProperty"); // true
getAge("toString"); // [Function: toString]
El uso de un objeto de prototipo nulo elimina este peligro sin introducir demasiada complejidad en las funciones hasPersony :getAge
const ages = Object.create(null, {
 alice: { value: 18, enumerable: true },
 bob: { value: 27, enumerable: true },
});
hasPerson("hasOwnProperty"); // false
getAge("toString"); // undefined
En tal caso, la adición de cualquier método debe hacerse con cautela, ya que pueden confundirse con los otros pares clave-valor almacenados como datos.
Hacer que su objeto no herede de Object.prototypetambién previene los ataques de contaminación de prototipos. Si un script malicioso agrega una propiedad a Object.prototype, se podrá acceder a ella en todos los objetos de su programa, excepto en los objetos que tengan un prototipo nulo.
const user = {};
// A malicious script:
Object.prototype.authenticated = true;
// Unexpectedly allowing unauthenticated user to pass through
if (user.authenticated) {
 // access confidential data
}
JavaScript también tiene API integradas que producen nullobjetos prototipo, especialmente aquellos que usan objetos como colecciones ad hoc de clave-valor. Por ejemplo:
El valor de retorno deArray.prototype.group()
Las propiedades groupsy indices.groupsdel resultado deRegExp.prototype.exec()
Array.prototype[@@unscopables](todos @@unscopableslos objetos deben tener null-prototipo)
import.meta
Objetos de espacio de nombres del módulo, obtenidos a través de import * as ns from "module";oimport()
El término " null objeto prototipo" a menudo también incluye cualquier objeto que no esté Object.prototypeen su cadena de prototipos. Dichos objetos se pueden crear extends nullcuando se usan clases.
Coerción de objetos
Muchas operaciones integradas que esperan que los objetos primero coaccionen sus argumentos a los objetos. La operación se puede resumir de la siguiente manera:
Los objetos se devuelven tal cual.
undefined y null lanza un TypeError.
Number Las primitivas , String, Boolean, Symbol, BigInt se envuelven en sus contenedores de objetos correspondientes.
La mejor manera de lograr el mismo efecto en JavaScript es a través del Object() constructor. Object(x) se convierte xen un objeto, y para undefined o null, devuelve un objeto simple en lugar de lanzar un TypeError.
Los lugares que usan la coerción de objetos incluyen:
El objectparámetro de for...in bucles.
El thisvalor de Array los métodos.
Parámetros de Object métodos como Object.keys().
Encajonamiento automático cuando se accede a una propiedad en un valor primitivo, ya que las primitivas no tienen propiedades.
El this valor al llamar a una función no estricta. Las primitivas están encuadradas mientras nul ly undefined se reemplazan con el objeto global .
A diferencia de la conversión a primitivas , el proceso de coerción de objetos en sí mismo no es observable de ninguna manera, ya que no invoca código personalizado como toStringovalueOf métodos.
Constructor
Object()
Convierte la entrada en un objeto.
Métodos estáticos
Object.assign()
Copia los valores de todas las propiedades propias enumerables de uno o más objetos de origen a un objeto de destino.
Object.create()
Crea un nuevo objeto con el objeto prototipo y las propiedades especificados.
Object.defineProperties()
Agrega las propiedades con nombre descritas por los descriptores proporcionados a un objeto.
Object.defineProperty()
Agrega la propiedad con nombre descrita por un descriptor dado a un objeto.
Object.entries()
Devuelve una matriz que contiene todos los pares de propiedades de cadena enumerables propias [key, value] de un objeto dado .
Object.freeze()
Congela un objeto. Otro código no puede eliminar o cambiar sus propiedades.
Object.fromEntries()
Devuelve un nuevo objeto de un iterable de [key, value] pares. (Este es el reverso de Object.entries).
Object.getOwnPropertyDescriptor()
Devuelve un descriptor de propiedad para una propiedad con nombre en un objeto.
Object.getOwnPropertyDescriptors()
Devuelve un objeto que contiene todos los descriptores de propiedad propios de un objeto.
Object.getOwnPropertyNames()
Devuelve una matriz que contiene los nombres de todas las propiedades enumerables y no enumerables propias del objeto dado.
Object.getOwnPropertySymbols()
Devuelve una matriz de todas las propiedades de los símbolos que se encuentran directamente sobre un objeto dado.
Object.getPrototypeOf()
Devuelve el prototipo ( [[Prototype]]propiedad interna) del objeto especificado.
Object.hasOwn()
Devuelve true si el objeto especificado tiene la propiedad indicada como propiedad propia , o false si la propiedad se hereda o no existe.
Object.is()
Compara si dos valores son el mismo valor. Iguala todos NaN los valores (lo que difiere tanto de IsLooselyEqualused by ==como IsStrictlyEqualde used by ===).
Object.isExtensible()
Determina si se permite extender un objeto.
Object.isFrozen()
Determina si un objeto se congeló.
Object.isSealed()
Determina si un objeto está sellado.
Object.keys()
Devuelve una matriz que contiene los nombresde todas las propiedades de cadena enumerables propias del objeto dado .
Object.preventExtensions()
Previene cualquier extensión de un objeto.
Object.seal()
Evita que otro código elimine propiedades de un objeto.
Object.setPrototypeOf()
Establece el prototipo del objeto (su [[Prototype]]propiedad interna).
Object.values()
Devuelve una matriz que contiene los valores que corresponden a todas las propiedades de cadena enumerables propias de un objeto dado .
Propiedades de instancia
Estas propiedades están definidas Object.prototypey compartidas por todas Object las instancias.
Object.prototype.__proto__ Obsoleto
Apunta al objeto que se usó como prototipo cuando se creó una instancia del objeto.
Object.prototype.constructor
La función constructora que creó el objeto de instancia. Para Object instancias sencillas, el valor inicial es el Object constructor. Cada instancia de otros constructores hereda la constructor propiedad de su Constructor.prototypeo bjeto respectivo.
Métodos de instancia
Object.prototype.__defineGetter__() Obsoleto
Asocia una función con una propiedad que, cuando se accede, ejecuta esa función y devuelve su valor de retorno.
Object.prototype.__defineSetter__() Obsoleto
Asocia una función con una propiedad que, cuando se establece, ejecuta esa función que modifica la propiedad.
Object.prototype.__lookupGetter__() Obsoleto
Devuelve la función enlazada como getter a la propiedad especificada.
Object.prototype.__lookupSetter__() Obsoleto
Devuelve la función enlazada como setter a la propiedad especificada.
Object.prototype.hasOwnProperty()
Devuelve un valor booleano que indica si un objeto contiene la propiedad especificada como propiedad directa de ese objeto y no heredada a través de la cadena de prototipos.
Object.prototype.isPrototypeOf()
Devuelve un valor booleano que indica si el objeto al que se llama este método está en la cadena de prototipo del objeto especificado.
Object.prototype.propertyIsEnumerable()
Devuelve un valor booleano que indica si la propiedad especificada es la propiedad propia enumerable del objeto .
Object.prototype.toLocaleString()
llamadas toString()_
Object.prototype.toString()
Devuelve una representación de cadena del objeto.
Object.prototype.valueOf()
Devuelve el valor primitivo del objeto especificado.
Ejemplos
Construyendo objetos vacíos
El siguiente ejemplo crea objetos vacíos usando la newpalabra clave con diferentes argumentos:
JSCopiar al portapapeles
const o1 = new Object();
const o2 = new Object(undefined);
const o3 = new Object(null);
Uso de Object para crear objetos booleanos
Los siguientes ejemplos almacenan Booleanobjetos en o:
JSCopiar al portapapeles
// equivalent to const o = new Boolean(true)
const o = new Object(true);
JSCopiar al portapapeles
// equivalent to const o = new Boolean(false)
const o = new Object(Boolean());
Prototipos de objetos
Al alterar el comportamiento de Object.prototypelos métodos existentes, considere inyectar código ajustando su extensión antes o después de la lógica existente. Por ejemplo, este código (no probado) ejecutará de forma condicional la lógica personalizada antes de que se ejecute la lógica integrada o la extensión de otra persona.
Al modificar prototipos con ganchos, pase thisy los argumentos (el estado de la llamada) al comportamiento actual llamando apply()a la función. Este patrón se puede utilizar para cualquier prototipo, como Node.prototype, Function.prototype, etc.
JSCopiar al portapapeles
const current = Object.prototype.valueOf;
// Since my property "-prop-value" is cross-cutting and isn't always
// on the same prototype chain, I want to modify Object.prototype:
Object.prototype.valueOf = function (...args) {
 if (Object.hasOwn(this, "-prop-value")) {
 return this["-prop-value"];
 } else {
 // It doesn't look like one of my objects, so let's fall back on
 // the default behavior by reproducing the current behavior as best we can.
 // The apply behaves like "super" in some other languages.
 // Even though valueOf() doesn't take arguments, some other hook may.
 return current.apply(this, args);
 }
};
Advertencia: la modificación de la prototypepropiedad de cualquier constructor integrado se considera una mala práctica y pone en riesgo la compatibilidad futura.

Continuar navegando