5

In this question I did not see suggestions to use constructor.

So instead of typeof callback == "function"

I would use callback && (callback.constructor==Function).

To me it seems obvious that comparison to memory pointers is always better than comparison to strings in terms of both runtime performance and coding safety.

Why not use constructor to detect all types and forget about ugly typeof?

It works for all primitive types, functions and arrays:

undefined === undefined
null === null
[1,2,3].constructor == Array 
(1).constructor == Number
(true).constructor == Boolean
(()=>null).constructor == Function
'abc'.constructor == String
(new Date()).constructor == Date
else it's an object, where instanceof helps to detect it's parents if needed.

If string interning can be relied upon then runtime performance advantage goes away. But safe coding advantage still stays.

Community
  • 1
  • 1
alpav
  • 2,972
  • 3
  • 37
  • 47

2 Answers2

4

instanceof is better because it works with inherited constructors. .constructor is a mutable property on an object, so it's not a good thing to check because one can simply change it. You can't change the instanceof something.

const x = new Date();
console.log("Date Constructor", x.constructor);
x.constructor = "herpderpderp";
console.log("Date Constructor", x.constructor);
SuperStormer
  • 4,997
  • 5
  • 25
  • 35
Sterling Archer
  • 22,070
  • 18
  • 81
  • 118
  • Is it bad coding practice to assign constructor after object was created with different constructor ? What is practical benefit of such assignment ? Is it often used in practice ? – alpav Oct 01 '16 at 02:58
  • 1
    I would assume so, since you're overriding the constructor which can potentially mess with other 3rd party scripts. I can see no benefit in manipulating it, and I have never used it in my 5 years developing to be honest. – Sterling Archer Oct 01 '16 at 03:00
  • Then practically it does not matter that constructor is mutable, which is the only advantage of using typeof vs constructor. In this case your answer does not answer my question - "why use typeof instead of constructor ?". – alpav Oct 01 '16 at 03:08
  • It does: instanceOf inspects the prototype chain. If you reassign the prototype of an object, you lose the `constructor` property. With instance of, it will still know the constructor. – Sterling Archer Oct 01 '16 at 03:13
  • I just checked in Chrome, if I assign callback.prototype = {a:1}, the following is still true: callback.constructor == Function. As to instanceOf, it is also slow just because it has to inspect prototype chain. Also it works only for objects anyway. – alpav Oct 01 '16 at 03:22
  • Almost everything in JS is an object in some form – Sterling Archer Oct 01 '16 at 03:31
  • No, see https://blog.simpleblend.net/is-everything-in-javascript-an-object/ – alpav Oct 01 '16 at 04:31
  • Also typeof new String('a')=='object', but new String('a').constructor == String, so constructor works even better than typeof. Why use typeof ? – alpav Oct 01 '16 at 04:44
2

You can also define your own functions for both tests that also work on primitives by using getPrototypeOf and isPrototypeOf. E.G.:

function typeOf(obj) {
    return Object.getPrototypeOf(obj).constructor;
}

typeOf(2) === Number // true
typeOf("cats") === String // true
class Foo {}
typeOf(new Foo()) === Foo // true

class Bar extends Foo {}
typeOf(new Bar()) === Bar // true
typeOf(new Bar()) === Foo // false    

var b = new Number(3)
if (typeOf(b) === Number) {
    console.log(b.valueOf() + 5)
}

and

function instanceOf(obj, type) {
    var objType = typeOf(obj)
    return (
        // Allow native instanceof in case of Symbol.hasInstance
        obj instanceof type ||
        // Handle case where is of type type
        typeOf(obj) === type ||
        // Handle general case
        type.isPrototypeOf(objType) || 
        // Handle special case where type.prototype acts as a
        // prototype of the object but its type isn't in the
        // prototype chain of the obj's type
        // OPTIONALLY remove this case if you don't want
        // primitives to be considered instances of Object
        type.prototype.isPrototypeOf(objType.prototype)

    );
}

instanceOf(3, Number) // true
instanceOf(new Number("2"), Number) // true
instanceOf(2, Number) // true, OPTIONAL with the last condition
                      // but is probably preferable as 2 does
                      // indeed get all methods of Objects
class Hat {}
instanceOf(new Hat(), Hat) // true
class Fedora extends Hat {}
instanceOf(new Fedora(), Fedora) // true
instanceOf(new Fedora(), Hat) // true
instanceOf(new Fedora(), Object) // true
Jamesernator
  • 778
  • 7
  • 13