Chaining Variable Assignments in JavaScript: Words of Caution

Declaring multiple variable expressions on a single line is common practice and also a great shorthand syntax.

However, while reviewing several developer’s work recently, I noticed many are not aware this creates implicit global variables.

(function() {
    var foo = bar = baz = 'local';
    console.log(foo); // local
    console.log(bar); // local
    console.log(baz); // local
}());

Can you spot the problem? Try the following:

var bar = 'funky';
var baz = 'disco';

(function helloworld() {
    var foo = bar = baz = 'local';
    console.log(foo); // local
    console.log(bar); // local
    console.log(baz); // local
}());

console.log(bar);// local, I thought it would be funky
console.log(baz);// local, I thought it would be disco

Multiple variable chaining on the same line creates a leak to the global namespace. Many developers are surprised that local bar and bazinside helloworld() has overwritten global bar and baz.

Why does this happen?

This is because of how operator associativity works in JavaScript, or more simply how operators with the same precedence are evaluated. The interpreter evaluates the = operator as right-to-left associativity.

Therefore the line:

var foo = bar = baz = 'local';

Is easier understood as:

var foo = ( bar = ( baz = 'local' ) );

This is evaluated as baz = 'local', baz has not been declared locally so check the Scope Chain we find baz was declared globally.

The result of this expression is local, which is returned to the next operator bar = 'local'. bar is not declared locally, so look to the Scope Chain or assign it to the global namespace and return the value local.

Finally, var foo = 'local' sees there is no operator preceding foo but the keyword var, so creates a local variable named foo and assigns it the value local.

The Solution

To avoid the global leak, we simply need to separate the variable declarations from assignments:

var bar = 'funky';
var baz = 'disco';

(function helloworld() {

    // method 1
    var foo, bar, baz;
    foo = bar = baz = 'local';

    // or method 2
    var foo = 'local';
    var bar = 'local';
    var baz = 'local';

    // or method 3
    var foo = 'local',
        bar = 'local',
        baz = 'local';

    console.log(foo); // local
    console.log(bar); // local
    console.log(baz); // local

}());

console.log(bar); // funky
console.log(baz); // disco

Now this is better, the local variables are contained in their scope and are completely hidden from the global namespace.

Summary

I actually think this pattern looks quite ugly, having to separate declarations from assignments, but since it stops implicit globals then I’m all for it.

Hopefully after this article I won’t be stumbling across quite as many global namespace leaks in the future!

Comments

  1. As the developer of a set of code, I love this shorthand syntax. As a reader of other’s code, I dislike it. Great article and good to know! Thanks.

    Reply
  2. Good spot. Thanks for sharing that. I usually prefer to make my code readable and I don’t use shorthand syntax for creating variables, but it’s worth to know.

    Reply
  3. Another way of writing it with semi-shorthand syntax


    var bar = 'funky';
    var baz = 'disco';
    (function helloworld() {
    var foo = 'local', bar = 'local', baz = 'local';
    console.log(foo); // local
    console.log(bar); // local
    console.log(baz); // local
    }());
    console.log(bar);// funky
    console.log(baz);// disco

    Reply
  4. Reminded me that I like to use the following pattern:


    MyObject.prototype.method = function(){
    var something
    = this.something
    = getSomethingFromSomwhere();
    // use something instead of this.something
    }

    Reply
  5. // method 4: reuse value of first var

    var bar = 'funky';
    var baz = 'disco';
    (function helloworld() {
    var foo = 'local', bar = foo, baz = foo;
    console.log(foo); // local
    console.log(bar); // local
    console.log(baz); // local
    }());
    console.log(bar); // funky
    console.log(baz); // disco

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *


eight × = 16

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>