Что такое оператор instanceof в JavaScript?

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

  • Что это?
  • Какие проблемы решает?
  • Когда это уместно, а когда нет?

person Alon Gubkin    schedule 15.03.2010    source источник
comment
Хотя приведенные ниже ответы очень полезны, вы не часто используете их (по крайней мере, я не использую) в реальных приложениях. Можно было бы создать свойство object.type, которое содержит строку и проверяет ее.   -  person Omar Al-Ithawi    schedule 07.03.2012
comment
JS не имеет никакого смысла: "foo" instanceof String = ›false, 1 instanceof Number =› false, {} instanceof Object = ›false. Чего-чего?!   -  person morbusg    schedule 19.11.2013
comment
@morbusg ваш комментарий вводит в заблуждение. Первый "foo" instanceof String => false правильный, потому что typeof "foo" == 'string'. new String("foo") instanceof String => true, потому что typeof String == 'function' - вы должны относиться к функции как к классу (определение класса). Переменная становится instanceof некоторым function (классом), когда вы назначаете ее как var v = new AnythingWhatTypeofEqualsFunction(). То же самое и с 1. typeof 1 == 'number' - 'число' не 'функция' :) Далее - {} instanceof Object это TRUE в узле и современных браузерах   -  person fider    schedule 12.12.2013
comment
@fider: Это был комментарий к спецификации языка от рубиста.   -  person morbusg    schedule 12.12.2013
comment
@morbusg - ({}) instanceof Object вернет true. Фактически, написанный вами код выдаст вам ошибку.   -  person DDM    schedule 17.08.2014
comment
@fider «потому что typeof String == 'function'» - Вы не ошиблись? typeof new String("") === "object". Вот почему new String("") instanceof String это true. String instanceof Function, но не String instanceof String. Кстати, "" instanceof String быть false имеет смысл, потому что "" не является экземпляром.   -  person Sebastian Simon    schedule 15.03.2021
comment
@SebastianSimon, проверьте мой ответ ниже (слишком долго, чтобы комментировать)   -  person fider    schedule 18.03.2021


Ответы (10)


экземпляр

Операнд левой стороны (LHS) - это фактический объект, проверяемый для операнда правой стороны (RHS), который является фактическим конструктором класса. Основное определение:

Checks the current object and returns true if the object
is of the specified object type.

Вот несколько хороших примеров и пример взят непосредственно с сайта разработчиков Mozilla:

var color1 = new String("green");
color1 instanceof String; // returns true
var color2 = "coral"; //no type specified
color2 instanceof String; // returns false (color2 is not a String object)

Стоит упомянуть, что instanceof принимает значение true, если объект наследуется от прототипа класса:

var p = new Person("Jon");
p instanceof Person

То есть p instanceof Person верно, поскольку p наследуется от Person.prototype.

По запросу OP

Я добавил небольшой пример с примерами кода и объяснением.

Когда вы объявляете переменную, вы даете ей определенный тип.

Например:

int i;
float f;
Customer c;

Выше показаны некоторые переменные, а именно i, f и c. Это integer, float и определенный пользователем Customer тип данных. Типы, подобные приведенным выше, могут быть для любого языка, а не только для JavaScript. Однако с помощью JavaScript, когда вы объявляете переменную, вы явно не определяете тип, var x, x может быть числом / строкой / типом данных, определяемым пользователем. Итак, что делает instanceof, это проверяет объект, чтобы увидеть, относится ли он к указанному типу, поэтому мы могли бы сделать сверху Customer объект:

var c = new Customer();
c instanceof Customer; //Returns true as c is just a customer
c instanceof String; //Returns false as c is not a string, it's a customer silly!

Выше мы видели, что c был объявлен с типом Customer. Мы обновили его и проверили, относится ли он к типу Customer или нет. Конечно, вернет истину. Затем, все еще используя объект Customer, мы проверяем, является ли он String. Нет, определенно не String, мы создали Customer объект, а не String объект. В этом случае возвращается false.

Это действительно так просто!

