in
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
Оператор in
возвращает true
, если свойство содержится в указанном объекте или в его цепочке прототипов.
Оператор in
не может быть использован для поиска значений в других видах коллекций. Чтобы проверить, существует ли определённое значение в массиве, можно использовать Array.prototype.includes()
. А у наборов есть метод Set.prototype.has()
.
Интерактивный пример
Синтаксис
prop in object
#prop in object
Параметры
prop
-
Строковое или символьное значение, представляющее название свойства (несимвольные значения будут преобразованы в строки). Также может быть именем приватного свойства.
object
-
Объект, для которого будет производится проверка, содержит ли он (или его цепочка прототипов) свойство с указанным именем (
prop
).
Исключения
TypeError
:-
Возникает, если
object
не является объектом (например, является примитивом).
Описание
Оператор in
проверяет, существует ли указанное свойство в объекте или его цепочке прототипов. Для проверки наличия только собственных свойств следует использовать Object.hasOwn()
.
Свойство может существовать в объекте, но иметь значение undefined
. Поэтому x in obj
не то же самое, что obj.x !== undefined
. Для того, чтобы оператор in
возвращал значение false
, используйте оператор delete
вместо присваивания свойству значения undefined
.
Также можно использовать оператор in
, чтобы проверить, существует ли в объекте приватное поле класса или метод. Оператор in
возвращает true
, если свойство определено и false
в противном случае. Такая проверка называется бренд-чек, потому что in
возвращает true
только в том случае, когда объект был создан с помощью конструктора класса и имеет доступ к приватным свойствам.
В этом случае используется особый синтаксис: левая сторона оператора in
является идентификатором свойства, а не выражением, но без кавычек (иначе это будет свойством с типом строка, а не приватным свойством).
Поскольку обращение к приватным свойствам объекта не связанного с текущим классом приводит к появлению TypeError
вместо возвращения undefined
, то оператор in
позволяет сократить запись такой проверки:
class C {
#x;
static isC(obj) {
try {
obj.#x;
return true;
} catch {
return false;
}
}
}
До более короткой:
class C {
#x;
static isC(obj) {
return #x in obj;
}
}
Оператор in
также позволяет избежать необходимости обрабатывать ошибки доступа к несуществующим приватным свойствам.
Однако, оператор in
по-прежнему требует предварительно объявлять приватные свойства заранее в окружающем классе, иначе будет выброшена ошибка SyntaxError
("Private field '#x' must be declared in an enclosing class") такая же, как и когда вы пытаетесь получить доступ к необъявленному приватному свойству.
class C {
foo() {
#x in this;
}
}
new C().foo(); // SyntaxError: Private field '#x' must be declared in an enclosing class
Примеры
Обычное использование
В примере ниже показаны некоторые способы использования оператора in
.
// Массивы
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
0 in trees; // true
3 in trees; // true
6 in trees; // false
"bay" in trees; // false (необходимо указать индекс элемента в массиве, а не значение)
"length" in trees; // true (length является свойством Array)
Symbol.iterator in trees; // true
// Уже существующие объекты
"PI" in Math; // true
// Пользовательские объекты
const mycar = { make: "Honda", model: "Accord", year: 1998 };
"make" in mycar; // true
"model" in mycar; // true
Необходимо указать объект справа от оператора in
. Например, можно указать строку, созданную с помощью конструктора String
, но нельзя использовать строковый литерал.
const color1 = new String("green");
"length" in color1; // true
const color2 = "coral";
// сгенерирует ошибку (color2 не является объектом String)
"length" in color2;
Использование оператора in
с неопределёнными или с уже удалёнными свойствами
Если удалить свойство при помощи оператора delete
, то оператор in
возвратит false
для этого свойства.
const mycar = { make: "Honda", model: "Accord", year: 1998 };
delete mycar.make;
"make" in mycar; // false
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
delete trees[3];
3 in trees; // false
Если задать свойству значение undefined
, а не удалять его, то для этого свойства оператор in
вернёт значение true
.
const mycar = { make: "Honda", model: "Accord", year: 1998 };
mycar.make = undefined;
"make" in mycar; // true
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
trees[3] = undefined;
3 in trees; // true
Оператор in
вернёт false
для пустых слотов в массиве, несмотря на то, что прямой доступ к свойству вернёт undefined
.
const empties = new Array(3);
empties[2]; // undefined
2 in empties; // false
Чтобы избежать подобного, следует заполнять новый массив непустыми значениями и не записывать значения по индексам, превышающим длину массива.
const empties = new Array(3).fill(undefined);
2 in empties; // true
Наследуемые свойства
Оператор in
возвратит true
для свойств, которые унаследованы по цепочке прототипов. Это может быть нежелательно при использовании объектов для хранения произвольных пар ключ-значение.
const ages = { alice: 18, bob: 27 };
function hasPerson(name) {
return name in ages;
}
hasPerson("hasOwnProperty"); // true
Можно использовать Object.hasOwn()
, чтобы проверить, существует ли в объекте данный ключ.
const ages = { alice: 18, bob: 27 };
function hasPerson(name) {
return Object.hasOwn(ages, name);
}
hasPerson("hasOwnProperty"); // false
Также можно использовать объект с прототипом null или Map
, чтобы избежать других ошибок.
const ages = new Map([
["alice", 18],
["bob", 27],
]);
function hasPerson(name) {
return ages.has(name);
}
hasPerson("hasOwnProperty"); // false
Использование оператора in
для реализации бренд-чека
Фрагмент кода ниже демонстрирует статическую функцию, которая проверяет, был ли объект создан конструктором класса Person
, и следовательно безопасно ли использовать методы этого класса.
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
}
Это помогает предотвратить следующую ошибку:
const p2 = {};
p1.ageDifference(p2); // TypeError: Cannot read private member #age from an object whose class did not declare it
Без оператора in
пришлось бы использовать блок try...catch
, чтобы проверить, есть ли в объекте приватное свойство.
Также это можно реализовать с помощью метода класса @@hasInstance
, и в дальнейшем использовать оператор instanceof
для выполнения такой же проверки (которая по умолчанию проверяет только наличие Person.prototype
в цепочке прототипов объекта).
class Person {
#age;
constructor(age) {
this.#age = age;
}
static [Symbol.hasInstance](o) {
// Проверяем `this` для предотвращения ложно-положительных
// результатов при вызове `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
}
Дополнительные примеры есть в разделе «Приватные свойства» и в руководстве по классам.
Спецификации
Specification |
---|
ECMAScript Language Specification # sec-relational-operators |
Совместимость с браузерами
BCD tables only load in the browser