Perfection kills

Exploring Javascript by example

`instanceof` considered harmful (or how to write a robust `isArray`)

January 10th, 2009 by kangax (Permalink)

Checking types in Javascript is well known as a pretty unreliable process.
Good old typeof operator is often useless when it comes to certain types of values:

typeof null; // "object"
typeof []; // "object"

People often expect to see something like “null” in the former check and something like “array” in the latter one.
Fortunately, checking for null is not that hard, despite useless typeof, and is usually accomplished by strict-comparing value to null:

value === null;

Checking for arrays, on the other hand, is a somewhat tricky business. There are usually two schools of thought – using instanceof operator (or checking object’s constructor property) and the-duck-typing way – checking for presence (or types) of certain set of properties (which are known to be present in array objects).

Obviously, both ways have their pros and cons.

1) `instanceof` operator / `constructor` property

instanceof operator essentially checks whether anything from left-hand object’s prototype chain is the same object as what’s referenced by prototype property of right-hand object. It sounds somewhat complicated but is easily understood from a simple example:

var arr = []; 
arr instanceof Array; // true

This statement returns `true` because Array.prototype (being a prototype property of a right-hand object) references the same object as an internal [[Prototype]] of left-hand object ([[Prototype]] is “visible” via arr.__proto__ in clients that have __proto__ extension). An alternative constructor check, which I mentioned earlier, would usually look like:

var arr = []; 
arr.constructor == Array; // true

Both instanceof and constructor look very innocent and seem like great ways to check if an object is an array. If I remember correctly, latest jQuery is using constructor:

An excerpt from jQuery (rev. 5917):

... 
isArray: function( arr ) {
  return !!arr && arr.constructor == Array;
} 
...

The problems arise when it comes to scripting in multi-frame DOM environments. In a nutshell, Array objects created within one iframe do not share [[Prototype]]’s with arrays created within another iframe. Their constructors are different objects and so both instanceof and constructor checks fail:

var iframe = document.createElement('iframe'); 
document.body.appendChild(iframe); 
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]  
 
// Boom! 
arr instanceof Array; // false  
 
// Boom! 
arr.constructor === Array; // false

This “problem” was mentioned by Crockford as far as back in 2003. Doug suggested to try duck-typing and check for a type of one of the Array.prototype methods – e.g.:

typeof myArray.sort == 'function'

Exactly for these reasons Javascript authors often resort to a second approach:

2) Duck-typing

We’ve been using it in Prototype.JS for quite some time now. Dean Edwards was using it in its base2, last time I looked at it.

An excerpt from Prototype.js (v. 1.6.0.3):

function isArray(object) {
  return object != null && typeof object === "object" &&
    'splice' in object && 'join' in object; 
}

By “fixing” multi-frame “problem”, this naive approach fails short in some of the trivial cases. If you were ever to have an object with splice and join properties, Object.isArray would obviously detect that object as being an Array:

var testee = { splice: 1, join: 2 }; 
Object.isArray(testee); // true

Back in June, I was reading ECMA-262 specs and noticed that there was an easy way to get value of an internal [[Class]] property that every native object has. Object.prototype.toString was defined like so:



Object.prototype.toString( )
When the toString method is called, the following steps are taken:
1. Get the [[Class]] property of this object.
2. Compute a string value by concatenating the three strings “[object ", Result (1), and "]“.
3. Return Result (2)

Contrary to Function.prototype.toString which is implementation dependent and is NOT recommended to be relied upon, Object.prototype.toString has a clearly defined behavior for all native objects.



15.3.4.2 Function.prototype.toString()
An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration. Note in particular that the use and placement of white space, line terminators, and semicolons within the representation string is implementation-dependent.

Just as a fun exercise, I wrote a simple __getClass method, put it into an “experimental” folder and forgot about it : )

function __getClass(object) {
  return Object.prototype.toString.call(object)
    .match(/^\[object\s(.*)\]$/)[1]; 
};

A couple of weeks ago, though, someone created a ticket for Prototype.js – proposing an Object.isDate method. An implementation used constructor check and so was vulnerable to cross-frame issues. This is when I remembered about getClass and its possible usage in isArray, isDate and other similar methods.

Specs mention that:



15.4.2.1 new Array([ item0[, item1 [,...]]])

The [[Class]] property of the newly constructed object is set to “Array”.