person JonH    schedule 15.03.2010
comment
@Alon - я добавил вам пример. См. Выше, color1 имеет строковый тип, поэтому, когда вы говорите color1 instanceof String;, это вернет true, потому что color1 - это строка. - person JonH; 15.03.2010
comment
@Alon - он проверяет объект, чтобы узнать, что это за объект. Рассмотрим объект человека / клиента. Итак, person p = new person() p теперь является типом человека, а не строковым типом. - person JonH; 15.03.2010
comment
В JavaScript может быть сложно понять, что переменная может иметь разные типы на протяжении всего срока службы. Пример кода: jsfiddle.net/sikusikucom/znSPv - person moey; 18.10.2012
comment
@ Siku-Siku.Com - Я не вижу в этом никакого подвоха var zero = 0; alert(zero); zero = "0"; alert(zero) мы без проблем перешли от примитивного int к примитивному string. - person JonH; 18.10.2012
comment
О, я имел в виду, что было сложно / ново понять (а не делать) такую ​​гибкость в JavaScript, особенно. для тех, кто привык к языку, требующему, например, явное приведение типов для изменения типа переменной. Как вы отметили, очень легко изменить тип, например, от примитивного int к примитивному string. - person moey; 19.10.2012
comment
Когда я кодирую [a, b] instanceof Array, почему он возвращает true как то же самое, что и new Array (a, b) instanceof Array, в то время как instanceof String возвращает false не так же, как new String (ab) instanceof String? - person Amitābha; 07.08.2013
comment
@ user2245634, потому что a буквальное значение. Взгляните сюда: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ - person JonH; 08.08.2013
comment
@JonH Ссылаясь на хорошие примеры, почему Object.prototype instanceof Object возвращает false? OP на этом сайте не объясняет, почему. - person Harish; 22.05.2015
comment
@JonH: Неважно, я нашел ответ здесь. Извините за неприятности. - person Harish; 22.05.2015
comment
Цитата ~ var color2 = coral; // тип не указан Да, тип указан - он интерпретируется как примитив string. JavaScript не обеспечивает вывод типа для примитивов. Следовательно, instanceof работает только с явно созданными объектами. Пример var color2 = "coral" color2 instanceof String "false" typeof color2 "string" - person Edward J Beckett; 20.07.2018

У instanceof есть важный аспект, который, похоже, пока не рассматривается ни в одном из комментариев: наследование. Переменная, оцениваемая с помощью instanceof, может возвращать истину для нескольких «типов» из-за прототипного наследования.

Например, давайте определим тип и подтип:

function Foo(){ //a Foo constructor
    //assign some props
    return this;
}

function SubFoo(){ //a SubFoo constructor
    Foo.call( this ); //inherit static props
    //assign some new props
    return this;
}

SubFoo.prototype = Object.create(Foo.prototype); // Inherit prototype
SubFoo.prototype.constructor = SubFoo;

Теперь, когда у нас есть пара «классов», давайте создадим несколько экземпляров и выясним, какие они экземпляры:

var 
    foo = new Foo()
,   subfoo = new SubFoo()
;

alert( 
    "Q: Is foo an instance of Foo? "
+   "A: " + ( foo instanceof Foo ) 
); // -> true

alert( 
    "Q: Is foo an instance of SubFoo? " 
+   "A: " + ( foo instanceof SubFoo ) 
); // -> false

alert( 
    "Q: Is subfoo an instance of Foo? "
+   "A: " + ( subfoo instanceof Foo ) 
); // -> true

alert( 
    "Q: Is subfoo an instance of SubFoo? "
+   "A: " + ( subfoo instanceof SubFoo ) 
); // -> true

alert( 
    "Q: Is subfoo an instance of Object? "
+   "A: " + ( subfoo instanceof Object ) 
); // -> true

Видите последнюю строчку? Все «новые» вызовы функции возвращают объект, унаследованный от Object. Это справедливо даже при использовании сокращения создания объекта:

alert( 
    "Q: Is {} an instance of Object? "
+   "A: " + ( {} instanceof Object ) 
); // -> true

А как насчет самих определений "классов"? Что они представляют собой экземпляры?

alert( 
    "Q: Is Foo an instance of Object? "
+   "A:" + ( Foo instanceof Object) 
); // -> true

alert( 
    "Q: Is Foo an instance of Function? "
+   "A:" + ( Foo instanceof Function) 
); // -> true

Я считаю, что понимание того, что любой объект может быть экземпляром НЕСКОЛЬКИХ типов, важно, поскольку вы (ошибочно) предполагаете, что вы можете различать, скажем, объект и функцию, используя instanceof. Как ясно видно из этого последнего примера, функция является объектом.

