Чому instanceof повертає false для деяких літералів?


284
"foo" instanceof String //=> false
"foo" instanceof Object //=> false
true instanceof Boolean //=> false
true instanceof Object //=> false
false instanceof Boolean //=> false
false instanceof Object //=> false

// the tests against Object really don't make sense

Літерали масиву та літератури об'єкта відповідають ...

[0,1] instanceof Array //=> true
{0:1} instanceof Object //=> true

Чому не всі? Або, чому не всі вони НЕ ?
І що ж вони тоді є екземпляром?

Те саме в FF3, IE7, Opera та Chrome. Так, принаймні, це послідовно.


Пропущено декілька.

12.21 instanceof Number //=> false
/foo/ instanceof RegExp //=> true

Відповіді:


424

Примітиви - це тип іншого типу, ніж об'єкти, створені зсередини Javascript. З документів Mozilla API :

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

Я не можу знайти спосіб побудувати примітивні типи з кодом, можливо, це неможливо. Це, мабуть, тому люди використовують typeof "foo" === "string"замість цьогоinstanceof .

Найпростіший спосіб запам'ятати подібні речі - це запитати себе: "Цікаво, що було б здоровим і легким для вивчення"? Якою б не була відповідь, Javascript робить інше.


5
Кожен день з новою причиною ненавидіти JavaScript - вдалий день. Я знаю, що це вже давно, але я вдячний вам за цю посаду.
— toniedzwiedz

57
Ваша термінологія неправильна. Слово "буквальне" позначає синтаксис для створення даних без використання конструктора. Він не посилається на отримані дані. Буквальний синтаксис можна використовувати для створення як об'єктів, так і не об’єктів. Правильний термін - «примітиви», які посилаються на не об’єктні дані. Деякі дані мають як примітивні, так і об’єктні подання. Рядок - це один із таких типів даних.
— сірий стан настає

14
FYI, ви можете створювати примітиви без буквального синтаксису. (new String()).valueOf();
— сірий стан приходить

11
Зверніть увагу, що typeof foo === 'string'недостатньо: див. Відповідь axkibe.
— Брайан Ларсен

1
Додатково, typeof new String('')повернення"object"
— трансанг

105

Я використовую:

function isString(s) {
    return typeof(s) === 'string' || s instanceof String;
}

Тому що в JavaScript рядки можуть бути буквами або об'єктами.


28
Я знайшов щось коротке btw. function isString(s) { return s.constructor === String; }Працює для літералів і рядкових об’єктів (принаймні в V8)
— axkibe

7
Треба любити JavaScript.
— Дерек 朕 會 功夫

2
Я використовую jQuery.type (s) === 'string' ( api.jquery.com/jquery.type ), jQuery.isArray (), jQuery.isFunction (), jQuery.isNumeric (), коли це можливо.
— Іван Самигін

1
@axkibe в той час як ви маєте рацію, це не так продуктивним як typeof.
— Qix - МОНІКА ПОМИЛИЛА

Ви можете використовувати typeof "?" == String.name.toLowerCase () [але чому це [] instanceof Array?]
— QuentinUK

62

У JavaScript все є об'єктом (або, принаймні, може трактуватися як об’єкт), за винятком примітивів (булеві значення, null, числа, рядки та значення undefined(та символ у ES6)):

console.log(typeof true);           // boolean
console.log(typeof 0);              // number
console.log(typeof "");             // string
console.log(typeof undefined);      // undefined
console.log(typeof null);           // object
console.log(typeof []);             // object
console.log(typeof {});             // object
console.log(typeof function () {}); // function

