The in
operator tests if a string or symbol property is present in an object or its prototype chain. If you want to check for only non-inherited properties, use Object.hasOwn()
instead.
A property may be present in an object but have value undefined
. Therefore, "x" in obj
is not the same as obj.x !== undefined
. To make in
return false
after a property is added, use the delete
operator instead of setting that property's value to undefined
.
You can also use the in
operator to check whether a particular private class field or method has been defined in an object. The operator returns true
if the property is defined, and false
otherwise. This is known as a branded check, because it returns true
if and only if the object was created with that class constructor, after which you can safely access other private properties as well.
This is a special syntax — the left-hand side of the in
operator is a property identifier instead of an expression, but unquoted (because otherwise it's a string property, not a private property).
Because accessing private properties on objects unrelated to the current class throws a TypeError
instead of returning undefined
, this syntax allows you to shorten:
class C {
#x;
static isC(obj) {
try {
obj.#x;
return true;
} catch {
return false;
}
}
}
To:
class C {
#x;
static isC(obj) {
return #x in obj;
}
}
It also generally avoids the need for dealing with error handling just to access a private property that may be nonexistent.
However, the in
operator still requires the private property to be declared beforehand in the enclosing class — otherwise, it would throw a SyntaxError
("Private field '#x' must be declared in an enclosing class"), the same one as when you try to access an undeclared private property.
class C {
foo() {
#x in this;
}
}
new C().foo(); // SyntaxError: Private field '#x' must be declared in an enclosing class