Это также важно, если вы используете какие-либо шаблоны наследования и хотите подтвердить потомство объекта другими методами, кроме утиного ввода.

Надеюсь, это поможет любому, кто исследует instanceof.

person webnesto    schedule 30.06.2011
comment
Еще более удивительно то, что после наследования прототипа с помощью SubFoo.prototype = new Foo(); вы можете добавить к нему дополнительные методы, и проверка subfoo instanceof Foo все равно пройдет, как и subfoo instanceof SubFoo - person Cory Danielson; 08.02.2014

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

У каждого объекта в JavaScript есть прототип, доступный через свойство __proto__. Функции также имеют свойство prototype, которое является начальным __proto__ для любых созданных ими объектов. Когда функция создается, ей присваивается уникальный объект для prototype. Оператор instanceof использует эту уникальность, чтобы дать вам ответ. Вот как instanceof мог бы выглядеть, если бы вы написали его как функцию.

function instance_of(V, F) {
  var O = F.prototype;
  V = V.__proto__;
  while (true) {
    if (V === null)
      return false;
    if (O === V)
      return true;
    V = V.__proto__;
  }
}

Это в основном перефразирование ECMA-262 версии 5.1 (также известного как ES5), раздел 15.3.5.3.

Обратите внимание, что вы можете переназначить любой объект свойству prototype функции, и вы можете переназначить свойство __proto__ объекта после его создания. Это даст вам интересные результаты:

function F() { }
function G() { }
var p = {};
F.prototype = p;
G.prototype = p;
var f = new F();
var g = new G();

f instanceof F;   // returns true
f instanceof G;   // returns true
g instanceof F;   // returns true
g instanceof G;   // returns true

F.prototype = {};
f instanceof F;   // returns false
g.__proto__ = {};
g instanceof G;   // returns false
person Jay Conrod    schedule 09.02.2012
comment
Стоит отметить, что прямое изменение свойства __proto__ в IE запрещено. Если я правильно помню, прямое манипулирование свойством также не включено в спецификацию ECMA, поэтому, вероятно, использовать его для других целей, кроме как в академических целях, - плохая идея. - person webnesto; 20.06.2012
comment
@webnesto, это правда, proto нет в спецификации. Я не знал, что IE не поддерживает это. Когда вы говорите «прямая манипуляция», вы имеете в виду, что он вообще не подвергается воздействию JS-кода или просто не доступен для записи? - person Jay Conrod; 20.06.2012
comment
Не уверен на 100% на старых версиях. Похоже, отсюда (stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10), что в IE9 он, по крайней мере, читается (но не изменяемый). Также стоит отметить, что похоже, что браузеры могут полностью отказаться от него. - person webnesto; 06.07.2012
comment
Понимание подразумеваемого существования свойства proto важно независимо от того, доступно ли оно для пользовательского кода или нет. +10 Если бы я мог процитировать спецификацию, это именно то, что я сюда искал. - person Mike Edwards; 14.07.2012
comment
Чтобы получить ссылку на прототип, используйте Object.getPrototypeOf(o), он будет таким же, как __proto__, который вы описываете, но соответствует ECMAScript. - person froginvasion; 30.07.2014
comment
__proto__ фактически попал в спецификацию ECMAScript 6, как указано в MDN - person Olga; 13.01.2015

Я думаю, стоит отметить, что instanceof определяется использованием ключевого слова new при объявлении объекта. В примере от JonH;

var color1 = new String("green");
color1 instanceof String; // returns true
var color2 = "coral";
color2 instanceof String; // returns false (color2 is not a String object)

Он не упомянул следующее;

var color1 = String("green");
color1 instanceof String; // returns false

Указание «new» фактически скопировало конечное состояние функции конструктора String в переменную color1, а не просто установило для нее возвращаемое значение. Я думаю, это лучше показывает, что делает новое ключевое слово;

function Test(name){
    this.test = function(){
        return 'This will only work through the "new" keyword.';
    }
    return name;
}

var test = new Test('test');
test.test(); // returns 'This will only work through the "new" keyword.'
test // returns the instance object of the Test() function.

var test = Test('test');
test.test(); // throws TypeError: Object #<Test> has no method 'test'
test // returns 'test'

Использование «new» присваивает значение «this» внутри функции объявленной переменной, в то время как неиспользование этого значения вместо этого присваивает возвращаемое значение.

