JavaScript: You want a Role, not a Type

Do not worry. That above is one Arnold Schwarzenegger in Austria. On a competition. Showing the difference between a “role” and a “type”. Arnie above is a “type” of a person, that has taken a particular “role” on that occasion.

Update 2012 Oct 08

Fortunately, since IE8, ES5 in IE behaves better. (IE? Yes IE. “Large Customers” still require IE support in 2022 Q1)

Therefore I am able to use this very simple (and global) roleof implementation.

Now I have a global function:

which helps me escape the

pitfalls and debates. And

“suite” is still in there for any case and for Your own comfort:

2010: Original Article

Few days ago one jQuery discussion, “blossomed” around good old: isObject() or is it isArray() , dilemma. One of few never ending javascript dramas.

Yet another typeof related discussion? That was my initial reaction. A very stubborn little issue, especially considering that javascript is a typeless language. Which in reality is a misnomer? It is impossible to have true and pure “no types involved” language.

Rather there is a type system behind which gives the illusion, that types do not exist. It transparently coerces values from ‘type’ A to ‘type’ B. This is usually implemented with the help of a “root type”. An object that encapsulates type issues. Sometimes this is called: meta-type. Type of types.

For example in VBScript we have VARIANT. In JavaScript, there is a slight confusion, unfortunately. JavaScript implementations have something one might call: “root type”. It is implemented and called: “Object”. But. ECMA spec is not mirroring this, explicitly. So in JavaScript, we have automatic type coercion rules. But they are not specified in simple object-oriented terms. That is: a global JavaScript object, called “Object” is not specified as a meta-type. For example, intrinsic number type is not specified as a “kind-of-a” Object, that is an object that inherits from “Object”.

And then, there is the unfortunate typeof operator. And it’s even more unfortunate siblinginstanceof.an
Instead of indulging in a lengthy explanation on why are they both problematic, I will show this code:

Well, there is no other so (seemingly) simple language like JavaScript, which can confuse a good-natured beginner, so much. Here she is bound to ask you: How can A be an “intanceof” Array, if typeof Array is clearly a “function”? Well, in JavaScript it certainly is not, but then it certainly appears to be. Ugh, imagine explaining this to someone who knows nothing of, object orientation, C++, or C# or Java and who is learning programming through JavaScript. Or this little ‘number’:

Ok, these are the problems, we need some solutions. Let me jump straight back to the point. Beginners or not, JavaScript developers have simply given up on those two above. Each and every javascript library has somewhere inside at least two methods, which are answering a simple (?) question: is this thing an Array or is this thing an Object?

This is the bare minimum, since typeof returns “object” for both Objects and Arrays. Is this supposed to be or not, is a moot point, which can be properly answered only if we think of the implementation of the language. Then we will realize that typeof sometimes “reveals” the parent object and sometimes not.

 

Most importantly type of array is returned as the type of its (real) prototype: “object”. Why is this and how is this is discussed countless times elsewhere. What we need is the solution for a robust and feasible way to encapsulate these issues and “forget” about them. This is done usually in the form of two simple (or not, it depends on) functions :

Nice and simple and works. Well, almost. In IE7 and before, this isFunction, will not work.

Object.prototype.ToString(obj) , has “special” behavior if given an argument. It returns a full ECMA “Class” name. A “type descriptor”, of the argument given. In IE, isFunction() has a problem. isArray() has no such problems. It works. For the time being. What do I mean by this? I mean that I do not like this implementation. This implementation depends on well-behaved browsers. This means, that we expect each and every browser to return these exact strings. In the present and future. Including some new and peculiar little browser on some new mobile future platform? Hmm, I would not bet on this. Some will, but I would not.

Towards the Solution

I do happen to think that first of all conceptually it has to be clear that “type” is an overloaded term here. It seems we will be able to decouple ourselves from javascript type issues. First of all (as ever), we have to sort out the terminology.( conceptology ?)