This means that creating isArray function could not be simpler than:

function isArray(o) {
  return Object.prototype.toString.call(o) === '[object Array]'; 
}

The solution is not dependent on frames (since it checks internal [[Class]]) and is more robust than duck-typing approach. I have tested it on a handful of browsers (including some archaic and mobile ones) and was happy to find that all of them are indeed compliant in this regard.

Let’s hope this little “trick” serves as a remedy to cross-frame issues that authors struggle to find workarounds for : )

Happy new year!

Categories: [[Class]], isArray 78 Comments »

Comments (78)

  1. Gravatar

    Seb said:

    Happy New Year kangax!
    And thanks for the post

  2. Gravatar

    Fabian Jakobs said:

    This made my day! Some days ago I had the problem to detect whether a variable, which came from a different frame, contained a function but neither instanceof nor duck typing can do this. The typeof operator was no option either because RegExp are typeof function as well. With your solution I can finally do this test.

  3. Gravatar

    Chris said:

    Very nice!

  4. Gravatar

    John Resig said:

    Just to clarify: As of 1.3 jQuery no longer uses instanceof or .constructor – for the same reasons that you list here. We were especially hitting problems when dealing with cross-frame pages.

  5. Gravatar

    Nicolas said:

    Very interesting post!, Thanks!

  6. Gravatar

    Dean Edwards said:

    Yes, I was using duck-typing in base2 (for Arrays only). I will switch to this method in the next release though.

  7. Gravatar

    Fabian Jakobs said:

    First experiments with this approach look very promising. I think we’ll use it in the next qooxdoo release as well.

  8. Gravatar

    kangax (article author) said:

    @John
    Good to know jQuery is cross-frame safe now. What exactly are you guys using?

    @Fabian
    Glad it helped : ) It’s also worth mentioning that same approach can be used with `Date`, `RegExp` and other common objects.

  9. Gravatar

    Simon said:

    Nice finding!
    I was always feeling a bit uncomfortable about the duck typing approach.

    Btw, your avatar links to http:///

  10. Gravatar

    kangax (article author) said:

    @Simon

    Thanks, fixed now.

  11. Gravatar

    John Resig said:

    @kangax: We use the Object.prototype.toString technique.
    http://dev.jquery.com/browser/trunk/jquery/src/core.js#L607

  12. Gravatar

    James said:

    Nice write-up!

    If I know I’m not going to be using any frames then I usually just use this:

    Array.prototype.isArray = true;
    alert( ([]).isArray ); // true

  13. Gravatar

    Keeto said:

    Mootools has an interesting approach with it’s $type function. Mootools wraps all native objects via the “Native” constructor in order to easily implement new methods for them. The Native constructor also creates a new property, called $family, which is the value returned by the $type() utility function.

  14. Gravatar

    kangax (article author) said:

    @James

    That would, unfortuantely, lead to false results in cases when truthy `isArray` property is present in an object (or in object’s prototype chain – exactly what happens in your example – where property is found in an object referenced by `Array.prototype`). While it could work in “closed” systems, it doesn’t seem like a viable solution in the wild : )

    @Keeto

    Interesting, although sounds a bit too obtrusive : )

  15. Gravatar

    Timothy said:

    This is really useful. Thanks a lot!

  16. Gravatar

    aNieto2k said:

    Hi, I found an interesting data:


    var t = new Date().getTime();
    for (var x =0; x

  17. Gravatar

    aNieto2k said:

    Sorry, bad code (del previous comment).

    var t = new Date().getTime();
    for (var x =0; x<10000; x++){
    var a = [];
    a instanceof Array;
    }
    console.log(new Date().getTime() - t);
    // 32ms.
    var t = new Date().getTime();
    for (var x =0; x<10000; x++){
    var b = [];
    b.constructor == Array
    }
    console.log(new Date().getTime() - t);
    // 82ms.

  18. Gravatar

    Ted Henry said:

    See also Crockford’s Blog where he credits Mark Miller.

  19. Gravatar

    Luke said:

    @kangax
    Thanks so much for sharing this. Really a weight off so many web devs’ shoulders.

    On the topic of using the method with other native types, I put together this quasi typeof function that uses this method.
    http://gist.github.com/47997

  20. Gravatar

    Gloridea said:

    There’s a problem to apply this way to check the array.
    In multi-frame DOM environment which is mentioned above,
    IE doesn’t detect the array from other frame, however, all other browsers do correctly.
    In IE, Object.prototype.toString.call(arrayFromOtherFrame) returns [object Object].
    I think I have to apply both of the discernment method; Object.prototype.toString and duck typing.

  21. Gravatar

    kangax (article author) said:

    @Gloridea

    I couldn’t reproduce IE returning “[object Object]“. Could you, perhaps, attach a simple test case which replicates it, and I’ll try to look into it.

  22. Gravatar

    Jordan said:

    Would it not make more sense to have this:

    Object.prototype.toString.call(o) === Object.prototype.toString.call([]);

    Then you aren’t depending on the text, just that the array literal and the object are the same.

  23. Gravatar

    kangax (article author) said:

    @Jordan

    I don’t see anything wrong with string comparison.Your example creates an array object every time method is called (so is probably less memory efficient).It also calls Object.prototype.toString twice (and so is probably slower). Why do you want to avoid “text dependence”?

  24. Gravatar

    Jordan said:

    You’re right, and I don’t have a good reason not to use “[object Array]“, I was just thinking out loud.

  25. Gravatar

    Diego Perini said:

    Juriy,
    very nice addition to the detection tools serie. These few bits will be essential to improve the quality of our code.

    Sometime I need to detect if a method is natively implemented by the browser running our code. The above trick will only tell if it is a “[object Function]“, but it maybe a function extended by another piece of code or framework over which we have no control.

    So while writing my project I found very handy to have an isNative() check like this:

      // detect native method in object
      // not same scope of isHostObject
      isNative = function(object, method) {
        return object && method in object &&
          typeof object[method] != 'string' &&
          // IE & W3C browser return "[native code]"
          // Safari < = 2.0.4 will return "[function]"
          (/\{\s*\[native code\]\s*\}|^\[function\]$/).test(object[method]);
      }
    

    this is how I detect the method is natively implemented in the browsers and not just extended/added by third party code.

    Your advice on the above is really appreciated and tell if you see it can be fooled in some way or maybe you can suggest a better way to do it.

    Great work.

    Diego

  26. Gravatar

    RobG said:

    It seems reasonable to include a link to a discussion that includes this technique on comp.lang.javascript:

  27. Gravatar
  28. Gravatar

    kangax (article author) said:

    Thanks for the link, Rob. That was a good discussion.

  29. Gravatar

    Douglas Crockford said:

    The next edition of ECMAScript will probably have an Array.isArray(value) function that will return true if the value is actually an array.

  30. Gravatar

    kangax (article author) said:

    @Douglas Crockford

    That’s good to know. And, how exactly will a value be tested? [[Class]], presence of `Array.prototype` in prototype chain or maybe special [[Put]]?

    On a side note, `Object.isArray` would make more sense to me than a somewhat repetitively looking `Array.isArray`, although, given addition of bunch of “static” Object extensions in ES3.1, I can see how a committee would want to offload this from `Object` to `Array`.

  31. Gravatar

    Diego Perini said:

    Hope Douglas statement implicitly means that Array having different constructors (from other contexts) will still return true ;-)

  32. Gravatar

    Paul ROYE said:

    Hi !
    Thanks for this page.

    But the Object.prototype.toString.call(object) does not work with my current project.
    I use IE 7, and I have to compare two objects created in two different windows (multi frame/iframe).
    The two objects use the same class to be created.

    But Object.prototype.toString.call(object) gives me [object Object]..

    The checking method way works well.
    typeof(n.method) == “function” gives me true

  33. Gravatar

    kangax (article author) said:

    @Paul ROYE

    I’d love to take a look at a test case. Could you provide one?

  34. Gravatar

    RobG said:

    @Jordan
    If you want to use a string comparison created by calling Object.prototype.toString, there is no need for double calls or calling every time, just store the value in a closure:

    var isArray = (function() {
    var testString = Object.prototype.toString.call([]);
    return function(o) {
    return Object.prototype.toString.call(o) == testString;
    };
    })();

    Seems to me that will reduce the issues of buggy implementations returning funny strings, they might be few and far between anyway.

    There should be no need to use it with functions as jQuery does, with typeof should be sufficient there (naturally host objects are in a league of their own for any of this stuff).

    Richard Cornford explains why it doesn’t work for IE across frames:

    Looking into this it seems that inter-window exchanges of objects get
    wrapped in additional objects. Presumably this is for security reasons,
    but you get some strange effects from it, such as the – hasOwnProperty -
    method of the Arrays passed in from the external widow produce ‘object’
    when tested with – typeof -, while the ‘local’ array’s methods produce
    ‘function’.

    So does this mean that, although it has no basis in ECMA 262 for
    Array/host object discrimination, the “The Miller Device” is the better
    test? Unfortunately it does not because the “The Miller Device” is
    victim of the same bug as -

    Object.prototype.toString.call(arrayFromAnotherWindow)

    - returns “[object Object]‘ in IE 6 and 7 (with similar implications for
    the “The Miller Device” in ‘isFunction’ testing caused by the same
    ‘wrapping’ of ‘remote’ objects)

    Rob.

  35. Gravatar

    James said:

    It just occurred to me that you could use a native JavaScript method that expects an array (e.g. ‘apply()’) to test for a real array, for example:

    function isArray(arr) {
    var out = false;
    try {
    (function(){}).apply({},arr);
    out = true;
    } catch(e) {}
    return out;
    }

  36. Gravatar

    Diego Perini said:

    @James,
    great trick thank you, it was just there under our eyes…try/catch let us do many of these trick.

    Unfortunately I cannot make it to work in Opera (9.63). It seems Opera accepts also an Object as a second parameter for “.apply()”, so no “Type Exception” there. A fall back is needed in this case.

    Also maybe just return “true” in the try block and “false” in the catch block to avoid the only variable you have.

  37. Gravatar

    kangax (article author) said:

    James,

    I’m afraid using `Function.prototype.apply` with try/catch is probably not strict enough for a general-purpose `isArray`. If you look at specs for `apply`, you can see that both – an array object and an `arguments` object – are valid values (and don’t make `apply` throw TypeError). The problem is that an `arguments` object, as I’m sure you know, does not inherit from `Array.prototype` and so doesn’t have any of `Array` methods – push, pop, slice, etc. If I check something with `isArray` and get `true` I would expect that something to have all of those methods.

    var args = (function(){ return arguments; })();
    if (isArray(args)) {
    args.slice(1); // boom!
    }

    Perhaps, a better name for the method in your example would be `isArrayLike`.

    Another problem with try/catch is how slow it is comparing to other alternatives. I mentioned a try/catch -based alternative in ajaxian comments some time ago, but I would not recommend using it in production code for performance reasons.

    function isArray(o) {
    try {
    Array.prototype.toString.call(o);
    return true;
    } catch(e) {
    return false;
    }
    }

    Moreover, there’s another problem with try/catch -based solutions and it is that there’s no guarantee than an error is related to an object not being an array. An error could be related to something else which would lead to false positives. Javascript’s error facilities are, unfortunately, quite poor and unreliable.

    For example, in my previous snippet, we rely on `Array.prototype.toString` throwing error when it is called in a context of a non-array object. The problem is that `Array.prototype.toString` calls `toString` of each of array elements and if one of those methods happens to throw an error (which is, of course, idiotic, but is still a possibility), we would erroneously end up in a wrong branch : )

    function isArray(o) {
    try {
    Array.prototype.toString.call(o);
    return true;
    } catch(e) {
    return false;
    }
    }

    var o = [ { toString: function(){ throw new TypeError(); } }, 2, 3];
    isArray(o); // false

    I hope this clarifies things a bit.

  38. Gravatar

    Serkan Yerşen said:

    This is the easiest & simplest way I can think of:
    function isArray(obj){
        return obj? !!obj.push : false;
    }

    Isn’t this function valid?

  39. Gravatar

    kangax (article author) said:

    @Serkan Yerşen

    I actually explain duck typing (which your example is) in the post. Is it valid? That depends on how you define valid, of course.

  40. Gravatar

    Serkan Yerşen said:

    Oh Sorry, You are right, I didn’t notice that in the post. I actually read the summary of your post from the Ajaxian :) Anyways my example can be secured for this type of problems but it will no be efficient, simple or short anymore.

    Thank you for your answer.

  41. Gravatar

    DBJ said:

    Accuse me of “blog plug” or jump here : http://dbj.org/dbj/?p=270

    Just a formalization of what we all know and what is said here.

    NOTE: XMLHttpRequest is not in ECMA5 list of “inbuilt objects”. That is a mistake. Can we rectify this ?

  42. Gravatar

    Andy said:

    Maybe I’m missing something, but why would you care if its of some type Array if it walks and quacks like an Array? Shouldn’t you just check for the properties/methods you actually care about or intend on using? In other words, if you only intend on using splice and join, shouldn’t the presence of those methods be sufficient? Barring all that, here’s the version I’d use based on duck typing (sorry about the indention):

    ‘use strict’;

    var isArray = (function () {
    var
    isUndefined = function (o) {
    return o === undefined;
    },

    isFunction = function (o) {
    return typeof o === ‘function’;
    };

    return function (o) {
    return o &&
    !isUndefined(o.length) &&
    isFunction(o.concat) &&
    isFunction(o.join) &&
    isFunction(o.pop) &&
    isFunction(o.push) &&
    isFunction(o.reverse) &&
    isFunction(o.shift) &&
    isFunction(o.slice) &&
    isFunction(o.sort) &&
    isFunction(o.splice) &&
    isFunction(o.unshift);
    };
    }());

  43. Gravatar

    Andy said:

    Also, I sure hope no one overwrites Object.prototype.toString to emit debugging information or the object as JSON instead.

  44. Gravatar

    kangax (article author) said:

    @Andy

    Of course you can use duck-typing, although I think type checking with something like `isArray` results in a higher clarity and conveys code intention somewhat better.

    As far as your implementation, I wouldn’t suggest checking for so many array members, mainly for performance reasons. Ensuring existence/type of 2 – 3 common array members is usually enough: if it’s a real array, it will be recognized as such; and if it’s not and someone is trying to spoof it, then it doesn’t really matter how many of them `isArray` checks against.

  45. Gravatar

    Myk Melez said:

    I’ve been using |array.constructor.name == Array.name| or simply |array.constructor.name == “Array”|, which seem to work pretty well.

  46. Gravatar

    Diego Perini said:

    Myk Melez,

    it seems to me that your solution does not work both in IE 8 and in Opera 9.64.

    The “name” property of functions is not supported in those browsers.

    Have you tried it ?

  47. Gravatar

    kangax (article author) said:

    @Myk Melez

    That looks like Mozilla’s non-standard Function.prototype.name and so is not present in, say, JScript (as Diego pointed out)

  48. Gravatar

    SmashThePain said:

    I use this for testing, isNativeCopy.
    Object.isNativeCopy = function(o, from)
    {
    var c = Object.prototype.toString;
    if (from.prototype)
    {
    from = c.call(from.prototype);
    }
    return c.call(o) == from;
    }

  49. Gravatar

    SmashThePain said:

    Btw, function __getClass have trouble with Class who don’t haves prototype chain. Like Math object.
    Name of my function isNativeCopy, because i thing, this is the right name. Instanceof is perfect for my. When i have cross frames, class in parent and childs is complete different. “isArray” is harmful in this way. Because in parent everything can extend native object. One simple example.

    Array.prototype.each = function(){};
    if (isArray(arr))
    {
    arr.each() //Boom
    }

    Object.prototype.toString, have one problem with Error object. When i call
    Object.prototype.toString(TypeError); //[object Error]
    If anybody know, please explain. Thank you.

    Only problem for my with instanceof is. Function.prototype instanceof Function. This return false, but Function.prototype property is Function. Another problem with memory leak in IE, but i never tested this.

    People like you worth respect.

  50. Gravatar

    kangax (article author) said:

    @SmashThePain

    1) It looks like your isNativeCopy has similar downside as instanceof/constructor -based checks; it doesn’t work with objects from different contexts:

    // will only report `true` if `someObject` is created within the same context as `Array`
    isNativeCopy(someObject, Array);
    

    2) What exactly is the problem with Object.prototype.toString.call(Math)?

    3) What exactly is the problem with Object.prototype.toString(TypeError); //[object Error]? What are you expecting instead of “[object Error]“?

    4)

    Function.prototype instanceof Function; // false

    This might seem unintuitive but is exactly how it is specified in the ES3 specs. Function.prototype is a Function object (has “Function” [[Class]]). The main difference here is that its [[Prototype]] is set to Object.prototype and not Function.prototype as is the case with all the other native Function objects.

    instanceof operator rightfully compares prototype of right-hand-side object (Function) to all of the “items” from the prototype chain of the left-hand-side object (Function.prototype). Since Function.prototype’s chain essentially consists of Object.prototype, followed by null (none of which are equal to Function.prototype) instanceof returns false.

    5) Which leak are you talking about?

  51. Gravatar

    SmashThePain said:

    No. It’s work for different context.
    I extracting [object Array] from prototype of Object. Just like you, but don’t need. isArray, isFunction, isEverythingElse. If why have instance of object, i use juse toString(). Reason is object like Math, who don’t have prototype chain. If you want, test this code. I will be happy, if you can.

    Object.prototype.toString(TypeError); I expect [object TypeError], not only [object Error]. Instanceof in this case is better and not only this case. How to test my object? How i can make code like this:

    function MyObject(){};
    Object.prototype.toString.call(MyObject); //[object MyObject].

    If you know, please explain.

    Object.prototype.toString.call(Math); //[object Window]
    If you call Math.toString(); //[object Math]

    One post in ajaxian:
    http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak

  52. Gravatar

    SmashThePain said:

    Please edit my post. My english is farmful :))

  53. Gravatar

    SmashThePain said:

    Hi again kangax.
    I found couple of bug in “isNativeCopy”.

    /*
    IE 7, 8 bugs
    Object.prototype.toString.call()
    String.prototype [object Object]
    Error.prototype  [object Object]
    
    Fixed Object who haves prototype chain, but they constructor is function Object(){}
    Like XMLHttpRequest and DOM Objects
    */
    Object.isNativeCopy = function(o, from)
    {
    	var c = Object.prototype.toString,
    		s = c.call(o);
    	return s == c.call(from.prototype) || s == from;
    }
    

    For my, this works great in many situation. That’s what i need. Sometime instanceof is better, but this approach for my is best when i use “sandbox” with iframe or cross-frame scripting. This for my, guarantee:

    if (Object.isNativeCopy(arr, Array))
    {
         arr.push();
         arr.pop();
         arr.anythingNativeMethod();
    }
    

    Btw: That approach in ajaxian, to prevent memory leak is dirty. What are you thinking? I think everybody can make:

    window.hasOwnProperty = function(){};
    document.hasOwnProperty = function(){};
    

    How to prevent this leak? What are you think?
    I will be happy, if you give another approach.

    Thanks a lot!

  54. Gravatar

    SmashThePain said:

    Hi again kangax. I so so sorry for my flood, but this is very interesting article for my.
    After a couple of hours with test my last solution, and keep thinking about the problem. The problem of my code is three.
    First:

    Object.isNativeCopy(arr, {prototype: Array}) //Boom. It's normal behaviour.
    

    Second:
    In Ecma Script. Only native object who prototype [[Class]] property is different from [[Class]] property of instance from this object is RegExp.
    From my browser only Chrome behaviour is that like writing in Ecma specification.

    Three:
    JScript isn’t very familiar with Ecma specification.
    In Ecma:
    15.5.4 Properties of the String Prototype Object
    The String prototype object is itself a String object (its [[Class]] is “String”)
    In IE:
    [object Object]
    In Ecma:
    15.11.4 Properties of the Error Prototype Object
    The Error prototype object is itself an Error object (its [[Class]] is “Error”).
    In IE:
    [object Object]

    After all of this. I write new solution for this problem. This time i don’t compare [[Class]] property of instance and [[Class]] of prototype object. This time i have isFunctionCopy, isArrayCopy, isEverythingNativeObjectCopy based on your solution.

    (function()
    {
    	var o = Object,
    		types = ['Object', 'Function', 'Array', 'String', 'RegExp', 'Date', 'Error', 'Boolean', 'Number'],
    		curr_type;
    	for (var i = 0, len = types.length; i
    

    It’s a pity for my. I don’t like code like this. Hard coding styling :( But for moment this is right…
    If you have time, i will very happy from you answer.
    Sory again for my flood!
    Thanks a lot!

  55. Gravatar

    kangax (article author) said:


    Object.prototype.toString(TypeError); I expect [object TypeError], not only [object Error]. Instanceof in this case is better and not only this case.

    That’s a good point. As you can see from 15.11.7.2 (ES3) TypeError (as well as other NativeError objects) all have [[Class]] of “Error”. ES5 actually didn’t change anything in regards to this, so TypeError objects in ES5 still have this “generic” “Error” [[Class]].

    function MyObject(){};
    Object.prototype.toString.call(MyObject); //[object MyObject].

    You can’t change [[Class]] values in ES3. That’s one of the reasons why [[Class]] based checking is more reliable than duck typing (whether this reliability is important is another question). It’s impossible for a 3rd party script to spoof [[Class]], since [[Class]] value can only be inferred from Object.prototype.toString call and can never be changed manually. Host objects, on the other hand, are allowed to do all kinds of stuff, including having all kinds of [[Class]] values. That’s, actually, one of the main problems with [[Class]] based checking in ES3.

    Object.prototype.toString.call(Math); //[object Window]

    I get “[object Math]” as expected (and as per specs) in FF 3.5.2. Which client did you test it with?

    JScript isn’t very familiar with Ecma specification.

    Yep. JScript is, undoubtedly, one of the worst “modern” implementations at conforming to ES3.

  56. Gravatar

    SmashThePain said:

    I get “[object Math]” as expected (and as per specs) in FF 3.5.2. Which client did you test it with?
    FF 3.0.13
    Whatever, in Ecma 3 Math istance [[Class]] is [object Math] that is good news. This technique is reliability for my application. I pushed Math in my Array and automaticly i have “isMathCopy” :))

    You can’t change [[Class]] values in ES3
    This is right. Maybe if engine set [[Class]] property itself, based on your constructor is safe? But i override in someone frame Array class, and in parent i get [object Array] and:

    if (isArrayCopy(arr))
    {
    arr.push() //boom
    }

    Only i don’t understand, why RegExp.prototype [[Class]] is Object. I don’t see logic in this. RegExp.prototype isn’t just one inherit from Object. RegExp.prototype define two new method. Why is in this way? Do you have logical opinion of this question?
    Thanks a lot man.

  57. Gravatar

    kangax (article author) said:

    Well, RegExp.prototype has [[Class]] of “RegExp” as per specs, so if you’re seeing “Object”, than it’s a bug in implementation.

    In FF 3.5.2, I get:

    Object.prototype.toString.call(RegExp.prototype); // [object RegExp]

    On a side note, why are naming methods as – isArrayCopy, isMathCopy, etc. Why “copy”?

  58. Gravatar

    SmashThePain said:

    Yes in Opera and IE is the same. But in Chrome [object Object]. I don’t have Safari but i will tested.
    Look in Ecma 3:
    15.10.6
    The value of
    the internal [[Class]] property of the RegExp prototype object is “Object”.

    If you are right this line work excellent:

    return c.call(o) == c.call(from.prototype); //Exclude Math object, who don't have prototype

    Why “copy”?
    Because we works with different context. Different context is dangerous sometime. If anybody extend in parent object like Array. He expect isArray() return exactly what return instanceof. He expect new member of object. And this is confused. When i want to know exactly, this object have same prototype like another. I use:

    this.object instanceof Another

    When i want this.object haves native interface of Native object i use “is…Copy”. For my this name is better. I don’t want method who confused for my and developer who’s used my code. That’s i think. If you have different opinion, i will be happy to read.

    Btw, i tested that memory leak instanceof in IE. My tested been in IE7, 8. In this two version i don’t see any leak. Must be tested in IE6 king of the leak :)))

  59. Gravatar

    kangax (article author) said:


    Look in Ecma 3:
    15.10.6
    The value of
    the internal [[Class]] property of the RegExp prototype object is “Object”.

    Ah, yes, you’re absolutely right. What’s interesting is that ES5 fixes this by stating that [[Class]] of `RegExp.prototype` is “RegExp”. Clearly, this is much more consistent than it is in ES3.

  60. Gravatar

    Breton said:

    function isArrayLike(a) {
    return typeof a ===”object && !!a.length;
    }

    function arrayLift(a) {
    return Array.prototype.slice.apply(a);
    }

    it might not be a “real” array, but it’s good enough to copy into a real array, or use most other array prototype functions on it. The only thing many of them expect is a length property. Most of the time I don’t even need a “real” array. Just an object with a length property.
    (be careful though, strings have a length property too! that’s why typeof a ===”object” is important.)

