HTML5 Zone is brought to you in partnership with:

Dr. Axel Rauschmayer is a freelance software engineer, blogger and educator, located in Munich, Germany. Axel is a DZone MVB and is not an employee of DZone and has posted 246 posts at DZone. You can read more from them at their website. View Full User Profile

Major and Minor JavaScript Pitfalls and ECMAScript 6

02.23.2012
| 5505 views |
  • submit to reddit
JavaScript has many pitfalls. This post partitions them into two categories: The ones that you learn and live with and the ones that really are bad. We’ll also look at what will be fixed in ECMAScript 6. Warning: If you are new to JavaScript then don’t let this post be your introduction to it. Consult other material first.

Major JavaScript pitfalls

There are two pitfalls in JavaScript that frequently trip up even experienced programmers:

  • Dynamic this. Using a function inside a method is problematic, because you can't access the method’s this. This might be fixed in ECMAScript 6 via block lambdas (that don’t have their own this):
    var obj = {
        name: "Jane",
        friends: ["Tarzan", "Cheeta"],
        printFriends: function () {
            // forEach argument is block lambda => can omit parentheses
            this.friends.forEach {
                | friend |
                console.log(this.name+" knows "+friend);
            }
        }
    }
    
  • Subtyping is difficult. While implementing a single type via a constructor is something that can be learned, creating a sub-constructor is too complicated [1]. As a result, numerous inheritance libraries have sprung up that lead to greatly varying coding styles, making life unnecessarily hard for humans and IDEs. And there is not that much that libraries can do about super-calls. ECMAScript 6 will probably bring us some kind of inheritance operator and super-calls [2]:
    var Employee = Person <| function (name, title) {
        super.constructor(name);
        this.title = title;
    }
    Employee.prototype .= {
        describe() {
            return super.describe() + " (" + this.title + ")";
        }
    };
    Additionally, there might be class literals which would make things even simpler.

Minor JavaScript pitfalls

Apart from the big ones, there are several minor pitfalls that people frequently complain about. It is obviously not ideal that those exist, but you can learn and accept them. And once you have, they are unlikely to trip you up in the future. The following list is not exhaustive, but covers the more ugly ones.

  • No block scoping. JavaScript’s var statement is function-scoped [3], even in nested blocks, the variables declared by it exist in the complete (innermost enclosing) function. You quickly learn to use a pattern called immediately-invoked function expression (IIFE). Not pretty, but it works.
    function createInterval(start, end) {
        if (start > end) {
            (function () {  // IIFE: open
                var tmp = start;
                start = end;
                end = tmp;
            }());  // IIFE: close
        }
        return [start, end];
    }
    ECMAScript 6 will have block-scoped variables via the let statement. And destructuring assignment helps you simplify the above code:
    function createInterval(start, end) {
        if (start > end) {
            [start, end] = [end, start];
        }
        return [start, end];
    }
  • Extracted methods can’t use this. You have to learn to use bind(). Example:
    function repeat(n, func) {
        for(var i = 0; i < n; i++) {
            func();
        }
    }
    var counter = {
        count: 0,
        inc: function () {
            this.count++;
        }
    }
    repeat(2, counter.inc.bind(counter));
  • Creating global variables via this. Before ECMAScript 5, if you made a function call to a constructor (because you forgot new) or a method (see previous item), then this would lead to global variables being read or created:
    > function Point(x,y) { this.x = x; this.y = y }
    > Point(12, 7)
    undefined
    > x
    12
    
    ECMAScript 5 strict mode [4] fixes that problem:
    > function Point(x,y) { "use strict"; this.x = x; this.y = y }
    > Point(12, 7)
    TypeError: Cannot set property 'x' of undefined
  • Automatic creation of globals. Before ECMAScript 5, writing to a variable that didn’t exist, yet, automatically created a global variable:
    > function f() { foobar = "hello" }
    > f()
    > foobar
    'hello'
    With strict mode, you get an error:
    > function f() { "use strict"; foobar = "hello" }
    > f()
    ReferenceError: foobar is not defined
  • Comparison via == is weird. There is a whole corpus of strange things you can do with ==. Simple solution: Don’t use it. Ever [5]. The strict equality operator === is very reliable.
  • for...in is weird. It iterates over an object’s inherited (enumerable) properties, it iterates over all of an array’s (enumerable) properties (not just the array elements). That is, it depends in a very fragile way on the enumerability of properties to exhibit the expected behavior. Solution: Don’t use it [6]. Use forEach for arrays.
    var arr = [ "apple", "pear", "orange" ];
    arr.forEach(function(elem) {
        console.log(elem);
    });
  • To iterate over the property names of an object, you can use:
    Object.keys({ first: "John", last: "Doe" }).
    forEach(function (propName) {
        console.log(propName);
    });
  • Array-like objects. Some objects in JavaScript are array-like, they have a length property and indexed access, but none of the array methods. You therefore need to borrow array methods via call(). The special variable arguments is array-like:
    function printArguments() {
        console.log(Array.prototype.join.call(arguments, "; "));
    }
    Not pleasant, but learnable. And you get errors quickly if you are doing something wrong. At least in ECMAScript 6, you won’t have to use arguments, any more:
    function printArguments(...args) {
        console.log(args.join("; "));
    }
    As a side benefit, function arity can now be meaningfully checked.
  • truthy and falsy values; both undefined and null. Yes, not terribly elegant. Get over it. ECMAScript 6 might have operators that allow you to check for either undefined or null. Furthermore, one will be able to define default values for missing parameters, obviating one major use case for this kind of check.


Source: http://www.2ality.com/2012/02/js-pitfalls.html
Published at DZone with permission of Axel Rauschmayer, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)