person Stephen Belanger    schedule 10.04.2011
comment
Нет смысла использовать new с какими-либо типами JavaScript, что делает принятый ответ более запутанным для новичков. text = String('test') и options = {} не будут тестироваться instanceof, а, скорее, typeof. - person rxgx; 19.08.2011
comment
Date.getTime () // сбой. да, новое важно. - person Stephen Belanger; 27.08.2011
comment
Попробуйте запустить это в консоли. Вы получите сообщение об ошибке, потому что getTime () не существует. Вам НЕОБХОДИМО использовать новый. - person Stephen Belanger; 20.12.2011

И вы можете использовать его для обработки ошибок и отладки, например:

try{
    somefunction();
} 
catch(error){
    if (error instanceof TypeError) {
        // Handle type Error
    } else if (error instanceof ReferenceError) {
        // Handle ReferenceError
    } else {
        // Handle all other error types
    }
}
person Tarek Saied    schedule 22.05.2011

Что это?

Javascript - это прототипный язык, что означает, что он использует прототипы для «наследования». оператор instanceof проверяет, присутствует ли свойство конструктора prototype в цепочке __proto__ объекта. Это означает, что он будет делать следующее (при условии, что testObj является функциональным объектом):

obj instanceof testObj;
  1. Проверьте, равен ли прототип объекта прототипу конструктора: obj.__proto__ === testObj.prototype >> если это true instanceof вернет true.
  2. Поднимется по цепочке прототипов. Например: obj.__proto__.__proto__ === testObj.prototype >>, если это true, instanceof вернет true.
  3. Будет повторять шаг 2 до тех пор, пока не будет исследован полный прототип объекта. Если нигде в цепочке прототипов объекта не соответствует testObj.prototype, тогда оператор instanceof вернет false.

Пример:

function Person(name) {
  this.name = name;
}
var me = new Person('Willem');

console.log(me instanceof Person); // true
// because:  me.__proto__ === Person.prototype  // evaluates true

console.log(me instanceof Object); // true
// because:  me.__proto__.__proto__ === Object.prototype  // evaluates true

console.log(me instanceof Array);  // false
// because: Array is nowhere on the prototype chain

Какие проблемы решает?

Это решило проблему удобной проверки, является ли объект производным от определенного прототипа. Например, когда функция получает объект, который может иметь разные прототипы. Затем, прежде чем использовать методы из цепочки прототипов, мы можем использовать оператор instanceof, чтобы проверить, есть ли эти методы на объекте.

Пример:

function Person1 (name) {
  this.name = name;
}

function Person2 (name) {
  this.name = name;
}

Person1.prototype.talkP1 = function () {
  console.log('Person 1 talking');
}

Person2.prototype.talkP2 = function () {
  console.log('Person 2 talking');
}


function talk (person) {
  if (person instanceof Person1) {
    person.talkP1();
  }
  
  if (person instanceof Person2) {
    person.talkP2();
  }
  
  
}

const pers1 = new Person1 ('p1');
const pers2 = new Person2 ('p2');

talk(pers1);
talk(pers2);

Здесь в функции talk() сначала проверяется, находится ли прототип на объекте. После этого выбирается соответствующий метод для выполнения. Невыполнение этой проверки может привести к выполнению несуществующего метода и, следовательно, к ошибке ссылки.

Когда это уместно, а когда нет?

Мы как бы уже рассмотрели это. Используйте его, когда вам нужно проверить прототип объекта, прежде чем что-то с ним делать.

person Willem van der Veen    schedule 05.09.2018
comment
Я думаю, вы хотели написать что-то еще, кроме прототип функции конструктора появляется где-то в свойстве prototype конструктора - person Bergi; 05.09.2018
comment
Возможно, вы захотите упомянуть, что было бы более подходящим, чтобы люди совместно использовали интерфейс и называли оба этих метода PersonX.prototype.talk, чтобы функция talk могла просто выполнять person.talk(). - person Bergi; 05.09.2018
comment
Вы были совершенно правы, обновили его, так что определение лучше. Спасибо, что указали! - person Willem van der Veen; 05.09.2018
comment
Кроме того, не используйте __proto__ в документации, это не рекомендуется - вместо этого напишите Object.getPrototype() - person Bergi; 05.09.2018

На вопрос «Когда это уместно, а когда нет?» Мои 2 цента:

