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

313

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

  • Что это?
  • Какие проблемы это решает?
  • Когда это уместно, а когда нет?
Алон Губкин
источник
3
Хотя приведенные ниже ответы очень полезны, вы не используете их (по крайней мере, я) в реальных текстовых приложениях. Можно создать свойство object.type, которое содержит строку и проверяет ее.
Омар Аль-Итави,
4
JS не имеет никакого смысла: "foo" instanceof String=> ложь, 1 instanceof Number=> ложь, {} instanceof Object=> ложь. Чего-чего?!
morbusg
12
@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в узле и современных браузерах
Fider
1
@fider: Это был комментарий к языковой спецификации, пришедший от rubyist.
Morbusg
@morbusg - ({}) instanceof Objectвернется true. На самом деле код, который вы написали, выдаст вам ошибку.
DDM

Ответы:

279

случай

Операнд левой стороны (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, если объект наследуется от прототипа classe:

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

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

По запросу ОП

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

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

Например:

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. Мы new'd его и проверили, имеет ли он тип Customerили нет. Конечно, это возвращает истину. Затем, продолжая использовать Customerобъект, мы проверяем, является ли он String. Нет, определенно не Stringмы обновили Customerобъект, а не Stringобъект. В этом случае возвращается false.

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

JONH
источник
@Alon - я добавил для тебя пример. Смотрите выше, color1 имеет тип string, поэтому, когда вы говорите, color1 instanceof String;это вернет true, потому что color1 является строкой.
Джон
@Alon - проверяет объект на предмет типа объекта. Рассмотрим объект человек / клиент. Таким образом, person p = new person()р теперь тип человека, а не тип строки.
Джон
В JavaScript может быть сложно понять, что переменная обладает гибкостью, чтобы иметь другой тип на протяжении всей ее жизни. Пример кода: jsfiddle.net/sikusikucom/znSPv
моё
@ Siku-Siku.Com - я не вижу никакого трюка, var zero = 0; alert(zero); zero = "0"; alert(zero)мы прошли путь от примитива intк примитиву stringбез каких-либо проблем.
Джон
О, я имел в виду, что было сложно / плохо понять, что такое «гибкость» в JavaScript, особенно. для тех, кто привык к языку, который требует, например, явного приведения для изменения типа переменной. Как вы указали, очень легко изменить тип, например, с примитива intна примитив string.
Мой
95

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

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

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

Я чувствую, что понимание того, что любой объект может быть экземпляром MULTIPLE-типов, важно, поскольку вы, я (ошибочно), допускаете, что вы можете различать, скажем, объект и функцию с помощью instanceof. Как видно из последнего примера, функция является объектом.

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

Надеюсь, что это помогает любому исследовать instanceof.

webnesto
источник
Еще более удивительным является то, что после того, как вы унаследуете от прототипа, SubFoo.prototype = new Foo();вы можете добавить к нему больше методов, и subfoo instanceof Fooпроверка все равно пройдет, а такжеsubfoo instanceof SubFoo
Cory Danielson
87

Другие ответы здесь верны, но они не касаются того, как на 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
Джей Конрод
источник
5
Стоит отметить, что прямое манипулирование __proto__свойством " " не допускается в IE. Если я правильно помню, прямое манипулирование свойством также не включено в спецификацию ECMA, так что, вероятно, это плохая идея использовать его для задания, отличного от академического.
webnesto
@ webnesto, это правда, прото не в спецификации. Я не знал, что IE не поддерживает это все же. Когда вы говорите о прямых манипуляциях, вы имеете в виду, что он вообще не подвергается JS-коду или просто не доступен для записи?
Джей Конрод
2
Не уверен на 100% в старых версиях. Похоже, отсюда ( stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10 ), что в IE9 он по крайней мере читабелен (хотя и не изменяем). Также следует отметить, что это звучит так, как будто браузеры могут полностью его удалить.
Веблесто
4
Понимание подразумеваемого существования свойства proto важно независимо от того, доступно оно для пользовательского кода или нет. +10, если бы я мог сослаться на спецификации, это именно то, что я искал здесь.
Майк Эдвардс
3
Чтобы получить ссылку на прототип, используйте Object.getPrototypeOf(o), это будет то же самое, что __proto__вы описываете, но соответствует ECMAScript.
froginvasion
45

Я думаю, что стоит отметить, что 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» внутри функции объявленной переменной, в то время как его использование вместо этого присваивает возвращаемое значение.

Стивен Белэнджер
источник
2
Не имеет смысла использовать newлюбой из типов JavaScript, что делает принятый ответ более запутанным для начинающих.text = String('test')и options = {}не собираюсь проверяться, instanceofа, скорее, с typeofвместо этого.
Райан
3
Date.getTime () // ошибка. да, новое важно.
Стивен Белэндж
1
Попробуйте запустить это в консоли. Вы получите ошибку, потому что getTime () не существует. Вы должны использовать новый.
Стивен Белэнджер
8

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

try{
    somefunction();
} 
catch(error){
    if (error instanceof TypeError) {
        // Handle type Error
    } else if (error instanceof ReferenceError) {
        // Handle ReferenceError
    } else {
        // Handle all other error types
    }
}
Тарек Сайед
источник
3
//Vehicle is a function. But by naming conventions
//(first letter is uppercase), it is also an object
//constructor function ("class").
function Vehicle(numWheels) {
    this.numWheels = numWheels;
}

//We can create new instances and check their types.
myRoadster = new Vehicle(4);
alert(myRoadster instanceof Vehicle);
yfeldblum
источник
3

Что это?

Javascript - это язык прототипов, что означает, что он использует прототипы для «наследования». instanceofоператор проверяет , если функция конструктора prototypepropertype присутствует в __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() функции сначала проверяется, находится ли прототип на объекте. После этого выбирается соответствующий метод для выполнения. Невыполнение этой проверки может привести к выполнению метода, который не существует, и, следовательно, к ошибке ссылки.

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

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

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

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

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

Эндрю Маги
источник
1

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оно опять-таки просто синтаксический сахар для ... но это уже другая история.


источник
1
Самая первая строка неверна! См. Здесь для получения дополнительной информации: «isPrototypeOf () отличается от оператора instanceof. В выражении« instanceof объекта AFunction »цепочка прототипов объекта проверяется на AFunction.prototype, а не на сам AFunction».
Ян Фото
1
@Yan И все еще instanceofпроисходит от isPrototypeOf. Я называю это синтаксическим сахаром. А ваш источник MDN - это шутка?
Нет, это не было и не должно было быть шуткой, но все же неправильная ссылка. Я имел в виду , чтобы этот отвечал. Я не хочу вдаваться в технические подробности, но instanceof не делаю то же самое, что и isPrototypeOf. Вот и все.
Ян Фото
0

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

Если вы используете события 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...
};
jussty
источник