14

I'm trying to use some of the more advanced OO features of Javascript, following Doug Crawford's "super constructor" pattern. However, I don't know how to set and get types from my objects using Javascript's native type system. Here's how I have it now:

function createBicycle(tires) {
    var that = {};
    that.tires = tires;
    that.toString = function () {
        return 'Bicycle with ' + tires + ' tires.';
    }
}

How can I set or retrieve the type of my new object? I don't want to create a type attribute if there's a right way to do it.

Is there a way to override the typeof or instanceof operators for my custom object?

1
  • So far, every answer has dealt with the instanceof operator. Is there any way to override the typeof operator? I'm not biased toward one method or the other, but I want to know if there's a reason typeof is unsuitable. Thanks for all the info! Commented Dec 17, 2009 at 13:54

5 Answers 5

15

The instanceof operator, internally, after both operand values are gather, uses the abstract [[HasInstance]](V) operation, which relies on the prototype chain.

The pattern you posted, consists simply on augmenting objects, and the prototype chain is not used at all.

If you really want to use the instanceof operator, you can combine another Crockford's technique, Prototypal Inheritance with super constructors, basically to inherit from the Bicycle.prototype, even if it's an empty object, only to fool instanceof:

// helper function
var createObject = function (o) {
  function F() {}
  F.prototype = o;
  return new F();
};

function Bicycle(tires) {
    var that = createObject(Bicycle.prototype); // inherit from Bicycle.prototype
    that.tires = tires;                         // in this case an empty object
    that.toString = function () {
      return 'Bicycle with ' + that.tires + ' tires.';
    };

    return that;
}

var bicycle1 = Bicycle(2);

bicycle1 instanceof Bicycle; // true

A more in-depth article:

Sign up to request clarification or add additional context in comments.

1 Comment

ES6 allows you to overide the [Symbol.hasInstance] function which overrides how instanceof works. Beware however that using that just once anywhere may cause a significant global slowdown of all JavaScript in your page - see slides 29 to 34 of a Google V8 presentation which says "Install Symbol.hasInstance property anywhere disables fast-path globally for the VM".
3

if you're using a constructor, a better solution than instanceOf, would be this :

Object.toType = function(obj) {
  return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
}


toType({a: 4}); //"object"
toType([1, 2, 3]); //"array"
(function() {console.log(toType(arguments))})(); //arguments
toType(new ReferenceError); //"error"
toType(new Date); //"date"
toType(/a-z/); //"regexp"
toType(Math); //"math"
toType(JSON); //"json"
toType(new Number(4)); //"number"
toType(new String("abc")); //"string"
toType(new Boolean(true)); //"boolean"
toType(new CreateBicycle(2)); //"createbicycle"

The explanation of WHY it's the best way to do it relies in This post.

2 Comments

Adding methods to built in objects is bad practice as it has the potential to break if the feature is added in a later version. Also, the function you add does not behave properly when the keys of the object are iterated with for(var key in blah) {}. You should use helper methods or inherit the object into your own object to make changes to it. This is why other programming languages don't let you do it.
This doesn't answer the question at all. The question is about changing the type, not getting the type.
3

If you declare Bicycle like this, instanceof will work:

function Bicycle(tires) {
  this.tires = tires;
  this.toString = function () {
    return 'Bicycle with ' + tires + ' tires.';
  }
}

var b = new Bicycle(2);
console.log(b instanceof Bicycle);

3 Comments

And of course if you're going to use constructors, a good naming scheme is to keep the first letter capitalized and don't start it with "create" unless it creates another constructor. In this case, the function name should be "Bicycle".
That would definitely work, but the OP is wanting to use the Crockford's super constructors technique, which avoids any use of this and new.
I think even Crock is cool with prototype-based inheritance these days.
1

In Firefox only, you can use the __proto__ property to replace the prototype for an object. Otherwise, you cannot change the type of an object that has already been created, you must create a new object using the new keyword.

1 Comment

Modern browsers instead support Object.setPrototypeOf as part of ES6. However using that will likely cause severe performance issues. Linked MDN article says "Warning: Changing the [[Prototype]] of an object is a very slow operation, in every browser and JavaScript engine. The effects on performance of altering inheritance are subtle and far-flung".
0

In my opinion, in a properly designed type heirarchy, you don't need to know the types of the individual objects. But I seem to be in the minority on that point.

If you must have type identification, make it explicit.

MyClass.prototype.type = "MyClass";

It is reliable and portable, at least for your objects. It also works across contexts. DOM objects are another matter, although you can make things easier for yourself with

window.type = "window";

and so on.

I believe the quote above was written by Douglas Crockford.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.