Dlaczego instanceof zwraca false dla niektórych literałów?

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

Literały tablicowe i literalne obiektowe pasują ...

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

Dlaczego nie wszyscy? Albo, dlaczego nie wszyscy oni nie ?
A zatem, co to za przykład?

Tak samo jest w FF3, IE7, Opera i Chrome. Więc przynajmniej jest spójny.


Brakowało kilku.

12.21 instanceof Number //=> false
/foo/ instanceof RegExp //=> true
Jonathan Lonowski
źródło

Odpowiedzi:

424

Prymitywy są innego rodzaju niż obiekty tworzone z poziomu Javascript. Z dokumentacji API Mozilla :

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

Nie mogę znaleźć żadnego sposobu na zbudowanie prymitywnych typów za pomocą kodu, być może nie jest to możliwe. Prawdopodobnie dlatego ludzie używają typeof "foo" === "string"zamiast instanceof.

Łatwym sposobem na zapamiętanie takich rzeczy jest zadawanie sobie pytania „Zastanawiam się, co byłoby rozsądne i łatwe do nauczenia się”? Jakakolwiek jest odpowiedź, Javascript robi to samo.

John Millikin
źródło
5
Codziennie z nowym powodem, dla którego nienawidzę JavaScript, to dobry dzień. Wiem, że to już dawno spóźnione, ale dziękuję za ten post.
toniedzwiedz
57
Twoja terminologia jest nieprawidłowa. Słowo „literał” odnosi się do składni do tworzenia danych bez użycia konstruktora. Nie odnosi się do danych wynikowych. Dosłowną składnię można wykorzystać do tworzenia zarówno obiektów, jak i nie-obiektów. Prawidłowy termin to „prymitywy”, które odnoszą się do danych niebędących obiektami. Niektóre dane mają zarówno pierwotne, jak i obiektowe reprezentacje. Łańcuch jest jednym z tych typów danych.
stan szary nadchodzi
14
Do twojej wiadomości, możesz tworzyć prymitywy bez dosłownej składni. (new String()).valueOf();
stan szary nadchodzi
11
Zauważ, że typeof foo === 'string'to za mało: patrz odpowiedź axkibe.
Bryan Larsen
1
Dodatkowo typeof new String('')zwroty"object"
transang
105

Używam:

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

Ponieważ w JavaScript ciągi znaków mogą być literałami lub obiektami.

axkibe
źródło
28
Znalazłem coś krótkiego. function isString(s) { return s.constructor === String; }Działa z literałami i obiektami łańcuchowymi (przynajmniej w V8)
axkibe
7
Uwielbiam JavaScript.
Derek 朕 會 功夫
2
Używam jQuery.type (s) === 'string' ( api.jquery.com/jquery.type ), jQuery.isArray (), jQuery.isFunction (), jQuery.isNumeric (), gdy jest to możliwe.
Ivan Samygin
1
@axkibe, dopóki masz rację, nie jest tak wydajna jak typeof.
Qix - MONICA MISTREATED
Możesz użyć typeof „?” == String.name.toLowerCase () [ale dlaczego [] instanceof Array?]
QuentinUK
62

