Verificaciones de marca privada, también conocido como `#foo in obj`
El operador in
puede ser usado para comprobar si un objeto dado (o cualquier objeto en su cadena de prototipos) tiene la propiedad especificada:
const o1 = {'foo': 0};
console.log('foo' in o1); // true
const o2 = {};
console.log('foo' in o2); // false
const o3 = Object.create(o1);
console.log('foo' in o3); // true
La característica de verificaciones de marca privada extiende el operador in
para soportar campos privados de clase:
class A {
static test(obj) {
console.log(#foo in obj);
}
#foo = 0;
}
A.test(new A()); // true
A.test({}); // false
class B {
#foo = 0;
}
A.test(new B()); // false; no es el mismo #foo
Dado que los nombres privados solo están disponibles dentro de la clase que los define, la prueba también debe ocurrir dentro de la clase, por ejemplo, en un método como static test
arriba.
Las instancias de subclases reciben campos privados de la clase padre como propiedades propias:
class SubA extends A {};
A.test(new SubA()); // true
Pero los objetos creados con Object.create
(o que tienen el prototipo configurado más tarde mediante el setter __proto__
o Object.setPrototypeOf
) no reciben los campos privados como propiedades propias. Debido a que la búsqueda de campos privados solo funciona en propiedades propias, el operador in
no encuentra estos campos heredados:
const a = new A();
const o = Object.create(a);
A.test(o); // false, el campo privado es heredado y no propio
A.test(o.__proto__); // true
const o2 = {};
Object.setPrototypeOf(o2, a);
A.test(o2); // false, el campo privado es heredado y no propio
A.test(o2.__proto__); // true
Acceder a un campo privado inexistente genera un error, a diferencia de las propiedades normales, donde acceder a una propiedad inexistente devuelve undefined
pero no genera excepciones. Antes de las verificaciones de marca privada, los desarrolladores se veían obligados a usar un bloque try
-catch
para implementar un comportamiento alternativo en casos donde un objeto no tuviera el campo privado necesario:
class D {
use(obj) {
try {
obj.#foo;
} catch {
// Alternativa para el caso en que obj no tuviera #foo
}
}
#foo = 0;
}
Ahora la existencia del campo privado puede ser comprobada usando una verificación de marca privada:
class E {
use(obj) {
if (#foo in obj) {
obj.#foo;
} else {
// Alternativa para el caso en que obj no tuviera #foo
}
}
#foo = 0;
}
Pero ten cuidado: la existencia de un campo privado no garantiza que el objeto tenga todos los campos privados declarados en una clase. El siguiente ejemplo muestra un objeto parcialmente construido que tiene solo uno de los dos campos privados declarados en su clase:
let halfConstructed;
class F {
m() {
console.log(#x in this); // true
console.log(#y in this); // false
}
#x = 0;
#y = (() => {
halfConstructed = this;
throw 'error';
})();
}
try {
new F();
} catch {}
halfConstructed.m();