Trackbacks

  1. Juhu Kinners » ‘typeof’ considered useless - or how to write robust type checks said:

    [...] always created in the main window and never in a frame or a popup window. As kangax explains in his recent post: The problems arise when it comes to scripting in multi-frame DOM environments. In a nutshell, [...]

  2. Painfully Obvious → Blog Archive → Link: Perfection Kills said:

    [...] How to write a robust isArray. Faced with a long-standing problem, Juriy finds the solution by reading the spec. Let this be a lesson to all of us. [...]

  3. Ajaxian » isArray: Why is it so bloody hard to get right? said:

    [...] has detailed the issue and even offers the latest and greatest in the art, which has lead to Prototype and other libraries [...]

  4. isArray: Why is it so bloody hard to get right? | How2Pc said:

    [...] has detailed the issue and even offers the latest and greatest in the art, which has lead to Prototype and other libraries [...]

  5. Detectar el tipo de una variable en Javascript : Blogografia said:

    [...] ir hacia atrás en lugar de avanzar. Las nuevas revisiones de Javascript, arrojan un poco de luz en la detección del tipo de variables usados mediante 1 operador y/o una propiedad de las [...]

  6. 20090121 網摘 - 財政預算案諮詢漫版 - 網絡暴民 Jacky’s Blog said:

    [...] ‘instanceof’ considered harmful use prototype.toString instead [...]

  7. Detectar el tipo de una variable en Javascript | aNieto2K said:

    [...] ir hacia atrás en lugar de avanzar. Las nuevas revisiones de Javascript, arrojan un poco de luz en la detección del tipo de variables usados mediante 1 operador y/o una propiedad de las [...]

  8. perfection kills » Blog Archive » Detecting built-in host methods said:

    [...] Perini mentions that he often needs to detect whether a method is “native” (i.e. provided by the host [...]

  9. JavaScript类型检测小结(下) - 岁月如歌 said:

    [...] How to write a robust ‘isArray’(很详细的介绍,一步一步,很有条理) [...]

  10. Things you should know when not using a JS library « Lea Verou said:

    [...] Use Object.prototype.toString instead. [...]

  11. Snippets: Improved getClass and getFunctionName | Elijah Grey said:

    [...] while ago, I made an modified version of Juriy Zaytsev’s __getClass method from his `instanceof` considered harmful blog post. My modified version uses the name of the constructor (if not already set) property of an [...]

  12. qooxdoo » News » The week in qooxdoo (2009-20-20) said:

    [...] all native JavaScript types. The implementation is based on the technique described in the article “`instanceof` considered harmful (or how to write a robust `isArray`)” by Kangax. We plan to gradually update the framework code to use these type [...]

  13. Comparaisons d’objets en javascript | nyamsprod's web labs said:

    [...] à ma fonction. Sans aller dans les détails, il se trouve que l’utilisation des fonctions typeof et instanceof est déconseillée, il faut plutôt se fier à Object.prototype.toString, une propriété interne de tout objet en [...]

  14. Tricking firebug to display an Object as an Array | Karmagination said:

    [...] The Objects are not converted to Arrays. I believe Firebug uses duck-typing to detect Arrays when displaying them on the [...]

  15. Painfully Obvious → Blog Archive → Deep-extending objects in JavaScript said:

    [...] these reasons, the typeof operator won’t work, nor will Juriy’s clever approach. Instead we’ll have to check the [...]

  16. typeof vs instanceof - LA.NET [EN] said:

    [...] good news is that there was a guy that noticed that he could use the toString method of the Object prototype to get the correct description of the current object (I don’t know who introduced this technique [...]

  17. perfection kills » Blog Archive » Professional Javascript. Review. said:

    [...] in one of the examples. It’s being done via instanceof operator and without explanation of why it might not work and what to do instead (note that iframe issue is mentioned later, in Chapter 20, however [...]

  18. G-zone » JS:检测变量类型,原来有这么多问题 said:

    [...] G-zone 4/02/2010JS:检测变量类型,原来有这么多问题 跳至评论 发表于: 前端er 作者:admin 刚刚看了篇文章,介绍检测变量类型,原文见这里。作者分析的很好,但也有不全面的地方,我简单翻译下,就不按照原文的顺序,一字一句翻译了。 [...]

Leave a Comment

Allowed tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

Please, don't forget to escape your input (<, > and &). Wrap code sections with <pre>