in

Оператор in возвращает true, если свойство содержится в указанном объекте или в его цепочке прототипов.

Оператор in не может быть использован для поиска значений в других видах коллекций. Чтобы проверить, существует ли определённое значение в массиве, можно использовать Array.prototype.includes(). А у наборов есть метод Set.prototype.has().

Интерактивный пример

Синтаксис

js
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 позволяет сократить запись такой проверки:

js
class C {
  #x;
  static isC(obj) {
    try {
      obj.#x;
      return true;
    } catch {
      return false;
    }
  }
}

До более короткой:

js
class C {
  #x;
  static isC(obj) {
    return #x in obj;
  }
}

Оператор in также позволяет избежать необходимости обрабатывать ошибки доступа к несуществующим приватным свойствам.

Однако, оператор in по-прежнему требует предварительно объявлять приватные свойства заранее в окружающем классе, иначе будет выброшена ошибка SyntaxError ("Private field '#x' must be declared in an enclosing class") такая же, как и когда вы пытаетесь получить доступ к необъявленному приватному свойству.

js
class C {
  foo() {
    #x in this;
  }
}

new C().foo(); // SyntaxError: Private field '#x' must be declared in an enclosing class

Примеры

Обычное использование

В примере ниже показаны некоторые способы использования оператора in.

js
// Массивы
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, но нельзя использовать строковый литерал.

js
const color1 = new String("green");
"length" in color1; // true

const color2 = "coral";
// сгенерирует ошибку (color2 не является объектом String)
"length" in color2;

Использование оператора in с неопределёнными или с уже удалёнными свойствами

Если удалить свойство при помощи оператора delete, то оператор in возвратит false для этого свойства.

js
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.

js
const mycar = { make: "Honda", model: "Accord", year: 1998 };
mycar.make = undefined;
"make" in mycar; // true
js
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
trees[3] = undefined;
3 in trees; // true

Оператор in вернёт false для пустых слотов в массиве, несмотря на то, что прямой доступ к свойству вернёт undefined.

js
const empties = new Array(3);
empties[2]; // undefined
2 in empties; // false

Чтобы избежать подобного, следует заполнять новый массив непустыми значениями и не записывать значения по индексам, превышающим длину массива.

js
const empties = new Array(3).fill(undefined);
2 in empties; // true

Наследуемые свойства

Оператор in возвратит true для свойств, которые унаследованы по цепочке прототипов. Это может быть нежелательно при использовании объектов для хранения произвольных пар ключ-значение.

js
const ages = { alice: 18, bob: 27 };

function hasPerson(name) {
  return name in ages;
}

hasPerson("hasOwnProperty"); // true

Можно использовать Object.hasOwn(), чтобы проверить, существует ли в объекте данный ключ.

js
const ages = { alice: 18, bob: 27 };

function hasPerson(name) {
  return Object.hasOwn(ages, name);
}

hasPerson("hasOwnProperty"); // false

Также можно использовать объект с прототипом null или Map, чтобы избежать других ошибок.

js
const ages = new Map([
  ["alice", 18],
  ["bob", 27],
]);

function hasPerson(name) {
  return ages.has(name);
}

hasPerson("hasOwnProperty"); // false

Использование оператора in для реализации бренд-чека

Фрагмент кода ниже демонстрирует статическую функцию, которая проверяет, был ли объект создан конструктором класса Person, и следовательно безопасно ли использовать методы этого класса.

js
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
}

Это помогает предотвратить следующую ошибку:

js
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 в цепочке прототипов объекта).

js
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

Смотрите также