instanceof редко используется в производственном коде, но полезен в тестах, где вы хотите убедиться, что ваш код возвращает / создает объекты правильных типов. Если четко указать типы объектов, которые ваш код возвращает / создает, ваши тесты становятся более мощным инструментом для понимания и документирования вашего кода.

person Andrew Magee    schedule 24.07.2013

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

Если вы используете события jQuery, иногда вам нужно написать более общую функцию, которая также может вызываться напрямую (без события). Вы можете использовать instanceof, чтобы проверить, является ли первый параметр вашей функции экземпляром jQuery.Event, и отреагировать соответствующим образом.

var myFunction = function (el) {                
    if (el instanceof $.Event) 
        // event specific code
    else
        // generic code
};

$('button').click(recalc);    // Will execute event specific code
recalc('myParameter');  // Will execute generic code

В моем случае функции нужно было что-то вычислить либо для всех (через событие щелчка на кнопке), либо только для одного конкретного элемента. Код, который я использовал:

var recalc = function (el) { 
    el = (el == undefined || el instanceof $.Event) ? $('span.allItems') : $(el);
    // calculate...
};
person jussty    schedule 26.07.2013

instanceof - это просто синтаксический сахар для isPrototypeOf:

function Ctor() {}
var o = new Ctor();

o instanceof Ctor; // true
Ctor.prototype.isPrototypeOf(o); // true

o instanceof Ctor === Ctor.prototype.isPrototypeOf(o); // equivalent

instanceof просто зависит от прототипа конструктора объекта.

Конструктор - это обычная функция. Строго говоря, это функциональный объект, поскольку в Javascript все является объектом. И у этого функционального объекта есть прототип, потому что у каждой функции есть прототип.

Прототип - это обычный объект, который находится в цепочке прототипов другого объекта. Это означает, что нахождение в цепочке прототипов другого объекта превращает объект в прототип:

function f() {} //  ordinary function
var o = {}, // ordinary object
 p;

f.prototype = o; // oops, o is a prototype now
p = new f(); // oops, f is a constructor now

o.isPrototypeOf(p); // true
p instanceof f; // true

Следует избегать оператора instanceof, поскольку он подделывает классы, которых нет в Javascript. Несмотря на то, что ключевого слова class нет и в ES2015, поскольку class снова просто синтаксический сахар для ... но это уже другая история.

person Community    schedule 26.07.2016
comment
Самая первая строка неверна! Дополнительную информацию см. здесь: isPrototypeOf () отличается от оператора instanceof. В экземпляре объекта выражения AFunction цепочка прототипов объекта проверяется по AFunction.prototype, а не по самой AFunction. - person Yan Foto; 09.12.2016
comment
@Yan И все же instanceof происходит от isPrototypeOf. Я называю это синтаксическим сахаром. А ваш исходник на MDN - шутка, не так ли? - person ; 11.12.2016
comment
Нет. Это не шутка и не должна быть шуткой, но по-прежнему неправильная ссылка. Я должен был иметь это один опубликовал. Я не хочу вдаваться в технические подробности, но instanceof не выполняет то же самое, что isPrototypeOf. Вот и все. - person Yan Foto; 12.12.2016

@SebastianSimon, мой последний ответ - 8 лет (когда я хромал), и, возможно, я написал какой-то бычий хит :)

Вкратце - в настоящее время единственные случаи, когда я использую instanceof, - это когда я использую class экземпляры, и поведение зависит от класса, который я получу, например. Я хочу различать, является ли 404 ErrorA (ресурс не существует) или ErrorB (служба не найдена) - коды ответов библиотеки сбивали с толку, но, к моему счастью, он был выдан с использованием разных классов ошибок.

Определенно (в настоящее время) я НЕ буду использовать его для проверки типов, отражающих примитивы - вы не можете быть уверены, возвращает ли библиотека 'msg' или new String('msg').

У обоих есть методы, принадлежащие классу String, потому что примитив 'msg' внутренне заключен в строковый объект. Внутренне означает переводчиком. Они оба Strings, но оператора instanceof здесь явно недостаточно - чтобы проверить, есть ли sth. является примитивом или классом, я бы использовал сочетание typeof && instanceof - но только для того, что возвращается из внешней библиотеки JS.

Currenlty TypeScript решает эту проблему, и вам больше не нужно использовать такие служебные проверки с typeof и instanceof.

person fider    schedule 17.03.2021