The results of type coercion in JavaScript can be difficult to predict, even when you know the rules. In this post, we will see how subtle differences in comparisons, can lead to significant changes in the results. Be careful when relying on 'truthyness' for logic. Whenever possible, use comparison operators to clarify your meaning. 'Falsy' values include:
0
undefined
null
empty strings
false
CAUTION - Boolean objects instantiated with new Boolean(false) are 'truthy' because they're objects, and objects are truthy! In order to test against them, you need to use the .valueOf() method. It's better in almost all cases to use true and false, instead.
Empty objects and empty arrays are truthy, including objects created with new Boolean(false).
Wrong: Don't use new Boolean()
var myBool = new Boolean(false);
test('Boolean object', function () {
ok(!myBool, 'Should be falsy'); // Fails
ok(!myBool.valueOf(),
'Should be falsy.'); // Passes
});
Right: Use true or false in your boolean declarations
var myBool = false;
test('Boolean object', function () {
ok(!myBool, '!myBool should be false.');
});
The following series of tests are meant to demonstrate how different types of comparisons in JavaScript that look similar can deliver very different results.
Always be careful that you're using the correct test for your situation:
function truthy(x) {
if (x) {
return true;
} else {
return false;
}
}
test('Truthy', function () {
// Falsy
equal(truthy(0), true, 'truthy(0)'); // Fail
equal(truthy(''), true, "truthy('')"); // Fail
equal(truthy(null), true, 'truthy(null)'); // Fail
equal(truthy(undefined), true,
'truthy(undefined)'); // Fail
equal(truthy(false), true, 'truthy(false)'); // Fail
// Truthy
equal(truthy('0'), true, "truthy('0')"); // Pass
equal(truthy(new Boolean(false)), true,
'truthy(new Boolean(false))'); // Pass
equal(truthy({}), true, 'truthy({})'); // Pass
equal(truthy([]), true, 'truthy([])'); // Pass
equal(truthy([0]), true, 'truthy([0])'); // Pass
equal(truthy([1]), true, 'truthy([1])'); // Pass
equal(truthy(['0']), true, "truthy(['0'])"); // Pass
equal(truthy(['1']), true, "truthy(['1'])"); // Pass
});
These are the falsy and truthy values we'll use as a baseline for a series of other comparisons.
CAUTION - Often, developers will use if (x) when they really want to see if the value has been set at all. This is especially problematic when 0, empty strings, or false are valid values. In that case, you want the test to pass as long as the expression evaluates to anything other than null or undefined. A good rule of thumb is to only use if (x) to evaluate booleans or the existence of objects or arrays.
function exists(x) {
if (x !== undefined && x !== null) {
return true;
} else {
return false;
}
}
test('exists', function () {
// Falsy
equal(exists(0), true, 'exists(0)'); // Pass
equal(exists(''), true, "exists('')"); // Pass
equal(exists(null), true, 'exists(null)');
equal(exists(undefined), true, 'exists(undefined)');
equal(exists(false), true, 'exists(false)'); // Pass
// Truthy
equal(exists('0'), true, "exists('0')"); // Pass
equal(exists(new Boolean(false)), true,
'exists(new Boolean(false))'); // Pass
equal(exists({}), true, 'exists({})'); // Pass
equal(exists([]), true, 'exists([])'); // Pass
equal(exists([0]), true, 'exists([0])'); // Pass
equal(exists([1]), true, 'exists([1])'); // Pass
equal(exists(['0']), true, "exists(['0'])"); // Pass
equal(exists(['1']), true, "exists(['1'])"); // Pass
});
Of course, a shorter version of that function will return the same results:
function exists(x) {
return (x !== undefined && x !== null);
}
CAUTION - The == operator can return some problematic results due to type coercion.
For example, say you're checking an array to be sure it contains a valid price, and some items cost $0.00. Your code could fail because ['0.00'] == false. Use === instead.
var isFalse = function isFalse(x) {
return (x == false);
};
test('isFalse using ==', function () {
// Falsy
equal(isFalse(0), true, 'isFalse(0)'); // Pass
equal(isFalse(''), true, "isFalse('')"); // Pass
equal(isFalse(null), true, 'isFalse(null)'); // Fail
equal(isFalse(undefined), true, 'isFalse(undefined)'); // Fail
equal(isFalse(false), true, 'isFalse(false)'); // Pass
// Truthy
equal(isFalse('0'), true, "isFalse('0')"); // Pass
equal(isFalse(new Boolean(false)), true,
'isFalse(new Boolean(false))'); // Pass
equal(isFalse({}), true, 'isFalse({})'); // Fail
equal(isFalse([]), true, 'isFalse([])'); // Pass
equal(isFalse([0]), true, 'isFalse([0])'); // Pass
equal(isFalse([1]), true, 'isFalse([1])'); // Fail
equal(isFalse(['0']), true, "isFalse(['0'])"); // Pass
equal(isFalse(['1']), true, "isFalse(['1'])"); // Fail
});
The following code will only return true if x is actually set to false:
var isFalse = function isFalse(x) {
return (x === false);
};
The following function is dangerous, because it will return true for 1, [1], and ['1'].
var isTrue = function isTrue(x) {
return (x == true);
};
Use === instead to eliminate the possibility of false positives.
Remember that if (x) will always return true when x is an object -- even if the object is empty. It's common to use ducktyping when you examine objects. (If it walks like a duck and talks like a duck, treat it like a duck.) The idea is similar to using feature detection in browsers, instead of running checks against the browser string. Ducktyping is feature detection for objects.
if (typeof foo.bar === 'function') {
foo.bar();
}


4 comments:
Bottom line: if you need to perform truthy/falsy evaluation, don't rely on coercion. It's not a handy shortcut, it's a pitfall, so if you need true/false values, write your code in such a way that once you hit your conditional, you can rigorously perform === true or === false checks.
Yep! Well said.
Thanks for sharing useful information. I always make sure to bookmark pages like this because you know it will be useful in the future too. thanks again.
Website Design Perth
This is a Great information..thanx! Web Design Portland
Post a Comment