Saturday, February 22, 2014

Falsy Values in Javascript

Javascript has a lot of truthy and falsy values. It can be really quite confusing, because this means your code will fail conditionals it shouldn't, and pass conditionals it shouldn't. It takes a little bit of discipline to ensure your code is always returning the right type. Here are some of my notes on the matter.

Truthy values in Javascript:

True
true

Empty object
{}

Empty list
[]

Non-empty string
"Hello, world"

Non-zero numbers
42

... (there are others, mostly variants on objects)


There are only six  falsy values in  Javascript, however:

False
false

Null object
null

Undefined
undefined

NaN
NaN

Zero
0

Empty string
""

There are some very odd things about these lists - the numbers and boolean keywords aside. Like, why are empty strings falsy, but empty arrays aren't? What's a string if not an array? Why aren't empty arrays and empty objects falsy? Why do we have undefined AND null?

In Javascript, there are a lot of ways to introduce bugs, and unfortunately, simple boolean algebra is one of them. Each of these values is truthy or falsy for a reason, but more importantly, they have vastly different use cases.

Empty objects and arrays are truthy because they are both objects. Objects instantiated with the array initializer will have a length attribute and "methods" for pushing, popping, reversing, and so on (because their prototype is the built in Array object). But, the Array object inherits from the base Object, so empty arrays are objects, and all objects in Javascript are truthy - they represent the existence of "something".

Empty strings, however, are an exception to this rule, as part of a conscious design choice. The exact reasoning is beyond my knowledge, but it makes sense to view empty strings as an exception to the rule that an object implies the existence of meaningful information. Not sure it's a decision I'd make, though.

A lot of problems happen because of undefined. Undefined is NOT null, and there is a reason for having both values. It is very important that programmers do not misuse these values, because it will quickly become impossible to determine where bugs are coming from and what the nature of them is.

When a name's value is null, that means it has no defined value. It is not an empty object, zero, the empty list, an empty string - it is undefined, unknown to Javascript. It should be returned when a function is asked for an object and no proper object has been found, or when an identifier needs to be declared but no object for it is available yet (like declaring "class members").

When a name is undefined, that means the name itself, the label, has never been defined in the current scope. It's not that the identifier doesn't have a value, it's that the identifier doesn't exist. There's a difference between a.x = null when x is defined, but not initialized, and a.x when, nowhere inside of a, is there anything with the name x.

In general, you should never, ever return undefined from a function. It's a language feature telling you that there is something fundamentally wrong going on, not a falsy value to let some other function deal with. Null, however, should be returned frequently; every time an object should be returned and nothing could be found, you should return null, not undefined.

Why would you return undefined accidentally? Excellent question. Undefined is the default return type for functions! If you have a void function, it's actually going to return undefined. This means if you don't make sure that every code path contains an explicit return, your function will sometimes return undefined, and sometimes not, which absolutely violates the rule about returning null if no valid answer could be found!

Unfortunately, some (many) of the built-in functions are not insulated against this. Array.pop() returns undefined if the array is empty, rather than null, for example. There's really no solution besides buckling up and learning how to be extremely careful.

NAN is confusing too, not because it's falsy, but because it doesn't equal itself. This makes perfect sense; NaN means "Not a Number", and it means that somewhere, you did a bad in your arithmetic, and wound up with something Javascript can't use. Since it's impossible to know that one non-number equals another, NaN === NaN returns false. To get around this, use the isNaN() function!

A final note: remember to use ===, not ==! This is an example of why:

var arr = [];
(arr == false) //true
(arr === false) //false
!!arr //true

This is because it evaluated arr and false to strings in the "==" comparison, and the empty string is false! BE CAREFUL.


Next post will be about why arithmetic in Javascript is terrifying.







No comments:

Post a Comment

Feel free to comment. If it's spam, I'll remove it. If spam becomes a problem, I'm going to change the comment policy. Till then, it's the wild west.