Почему 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
К вашему сведению, вы можете создавать примитивы без буквального синтаксиса. (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 строки могут быть литералами или объектами.

axkibe
источник
28
Я нашел что-то короткое между прочим. 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 все является объектом (или, по крайней мере, может рассматриваться как объект), за исключением примитивов (логические, нулевые, числа, строки и значение 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. Однако Booleans, число и строка также имеют конструктор 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
user144049
источник
18
Обратите внимание, что при тестировании переменных этот метод может не работать при определенных обстоятельствах. Существует неявная ссылка на текущее окно перед Stringи Booleanв приведенном выше примере, поэтому, если вы тестируете constructorсвойство строковой переменной, созданной в другом окне (например, всплывающее окно или фрейм), оно не будет равно просто String, оно будет быть равным thatOtherWindowsName.String.
Майкл Мэтьюз
И не возражает ли instanceof это и возвращает ли соответствующий логический результат?
Крис Ноу
5
это не удастся, если вы прошли потомок String.
Брайан Ларсен
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 будет работать

saurabhgoyal795
источник
3

Это определяется в разделе 7.3.19 спецификации ECMAScript. Шаг 3 :If Type(O) is not Object, return false.

Другими словами, если объект Objin Obj instanceof Callableне является объектом, он instanceofбудет falseнапрямую закорочен.

HKTonyLee
источник
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"
samdd
источник
-2

Для меня путаница, вызванная

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

Так "str" istanceof Stringдолжно вернуться, trueпотому что как istanceof работает, как показано ниже:

"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

Mko
источник
2
# 1 является правильным, но это из-за метода доступа к свойству , который помещает примитивное значение в соответствующий тип объекта, аналогично Object("str").__proto__или Object("str") instanceof String.
Джонатан Лоновски
@JonathanLonowski спасибо за указание на это. Я этого не знал
mko
-8

Или вы можете просто сделать свою собственную функцию следующим образом:

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

использование:

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

Оба должны вернуть истину.

STH
источник
14
Я вижу Eval. Злой.
Ария Картер-Вейр