notes on javascript, linux, and more

11.11.2009

typeof(null) and Pointers in JavaScript

I had a bit of an epiphany this morning when I was trying to figure out why typeof(null) === 'object' yields true. If you have some value where typeof(value) === "object" and you assign that value to a variable, that variable becomes a reference to that value. If you have some value where typeof(value) === "string" || typeof(value) === "number" || typeof(value) === "boolean", and you assign that value to a variable, that variable now contains a copy of that value rather than a reference. But sometimes, typeof(my_string_variable) === 'object', and assigning that string variable to another variable will create a reference to, not a copy of, that variable's value. Why is that, and why is typeof(null) === 'object' true?

First let's run through some examples to illustrate what I'm talking about.


var
a = 'a',
b = a

a.foo = 'bar'
alert(b.foo) // alerts 'undefined'

a = new Object()
b = a

a.foo = 'bar'
alert(b.foo) // alerts 'bar'


What if I want to create a pointer to a string, a number, or a boolean?


alert(typeof('a')) // alerts 'string'
alert(typeof(true)) // alerts 'boolean'
alert(typeof(1)) // alerts 'number'


So, assigning those values will result in a copy, not a reference. But:


alert(typeof(new String('a'))) // alerts 'object'
alert(typeof(new Boolean(true))) // alerts 'object'
alert(typeof(new Number(1))) // alerts 'object'


What does this mean? It means you can create pointers to JavaScript primitives. This answers my first question. If typeof(value) === 'object' but value is a number, a string, or a boolean value, then you've got yourself a pointer to an object. If you want to assign your object's value to a variable, sans pointer, just use value.valueOf().

You can also check whether some value is a pointer or primitive using instanceof:


alert('a' instanceof String) // alerts false
alert(true instanceof Boolean) // alerts false
alert(1 instanceof Number) // alerts false
alert(new String('a') instanceof String) // alerts true
alert(new Boolean(true) instanceof Boolean) // alerts true
alert(new Number(1) instanceof Number) // alerts true


The below function makes it easy to point to primitive values.


function point(value) { // return a pointer to some primitive value
switch( typeof(value) ) {
case 'string': case 'number': case 'boolean':
return new value.constructor(value)
}
return value
}


Use it like this:


var
my_string = point('a'),
my_string_alias = my_string

my_string.foo = 'bar'
alert(my_string_alias.foo) // alerts 'bar'


It should be noted, that if you pass a variable containing a primitive into point, you will not get a pointer to that primitive; a new object will be created with the value you passed in. Here's an example:


var
my_string = 'a',
my_string_alias = point(my_string)

my_string_alias.foo = 'bar'

alert(my_string.foo) // alerts 'undefined'


One way that this can be resolved is by a simple reassignment of the original variable to the new object:


var
my_string = 'a',
my_string_alias = my_string = point(my_string)

my_string_alias.foo = 'bar'

alert(my_string.foo) // alerts 'bar'


Ok, so now that that has been cleared up, what about my second question: why is typeof(null) === 'object' true? The only thing I can think is that null is a pointer, not a primitive value, and that when typeof returns 'object', it means that the passed in value is a pointer, not a primitive value. Perhaps when I have some time I'll download the TraceMonkey source and grep through the logic in typeof to verify this, but its the best explanation I've found thus far; many JavaScripters seem to consider typeof broken because it returns "object" when a null value is passed in. What's the use all of this information? Well, one use is that we can use this knowledge to check if some value is iterable:


function is_iterable ( value ) {
return value !== null && typeof( value ) === 'object'
}


If is_iterable returns true for some variable or value that you pass in, you are assured that for (var x in value) loops and x in value statements on that value will execute without throwing a TypeError.

I'm sure more knowledgeable JavaScripters than I might take all of this information for granted, but I was unable to find any informed resources on this particular subject in my googlings. I hope that you've found it useful.

1 comment:

macci said...

Hello elijahr,

It was really a great piece of information. I was a bit confused with the difference in behavior of arguments when passed through a function and your post has helped me to sort them out.

Thank you :)

About Me

My photo
chicago, il, United States
I'm a software engineer by profession.

Labels