Проверка класса: "instanceof"

Оператор instanceof позволяет проверить, какому классу принадлежит объект, с учетом прототипного наследования.

Алгоритм работы instanceof

Вызов obj instanceof Constructor возвращает true, если объект принадлежит классу Constructor или классу, наследующему от него.

Пример использования:

function Rabbit() {}

// создаём объект
var rabbit = new Rabbit();

// проверяем -- этот объект создан Rabbit?
alert( rabbit instanceof Rabbit ); // true, верно

Массив arr принадлежит классу Array, но также и является объектом Object. Это верно, так как массивы наследуют от объектов:

var arr = [];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true

Как это часто бывает в JavaScript, здесь есть ряд тонкостей. Проверка происходит через сравнение прототипов, поэтому в некоторых ситуациях может даже ошибаться!

Алгоритм проверки obj instanceof Constructor:

  1. Получить obj.__proto__
  2. Сравнить obj.__proto__ с Constructor.prototype
  3. Если не совпадает, тогда заменить obj на obj.__proto__ и повторить проверку на шаге 2 до тех пор, пока либо не найдется совпадение (результат true), либо цепочка прототипов не закончится (результат false).

В проверке rabbit instanceof Rabbit совпадение происходит на первом же шаге этого алгоритма, так как: rabbit.__proto__ == Rabbit.prototype.

А если рассмотреть arr instanceof Object, то совпадение будет найдено на следующем шаге, так как arr.__proto__.__proto__ == Object.prototype.

Забавно, что сама функция-конструктор не участвует в процессе проверки! Важна только цепочка прототипов для проверяемого объекта.

Это может приводить к забавному результату и даже ошибкам в проверке при изменении prototype, например:

// Создаём объект rabbit, как обычно
function Rabbit() {}
var rabbit = new Rabbit();

// изменили prototype...
Rabbit.prototype = {};

// ...instanceof перестал работать!
alert( rabbit instanceof Rabbit ); // false

Стоит ли говорить, что это один из доводов для того, чтобы никогда не менять prototype? Так сказать, во избежание.

Не друзья: instanceof и фреймы

Оператор instanceof не срабатывает, когда значение приходит из другого окна или фрейма.

Например, массив, который создан в ифрейме и передан родительскому окну – будет массивом в том ифрейме, но не в родительском окне. Проверка instanceof Array в родительском окне вернёт false.

Вообще, у каждого окна и фрейма – своя иерархия объектов и свой window .

Как правило, эта проблема возникает со встроенными объектами, в этом случае используется проверка внутреннего свойства [[Class]], которое подробнее описано в главе Типы данных: [[Class]], instanceof и утки.

Итого

  • Оператор obj instanceof Func проверяет тот факт, что obj является результатом вызова new Func. Он учитывает цепочку __proto__, поэтому наследование поддерживается.
  • Оператор instanceof не сможет проверить тип значения, если объект создан в одном окне/фрейме, а проверяется в другом. Это потому, что в каждом окне – своя иерархия объектов. Для точной проверки типов встроенных объектов можно использовать свойство [[Class]].

Оператор instanceof особенно востребован в случаях, когда мы работаем с иерархиями классов. Это наилучший способ проверить принадлежность тому или иному классу с учётом наследования.

Задачи

важность: 5

Почему instanceof в коде ниже возвращает true, ведь объект a явно создан не B()?

function A() {}

function B() {}

A.prototype = B.prototype = {};

var a = new A();

alert( a instanceof B ); // true

Да, это выглядит достаточно странно, поскольку объект a не создавался функцией B.

Но методу instanceof на самом деле вообще не важна функция. Он смотрит на её prototype и сверяет его с цепочкой __proto__ объекта.

В данном случае a.__proto__ == B.prototype, поэтому instanceof возвращает true.

По логике instanceof именно прототип задаёт «тип объекта», поэтому instanceof работает именно так.

важность: 5

В коде ниже создаётся простейшая иерархия классов: Animal -> Rabbit.

Что выведет instanceof?

Распознает ли он rabbit как Animal, Rabbit и к тому же Object?

function Animal() {}

function Rabbit() {}
Rabbit.prototype = Object.create(Animal.prototype);

var rabbit = new Rabbit();

alert( rabbit instanceof Rabbit );
alert( rabbit instanceof Animal );
alert( rabbit instanceof Object );

Да, распознает.

Он проверяет наследование с учётом цепочки прототипов.

function Animal() {}

function Rabbit() {}
Rabbit.prototype = Object.create(Animal.prototype);

var rabbit = new Rabbit();

alert( rabbit instanceof Rabbit ); // true
alert( rabbit instanceof Animal ); // true
alert( rabbit instanceof Object ); // true
Карта учебника

Комментарии

перед тем как писать…
  • Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.