W JavaScript wszystko jest obiektem (lub przynajmniej może być traktowane jako obiekt), z wyjątkiem prymitywów (booleany, null, liczby, łańcuchy i wartość undefined(i symbol w 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

Jak widać obiekty, tablice i wartość nullsą uważane za obiekty ( nulljest odniesieniem do obiektu, który nie istnieje). Funkcje są rozróżniane, ponieważ są specjalnym rodzajem obiektów, które można wywoływać . Jednak nadal są obiektami.

Z drugiej strony literały true, 0, ""a undefinednie obiektów. Są to prymitywne wartości w JavaScript. Jednak logicznych, liczby i ciągi mają także konstruktorów Boolean, Numbera Stringodpowiednio który owinąć swoje odpowiednie prymitywów, aby zapewnić dodatkową funkcjonalność:

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

Jak widać, gdy prymitywne wartości są zawinięte w odpowiednio Boolean, Numberi Stringkonstruktory stają się obiektami. instanceofOperator działa tylko dla obiektów (dlatego wraca falsedo wartości pierwotnych):

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

Jak widać oba typeofi instanceofsą niewystarczające do sprawdzenia, czy wartość jest wartością logiczną, liczbą lub łańcuchem - typeofdziała tylko dla prymitywnych boolanów, liczb i łańcuchów; i instanceofnie działa dla prymitywnych boolanów, liczb i łańcuchów.

Na szczęście istnieje proste rozwiązanie tego problemu. Domyślna implementacja toString(tzn. Jak jest natywnie zdefiniowana Object.prototype.toString) zwraca wewnętrzną [[Class]]właściwość zarówno pierwotnych wartości, jak i obiektów:

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]

Wewnętrzna [[Class]]właściwość wartości jest znacznie bardziej użyteczna niż typeofwartość. Możemy użyć Object.prototype.toStringdo stworzenia własnej (bardziej użytecznej) wersji typeofoperatora w następujący sposób:

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

Mam nadzieję, że ten artykuł pomógł. Aby dowiedzieć się więcej o różnicach między prymitywami a opakowanymi obiektami, przeczytaj następujący post na blogu: Sekretne życie prymitywów JavaScript

Aadit M. Shah
źródło
6
+1, chociaż nulljest to również prymitywna wartość (tylko typeofoperator jest mylący)
Bergi,
33

Możesz użyć właściwości konstruktora:

'foo'.constructor == String // returns true
true.constructor == Boolean // returns true
użytkownik 144049
źródło
18
Pamiętaj, że podczas testowania zmiennych technika ta może się nie powieść w pewnych okolicznościach. Jest ukryte odniesienie do bieżącego okna z przodu Stringi Booleanw powyższym przykładzie, więc jeśli są badania constructorwłaściwości zmiennej łańcucha utworzonego w innym oknie (jak popup lub ramy) to będzie nie być równa po prostu String, że będzie być równym thatOtherWindowsName.String.
Michael Mathews,
I czy instanceof nie zajmuje się tym i nie zwraca odpowiedniego wyniku logicznego?
Chris Noe,
5
to się nie powiedzie, jeśli przejdziesz potomek String.
Bryan Larsen
1
@MichaelMathews: Działa to w celu zaradzenia temu:Object.prototype.toString.call('foo') === '[object String]'
rvighne
@BryanLarsen i @MichaelMathews Czy jest jakiś problem podczas korzystania d.constructor == String? Np. Z luźnym operatorem równości.
dotnetCarpenter
7
 typeof(text) === 'string' || text instanceof String; 

możesz tego użyć, zadziała w obu przypadkach jako

  1. var text="foo"; // typeof będzie działać

  2. String text= new String("foo"); // instanceof będzie działać

saurabhgoyal795
źródło
3

Jest to zdefiniowane w specyfikacji ECMAScript w rozdziale 7.3.19 Krok 3 :If Type(O) is not Object, return false.

Innymi słowy, jeśli Objw Obj instanceof Callablenie jest przedmiotem, instanceofbędzie zwarcie do falsebezpośrednio.

HKTonyLee
źródło
1

Myślę, że wpadłem na realne rozwiązanie:

Object.getPrototypeOf('test') === String.prototype    //true
Object.getPrototypeOf(1) === String.prototype         //false
Robby Harris
źródło
-1

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

Zwraca ciąg znaków reprezentujący instanceof(nazwa konstruktora)

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
źródło
-2

Dla mnie zamieszanie spowodowane przez

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

Więc "str" istanceof Stringpowinien wrócić, trueponieważ jak działa istanceof, jak poniżej:

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

Wyniki ekspresji nr 1 i nr 2 sobą sprzeczne, więc jeden z nich powinien być nieprawidłowy.

# 1 jest zły

Rozumiem, że jest to spowodowane __proto__niestandardową właściwością, więc użyj standardowej:Object.getPrototypeOf

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

Teraz nie ma zamieszania między wyrażeniem nr 2 i nr 3

mko
źródło
2
# 1 jest poprawne, ale wynika to z akcesora właściwości , który ustawia pierwotną wartość na odpowiedni typ obiektu, podobny do Object("str").__proto__lub Object("str") instanceof String.
Jonathan Lonowski,
@JathanathanLonowski dziękuje za zwrócenie na to uwagi. Nie wiedziałem o tym
mko
-8

Lub możesz po prostu stworzyć własną funkcję:

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

stosowanie:

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

Oba powinny zwrócić wartość true.

coś
źródło
14
Widzę eval. Zło.
Aaria Carter-Weir