Чому 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.