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


313

instanceofКлючове слово в JavaScript може бути досить заплутаним , коли він вперше зустрічається, так як люди схильні думати , що JavaScript не є об'єктно-орієнтована мова програмування.

  • Що це?
  • Які проблеми вона вирішує?
  • Коли це доречно і коли ні?

3
Хоча наведені нижче відповіді дуже корисні, ви не використовуєте їх багато (принаймні, я не) в реальних програмах для слів. Можна створити властивість object.type, яка містить рядок і перевіряє його.
Омар Аль-Ітаві

4
JS не має жодного сенсу: "foo" instanceof String=> false, 1 instanceof Number=> false, {} instanceof Object=> false. Скажи що?!
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: Це був коментар до мовної специфікації, походить від рубіста.
morbusg

@morbusg - ({}) instanceof Objectповернеться true. Насправді код, який ви написали, призведе до помилки.
DDM

Відповіді:


279

instanceof

Операнд з лівої сторони (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оцінка правдивою, якщо об'єкт успадковується від прототипу класу:

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. Ми розібрали його і перевірили, чи він типу, Customerчи ні. Звичайно, це повертає істину. Потім, використовуючи Customerоб'єкт, ми перевіряємо, чи він є String. Ні, безумовно, Stringми не новим Customerоб'єктом, а не Stringоб'єктом. У цьому випадку він повертається помилковим.

Це дійсно так просто!


@Alon - я додав для тебе приклад. Дивіться вище, color1 є рядком типу, тож коли ви скажете, color1 instanceof String;це повернеться правдою, оскільки color1 - це рядок.
JonH

@Alon - Він перевіряє об'єкт, щоб побачити, який це тип об'єкта. Розгляньте об’єкт людини / замовника. Отже, person p = new person()р тепер тип людини, а не тип рядка.
JonH

У JavaScript може бути складно зрозуміти, що змінна має гнучкість мати різний тип протягом усієї її життєвої тривалості. Приклад коду: jsfiddle.net/sikusikucom/znSPv
Moey

@ Siku-Siku.Com - Я не бачу жодної хитрості до цього, var zero = 0; alert(zero); zero = "0"; alert(zero)ми перейшли від примітиву intдо примітиву stringбез жодних питань.
JonH

О, я мав на увазі, що це "хитро / нове розуміння (не робити )" гнучкості "в JavaScript, особливо. для тих, хто звик до мови, яка вимагає, наприклад, явного кастингу, щоб змінити тип змінної. Як ви зазначали, змінити тип з примітивного intна примітивний дуже просто string.
moey

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

Я вважаю, що розуміння того, що будь-який об'єкт може бути екземпляром МНОГО типів, є важливим, оскільки ви мої (неправильно) припускаєте, що ви могли розмежувати, скажімо, об'єкт і функцію за допомогою instanceof. Як показує останній приклад, функція є об'єктом.

Це також важливо, якщо ви використовуєте будь-які шаблони успадкування і хочете підтвердити потомство об'єкта іншими методами, ніж типізація качок.

Сподіваюся, що це допомагає кожному, хто досліджує instanceof.


Ще дивовижніше те, що після того, як ви успадкуєте від прототипу, SubFoo.prototype = new Foo();ви можете додати до нього більше методів, і subfoo instanceof Fooчек все одно пройде так само, якsubfoo instanceof SubFoo
Cori 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 він є принаймні читабельним (хоча й не мінливим). Також слід зазначити, що це здається, що веб-переглядачі можуть повністю скинути його.
webnesto

4
Розуміння наявного існування прото- властивості важливо, доступний він для коду користувача чи ні. +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

Вказавши "нове", фактично скопійовано кінцевий стан функції конструктора String у color1 var, а не просто встановивши його на повернене значення. Я думаю, що це краще показує, що робить нове ключове слово;

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" призначає заявленому var значення "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);

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
Я думаю, ти хотів написати щось інше, ніж " прототип функції конструктора з'являється десь у властивості прототипу конструктора "
Бергі,

Ви можете сказати, що було б доцільніше, щоб люди мали спільний інтерфейс та називали обидва ці методи PersonX.prototype.talk, щоб talkфункція могла просто виконувати person.talk().
Бергі

Ви абсолютно правильно оновили його, тож визначення краще. Дякуємо, що вказали!
Віллем ван дер Веен

Крім того, не використовуйте __proto__в документації, воно застаріле - пишіть Object.getPrototype()замість цього
Бергі

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. У виразі" object 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...
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.