Object.prototype.toString.call(o) returns what I shall call: “type descriptor”
Type descriptor format is well known ES5 concept. Bellow is a formal definition of a few strings which are reserved in ES5. Just like “use strict” is a reserved string. I will formalize this as :

 

(Yes, “Argument” and “JSON” are part of ES5.) Type descriptor is a string naming the type and the role of the object in JavaScript. And the key word we have here is: the role!
We use this to differentiate an ES5 entity typeof name from its Role.
The core idea is to use the Role concept to differentiate between the type and the role.

Array-related example: [] is an “object” whose role is to be an “Array”.

Therefore JavaScript “typeof” operator returns the “type name”, NOT the “role name”. And there is indeed no “roleof” operator in JavaScript. There is no operator in JavaScript, which would simply return a proper role name on an array. There is only one thing: Object.prototype.toString() :

description = type + role

Recap time. Type alone is not enough as a full description of ES5 entities. 99% of the time what are we actually interested in are roles, not types of JavaScript objects. We want to know (for example) if something is an Array or Date or RegEx. In 99% of cases we do not want to know that they are all objects. We know that already. To clarify all of this, let me present to You, a small framework based on this discussion.

What we have generated here, is first of all one “repository” of roles and their id’s.

Then, we have generated “is” function for each role defined. For example, for Function we have generated:

 

Please note that here we actually compare not the string name, but the ID of the role “Function”, which above is: 4, with the ID of the computed role of the argument “o”.

Ok, but really: Why ‘Role’, why not ‘Class’?

Simply because Class is one very overloaded term. Especially if one deals with several languages in the same time. ES5 is using the term ‘Class’, for one of its ‘internal property’-es. The “ES5 spec” says:

The value of the [[Class]] internal property is defined by this specification for every kind of built-in object. The value of the [[Class]] internal property of a host object may be any String value except one of “Arguments”, “Array”, “Boolean”, “Date”, “Error”, “Function”, “JSON”, “Math”, “Number”, “Object”, “RegExp”, and “String”. The value of a [[Class]] internal property is used internally to distinguish different kinds of built-in objects. Note that this specification does not provide any means for a program to access that value except through Object.prototype.toString()

Hooray! This is excellent news! String values : “Arguments”, “Array”, “Boolean”, “Date”, “Error”, “Function”, “JSON”, “Math”, “Number”, “Object”, “RegExp”, and “String”, are reserved words by the latest ES5 spec. This is what we use as role names, and this is what comes out of Object.prototype.toString().

But… I beg to differ on using the term “Class”, in this context. Simply because “class” as used here is not what “class” means elsewhere in OOD, OOA, C++, C#, or Java. There is no prescribed standard “class hierarchy” behind a JavaScript implementation. There are no classes in JavaScript. Just objects and prototypes. Also, the “Class”, is just used (borrowed) and not defined in ECMA Script 5 spec. Instead, I have chosen the term “role”, which I think, is much closely related to the reality of the JavaScript nature. So, what is ECMA 5 internal property “Class” is what we call here: the “Role”. And the reserved values of it are what we use as role names. I do not invent anything here, besides a new term: “Role”.

What about IE?

I hope I have presented here a simple and useful mechanism, that does its job well. Especially if ubiquitously used, across all the browsers, projects, and teams. This mechanism also seems robust and future-proof. From now on I will use this each and every time I need objects Role related information in my JavaScript code. Which is almost always. Also here we are facing IE browser differences, head-on. Because of the simple reason: in IE < 8, methods on DOM nodes are treated as objects. Are they “treated” or “implemented” or “thought of”, does not matter. What matter is that in IE 7 or before, we have this situation :

Ok, ok. I am a professional. I will serve my customers. I will not try to educate them instead ;) Solutions for IE < 8, are certainly doable, and perhaps not very elegant. For that please see my next post.

–DBJ

A well-known role
A well-known role