You should be familiar with the difference between primitives and objects [1].
> typeof undeclaredVariable === "undefined" true > var declaredVariable; > typeof declaredVariable 'undefined' > typeof undefined 'undefined'There are other ways to check whether a value is undefined:
> var value = undefined; > value === undefined trueBut they throw an exception when used with an undeclared variable – only typeof allows you to do that:
> undeclaredVariable === undefined ReferenceError: undeclaredVariable is not definedNote: uninitialized variables, missing parameters and non-existent properties are not problematic, because they can all be accessed and have the value undefined:
> var declaredVariable; > declaredVariable === undefined true > (function (x) { return x === undefined }()) true > ({}).foo === undefined trueProblem: typeof is verbose when used for this task.
Better: This is a rare use case, so there might not be a need for a better solution. One could introduce a dedicated operator:
> defined undeclaredVariable false > var declaredVariable; > defined declaredVariable falseAlternatively, one might also want to just check whether a variable has been declared:
> declared undeclaredVariable false > var declaredVariable; > declared declaredVariable true
> typeof null 'object'Better: Don’t use typeof for this task, perform this check as follows:
function isDefined(x) { return x !== null && x !== undefined; }Another possibility is to introduce a “default operator” that returns defaultValue if myValue isn’t defined:
myValue ?? defaultValueThe above expression is equivalent to
(myValue !== undefined && myValue !== null) ? myValue : defaultValueFurthermore,
myValue ??= defaultValueis an abbreviation for
myValue = myValue ?? defaultValueWhen you access a nested property such as bar, you would want help from an operator, as well:
obj.foo.barThe above expression will fail if either obj or obj.foo is not defined. An operator .?? could make the above expression safe by returning the first undefined or null it encounters while traversing the path of properties:
obj.??foo.??barThe above is equivalent to:
(obj === undefined || obj === null) ? obj : (obj.foo === undefined || obj.foo === null) ? obj.foo : obj.foo.barChecking for definedness will become somewhat less important with default parameter values in ECMAScript.next.
function isObject(x) { return (typeof x === "function" || (typeof x === "object" && x !== null)); }Problems: The above check is complicated by the fact that typeof considers functions different from objects and that typeof null is "object".
Better: The above function is a pretty good solution. The following way of detecting an object is also frequently used:
function isObject2(x) { return x === Object(x); }Caveat: You might think that you could use instanceof Object here. But instanceof uses the prototype of an object to determine whether the instance-of relationship holds and you can create an object that doesn’t have a prototype ():
> var obj = Object.create(null); > Object.getPrototypeOf(obj) nullobj is indeed an object, but not an instance of anything:
> typeof obj 'object' > obj instanceof Object falseYou’ll rarely encounter this kind of object in practice, but it can exist and there are uses for it.
> typeof "abc" 'string' > typeof undefined 'undefined'Problem: You have to be aware of the typeof null quirk.
> typeof null // beware! 'object'Better: The following function is a fix (for this use case).
function getPrimitiveTypeName(x) { var typeName = typeof x; switch(typeName) { case "undefined": case "boolean": case "number": case "string": return typeName; case "object": if (x === null) { return "null"; } default: // fall through from prev. case throw new TypeError("Argument isn’t primitive: "+x); } }Best: It would be great to have a function getTypeName() that performs the above algorithm for primitives and returns the value of the internal [[Class]] property for objects. [2] shows how such a function can be implemented.
> typeof function () {} 'function' > typeof Object.prototype.toString 'function'In principle, instanceof Function is another way of performing this check. At first glance, it is more elegant. But browsers exhibit a strange quirk: Each frame and window has its own global variables. Hence, if you pass an object from one frame to another, instanceof ceases to work, because the right-hand side refers to a different constructor. That is why ECMAScript 5 has Array.isArray(). It would be nice to have a cross-frame mechanism for checking whether an object is an instance of a given constructor. The aforementioned getTypeName() is one possible work-around, but there might be a more fundamental (as in: cleaner) solution.