Як ви бачите об'єкти, масиви та значення null- всі вважаються об'єктами ( nullце посилання на об'єкт, який не існує). Функції розрізняють тим, що вони є особливим типом об'єктів, що дзвоняться . Однак вони все ще є об'єктами.

З іншого боку, літерали true, 0, ""і undefinedне заперечує. Вони є примітивними значеннями в JavaScript. Однак булеві числа, числа та рядки також мають конструктори Boolean, Numberі Stringвідповідно, які обертають свої відповідні примітиви, щоб забезпечити додаткову функціональність:

console.log(typeof new Boolean(true)); // object
console.log(typeof new Number(0));     // object
console.log(typeof new String(""));    // object

Як ви бачите, коли примітивні значення обернуті всередині Boolean, Numberа Stringконструктори відповідно стають об'єктами. instanceofОператор працює тільки для об'єктів (саме тому він повертається falseдля примітивних значень):

console.log(true instanceof Boolean);              // false
console.log(0 instanceof Number);                  // false
console.log("" instanceof String);                 // false
console.log(new Boolean(true) instanceof Boolean); // true
console.log(new Number(0) instanceof Number);      // true
console.log(new String("") instanceof String);     // true

Як ви бачите і те, typeofі інше instanceofнедостатньо для перевірки того, чи є значення булевим, числом або рядком - typeofпрацює лише для примітивних булів, чисел і рядків; іinstanceof не працює для примітивних булів, чисел і рядків.

На щастя, існує просте рішення цієї проблеми. Реалізація за замовчуванням toString(тобто, як це визначено в нашому випадку Object.prototype.toString) повертає внутрішню [[Class]]властивість як примітивних значень, так і об'єктів:

function classOf(value) {
    return Object.prototype.toString.call(value);
}

console.log(classOf(true));              // [object Boolean]
console.log(classOf(0));                 // [object Number]
console.log(classOf(""));                // [object String]
console.log(classOf(new Boolean(true))); // [object Boolean]
console.log(classOf(new Number(0)));     // [object Number]
console.log(classOf(new String("")));    // [object String]

Внутрішня [[Class]]властивість значення набагато корисніше typeofзначення. Ми можемо використовувати Object.prototype.toStringдля створення власної (більш корисної) версії typeofоператора наступним чином:

function typeOf(value) {
    return Object.prototype.toString.call(value).slice(8, -1);
}

console.log(typeOf(true));              // Boolean
console.log(typeOf(0));                 // Number
console.log(typeOf(""));                // String
console.log(typeOf(new Boolean(true))); // Boolean
console.log(typeOf(new Number(0)));     // Number
console.log(typeOf(new String("")));    // String

Сподіваюся, ця стаття допомогла. Щоб дізнатися більше про відмінності примітивів та загорнутих об'єктів, прочитайте наступну публікацію в блозі: Таємне життя примітивів JavaScript


6
+1, хоча nullце і примітивне значення (лише typeofоператор заплутано)
— Бергі,

33

Ви можете використовувати властивість конструктора:

'foo'.constructor == String // returns true
true.constructor == Boolean // returns true

18
Зауважте, що при тестуванні змінних ця методика може вийти з ладу за певних обставин. Існує неявна посилання на поточне вікно перед Stringі Booleanу наведеному вище прикладі, тому, якщо ви тестуєте constructorвластивість змінної рядка, створеної в іншому вікні (наприклад, спливаюче вікно або кадр), це не буде просто String, воно буде бути рівним thatOtherWindowsName.String.
— Майкл Метюз

І хіба екземпляр не справляється з цим і не повертає відповідний бульний результат?
— Кріс Ное

5
це не вдасться, якщо ви перейшли нащадка Струни.
— Брайан Ларсен

1
@MichaelMathews: Це працює на засіб , яке:Object.prototype.toString.call('foo') === '[object String]'
— rvighne

@BryanLarsen та @MichaelMathews Чи є проблеми у використанні d.constructor == String ? Наприклад, з оператором вільної рівності.
— dotnetCarpenter

7
 typeof(text) === 'string' || text instanceof String; 

ви можете використовувати це, він буде працювати в обох випадках як

  1. var text="foo"; // typeof буде працювати

  2. String text= new String("foo"); // instanceof буде працювати


3

Це визначено у розділі 7.3.19 специфікації ECMAScript :If Type(O) is not Object, return false.

Іншими словами, якщо Objв Obj instanceof Callableне є об'єктом, то instanceofбуде коротке замикання на falseбезпосередньо.


1

Я вважаю, що я придумав життєздатне рішення:

Object.getPrototypeOf('test') === String.prototype    //true
Object.getPrototypeOf(1) === String.prototype         //false

-1

https://www.npmjs.com/package/typeof

Повертає рядкове представлення instanceof(назва конструкторів)

function instanceOf(object) {
  var type = typeof object

  if (type === 'undefined') {
    return 'undefined'
  }

  if (object) {
    type = object.constructor.name
  } else if (type === 'object') {
    type = Object.prototype.toString.call(object).slice(8, -1)
  }

  return type.toLowerCase()
}

instanceOf(false)                  // "boolean"
instanceOf(new Promise(() => {}))  // "promise"
instanceOf(null)                   // "null"
instanceOf(undefined)              // "undefined"
instanceOf(1)                      // "number"
instanceOf(() => {})               // "function"
instanceOf([])                     // "array"

-2

Для мене плутанина, викликана

"str".__proto__ // #1
=> String

Тому "str" istanceof Stringслід повернутися, trueтому що як працюєфайл, як показано нижче:

"str".__proto__ == String.prototype // #2
=> true

Результати виразів №1 і №2 суперечать один одному, тому один з них повинен бути неправильним.

№1 - це неправильно

Я розумію, що це викликане __proto__властивістю нестандартне, тому використовуйте стандартне:Object.getPrototypeOf

Object.getPrototypeOf("str") // #3
=> TypeError: Object.getPrototypeOf called on non-object

Тепер між виразом №2 та №3 немає ніякої плутанини


2
№1 є правильним, але це пов'язано з аксесуаром властивості , який передає первісне значення відповідному типу об'єкта, подібному до Object("str").__proto__або Object("str") instanceof String.
— Джонатан Лоновський

@JonathanLonowski дякую, що вказав на це. Я цього не знав
— мко

-8

Або ви можете просто зробити власну функцію так:

function isInstanceOf(obj, clazz){
  return (obj instanceof eval("("+clazz+")")) || (typeof obj == clazz.toLowerCase());
};

використання:

isInstanceOf('','String');
isInstanceOf(new String(), 'String');

Вони повинні повернути істину.


14
Я бачу овал. Зло.
— Aaria Carter-Weir
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.