Debugging Web(2) - 10 Pitfalls for the JavaScript Developers

web

Strictly speaking, this post does not belong to this category. But on the other hand, the following pitfalls are usually the evil source.

Note: the code is intentionally to put in one line, just for your convenience to test it in the FireBug.

1. Unexpected global variables

Declare a variable via var at any time, whether this variable is in the global scope or local scope. Any variable without declaration resides in the global scope, polluting the global namespace.

2. Caution: Boolean operation ahead

JavaScript is extremely tolerate for the type checking, raising an exception is the last thing the interpreter wants to do. The inherent conversion may surprise you especially for the C/C++ programmer. For example:

if (new Boolean(false)) alert("true");

Although the constructed Boolean object is false, the if expression returns true. Why? Because the Boolean is an object, if it is not null, the expression returns true.

Here is another example used in the vararg functions: if (arg5 != null); C/C++ programmer may jump to the old trick: if (p). Oops, this expression could not differentiate whether p is null or 0 or "". JavaScript: The Definitive Guide, 5th Edition Section 5.4 disclose the conversion rule for the == operator:

If the two values have the same type, test them for identity. If the values are identical, they are equal; if they are not identical, they are not equal.

If the two values do not have the same type, they may still be equal. Use the following rules and type conversions to check for equality:

If one value is null and the other is undefined, they are equal.

If one value is a number and the other is a string, convert the string to a number and try the comparison again, using the converted value.

If either value is TRue, convert it to 1 and try the comparison again. If either value is false, convert it to 0 and try the comparison again.

If one value is an object and the other is a number or string, convert the object to a primitive and try the comparison again. An object is converted to a primitive value by either its toString( ) method or its valueOf( ) method. The built-in classes of core JavaScript attempt valueOf( ) conversion before toString( ) conversion, except for the Date class, which performs toString( ) conversion. Objects that are not part of core JavaScript may convert themselves to primitive values in an implementation-defined way.

Any other combinations of values are not equal.

typeof operator is also a safe approach, like if (typeof(arg5) == "undefined")

3. valueOf vs. toString

When JavaScript convert an object to string, the toString function is hidden by valueOf, the valueOf is evaluated, then convert the result to string, like this:

>>> function Foo() {}; Foo.prototype.valueOf = function() { return 3;}; Foo.prototype.toString = function() { return "bar";}; var x = "foo" + new Foo(); x;
"foo3"

The only exception is Date, the toString is used in + operator, while valueOf is used in the comparison.

4. Immutable String

JavaScript takes this approach to pass the argument to the function: pass by value for primitive data types, pass by reference for objects. String is regarded as the object, but passed by the value:

>>> function foo(s) { s[0] = 't';}; var x="hello"; foo(x); x;
"hello"

5. Pervasive scope

For C/C++ developer, the declared variable take effects after its declaration, like this:

#include <stdio.h>
int x = 9;

int foo() {
    printf("x =%d\n", x);
    int x = 10;
    printf("x =%d\n", x);
    return 0;
}

int main() {
    return foo();
}

the output is

x =9
x =10

While in JavaScript, once the variable is declared, it is populated to the whole closure, for instance:

var x = 9;
function foo() {
  alert(x);
  var x = 10;
  alert(x);
}
foo();

The alerts are undefined and 10. The global value x is shadowed by the local variable x. Local variable x is declared in this closure, but not assigned before the first alert, so its value is undefined.