The in
operator returns true
if the specified property is in the specified object or its prototype chain.
Syntax
prop in object
#prop in object
Parameters
prop
- : A string or symbol representing a property name (non-symbols will be coerced to strings). Can also be a private property identifier.
object
- : Object to check if it (or its prototype chain) contains the property with specified name (
prop
).
- : Object to check if it (or its prototype chain) contains the property with specified name (
Exceptions
- TypeError
- : Thrown if
object
is not an object (i.e. a primitive).
- : Thrown if
Description
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
Examples
Basic usage
The following examples show some uses of the in
operator.
// Arrays
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
0 in trees; // returns true
3 in trees; // returns true
6 in trees; // returns false
"bay" in trees; // returns false (you must specify the index number, not the value at that index)
"length" in trees; // returns true (length is an Array property)
Symbol.iterator in trees; // returns true
// Predefined objects
"PI" in Math; // returns true
// Custom objects
const mycar = { make: "Honda", model: "Accord", year: 1998 };
"make" in mycar; // returns true
"model" in mycar; // returns true
You must specify an object on the right side of the in
operator. For example, you can specify a string created with the String
constructor, but you cannot specify a string literal.
const color1 = new String("green");
"length" in color1; // returns true
const color2 = "coral";
// generates an error (color2 is not a String object)
"length" in color2;
Using the in operator with deleted or undefined properties
If you delete a property with the delete
operator, the in
operator returns false
for that property.
const mycar = { make: "Honda", model: "Accord", year: 1998 };
delete mycar.make;
"make" in mycar; // returns false
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
delete trees[3];
3 in trees; // returns false
If you set a property to undefined but do not delete it, the in
operator returns true for that property.
const mycar = { make: "Honda", model: "Accord", year: 1998 };
mycar.make = undefined;
"make" in mycar; // returns true
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
trees[3] = undefined;
3 in trees; // returns true
The in
operator will return false
for empty array slots, even if accessing it directly returns undefined
.
const empties = new Array(3);
empties[2]; // returns undefined
2 in empties; // returns false
To avoid this, make sure a new array is always filled with non-empty values or not write to indexes past the end of array.
const empties = new Array(3).fill(undefined);
2 in empties; // returns true
Inherited properties
The in
operator returns true
for properties in the prototype chain. This may be undesirable if you are using objects to store arbitrary key-value pairs.
const ages = { alice: 18, bob: 27 };
function hasPerson(name) {
return name in ages;
}
hasPerson("hasOwnProperty"); // true
You can use Object.hasOwn to check if the object has the key.
const ages = { alice: 18, bob: 27 };
function hasPerson(name) {
return Object.hasOwn(ages, name);
}
hasPerson("hasOwnProperty"); // false
Alternatively, you should consider using a null prototype object or a Map for storing ages
, to avoid other bugs.
const ages = new Map([
["alice", 18],
["bob", 27],
]);
function hasPerson(name) {
return ages.has(name);
}
hasPerson("hasOwnProperty"); // false
Using the in operator to implement branded checks
The code fragment below demonstrates a static function that tells if an object was created with the Person
constructor and therefore can perform other methods safely.
class Person {
#age;
constructor(age) {
this.#age = age;
}
static isPerson(o) {
return #age in o;
}
ageDifference(other) {
return this.#age - other.#age;
}
}
const p1 = new Person(20);
const p2 = new Person(30);
console.log(p1.ageDifference(p2)); // -10
console.log(Person.isPerson(p1)); // true
if (Person.isPerson(p1) && Person.isPerson(p2)) {
console.log(p1.ageDifference(p2)); // -10
}
It helps to prevent the following case:
const p2 = {};
p1.ageDifference(p2); // TypeError: Cannot read private member #age from an object whose class did not declare it
Without the in
operator, you would have to use a try...catch
block to check if the object has the private property.
You can also implement this as a @@hasInstance
method of the class, so that you can use the instanceof
operator to perform the same check (which, by default, only checks for the existence of Person.prototype
in the object's prototype chain).
class Person {
#age;
constructor(age) {
this.#age = age;
}
static [Symbol.hasInstance](o) {
// Testing `this` to prevent false-positives when
// calling `instanceof SubclassOfPerson`
return this === Person && #age in o;
}
ageDifference(other) {
return this.#age - other.#age;
}
}
const p1 = new Person(20);
const p2 = new Person(30);
if (p1 instanceof Person && p2 instanceof Person) {
console.log(p1.ageDifference(p2)); // -10
}
For more examples, see Private class features and the class guide.