Three JavaScript Quirks That Java/C Developers Should Know

JavaScript can be a deceiving language and it can be a real pain because it isn’t 100% consistent. As it’s well known it does have bad parts, confusing or redundant features that should be avoided: the infamous with statement, implicit global variables and comparison erratic behavior are probably the best known.

JavaScript is one of the most successful flames generator in history! Besides the flaws it has (which are, in part, addressed in the new ECMAScript specifications), most programmers hate JavaScript for 2 reasons:

  • The DOM, which they erroneously think is equivalent to the JavaScript language, which has quite a terrible API.
  • They come to JavaScript from languages like C and Java. They are fooled by JavaScript’s syntax into believing that it works the same way as those imperative languages do. This misconception will lead to confusion, frustration, and bugs.

That’s why, generally, JavaScript has a worse reputation than it deserves.

During my career, I noticed a few patterns: language features most developers with a Java or C/C++ background assume to be identical in JavaScript, while they are completely different.

This article gathers the most troublesome ones, comparing the Java-way to the JavaScript-way to shown differences and highlight best practices in JavaScript.

Scoping

Most developers start working on JavaScript because they are forced, and almost every of them start writing code before taking a moment to learn the language. Every such developer has been tricked by JavaScript scope at least once.

Because JavaScript’s syntax closely resembles (on purpose) C-family languages, with curly braces delimiting function‘s, if‘s and for‘s bodies, one would reasonably expect lexical block-level scope. Unfortunately, this is not the case.

First, in JavaScript variable scope is determined by functions, not by brackets. In other words, if and for bodies don’t create a new scope, and a variable declared inside their bodies is actually hoisted, i.e. created at the beginning of the innermost function in which it is declared, or of the global scope otherwise.

Second, the presence of the with statement forces JavaScript scoping to be dynamic, impossible to determine until runtime. You might not be surprised to hear that the use of the with statement is deprecated: JavaScript stripped of with would actually be a lexically scoped language, i.e. the scope could be completely determined by looking at the code.

Formally, in JavaScript there are four ways for a name to enter a scope:

  • Language-defined: by default all scopes contains the names this and arguments.
  • Formal parameters: any (formal) parameters declared for a function is scoped to the body of that function.
  • Function declarations.
  • Variable declarations.

One further complication is caused by the implicit global scoping assigned to variables declared (implicitly) without the var keyword. This madness pairs with the implicit assignment of the global scope to this reference when functions are called without an explicit bind (more on this in next sections).

Before delving into the details, let’s clearly state the good pattern that can be used to avoid confusion:

Use strict mode ('use strict';), and move all variables and functions declaration at the top of each function; avoid variables declaration inside for and if blocks, as well as function declarations inside those blocks (for different reasons, that goes beyond the scope of this article).

Hoisting

Hoisting is a simplification that is used to explain the actual behavior of declarations. Hoisted variables are declared at the very beginning of the function containing them, and initialized to undefined. Then assignment takes place in the actual line where the original declaration was.

Take a look at the following example:

function myFunction() {
  console.log(i);
  var i = 0;
  console.log(i);
  if (true) {
    var i = 5;
    console.log(i);
  }
  console.log(i);
}

What values do you expect to be printed to the console? Would you be surprised to the following output?

undefined
0
5
5

Inside the if block, the var statement doesn’t declare a local copy of the variable i, but rather overwrites the one declared before. Notice that the first console.log statement prints the actual value of variable i, which is initialized to undefined. You can test it by using the "use strict"; directive as the first line in the function. In strict mode variables must be declared before being used, but you can check that JavaScript engine won’t complain for the declaration. On a side note, be aware that you will get no complain for redeclaring a var: if you want to catch such bugs, you should better process your code with a linter such JSHint or JSLint.

Let’s now see one more example to highlight another error-prone use of variable declarations:

var notNull = 1;
function test() {
  if (!notNull) {
    console.log("Null-ish, so far", notNull);
    for(var notNull = 10; notNull <= 0; notNull++){
      //..
    }
    console.log("Now it's not null", notNull);
  }
  console.log(notNull);
}

Despite you might expect differently, the if body is executed because a local copy of a variable named notNull is declared inside the test() function, and it is hoisted. Type coercion also plays a role here.

Function Declarations vs Function Expressions

Hoisting doesn’t apply to variables only, function expressions, which are variables to all intents and purposes, and function declarations are hoisted as well. This topic needs to be treated with way more care than I’ll do here, but in short function declarations behaves mostly as function expressions, except that their declarations are moved to the beginning of their scope.

Consider the following example showing the behavior of a function declaration:

function foo() {
    // A function declaration
    function bar() {
        return 3;
    }
    return bar();

    // This function declaration will be hoisted and overwrite the previous one
    function bar() {
        return 8;
    }
}

Now, compare it with this example that shows the behavior of a function expression:

function foo() {
    // A function expression
    var bar = function() {
        return 3;
    };
    return bar();

    // The variable bar already exists, and this code will never be reached
    var bar = function() {
        return 8;
    };
}

See the references section for further insight on these concepts.

With

The following example shows a situation where scoping can only be determined at runtime:

function foo(y) {
  var x = 123;
  with(y) {
    return x;
  }
}

If y has a field named x, then function foo() will return y.x, otherwise it will return 123. This coding practice is a possible source of runtime errors, hence it’s highly recommended that you avoid using the with statement.

Looking at the Future: ECMAScript 6

ECMAScript 6 specifications will add a fifth way to add block-level scoping: the let statement. Consider the code below:

function myFunction() {
  console.log(i);
  var i = 0;
  console.log(i);
  if (false) {
    let i = 5;
    console.log(i);
  }
  console.log(i);
}

In ECMAScript 6, declaring i with let inside the body of the if will create a new variable local to the if block. As a non-standard alternative, it’s possible to declare let blocks as follows:

var i = 6;
let (i = 0, j = 2) {
  /* Other code here */
}
// prints 6
console.log(i);

In the code above, the variables i and j will exist only inside the block. At the time of writing, the support for let is limited, even for Chrome.

Scope in a Nutshell

The next table summarizes the scope in different languages:

Feature Java Python JavaScript Warnings
Scope Lexical (block) Lexical (function, class or module) Yes It works very differently from Java or C
Block scope Yes No `let` keyword (ES6) Again, warning: this is not Java!
Hoisting No way! No Yes For variables and function expressions, only declaration is hoisted. For function declarations, the definition is hoisted as well

Functions

Another very misunderstood feature of JavaScript are functions, especially because in imperative programming languages like Java there is no such a concept as a function.

As a matter of facts, JavaScript is a functional programming language. Well, not a pure functional programming language as Haskell – after all it still has an imperative style, and mutability is encouraged rather than simply allowed, as for Scala. Nevertheless JavaScript could be used as a purely functional programming language, with function calls deprived of any side effect.

First-Class Citizens

Functions in JavaScript can be treated like any other type, for example String and Number: they can be stored in variables, passed as arguments to functions, returned by functions, and stored in arrays. Functions can also have properties and can be changed dynamically and that’s because…

Objects

One very surprising fact, for most JavaScript newbies, is that functions are actually objects. In JavaScript every function is actually a Function object. The Function constructor creates a new Function object:

var func = new Function(['a', 'b', 'c'], '');

This is (almost) equivalent to:

function func(a, b, c) { }

I said they are almost equivalent because using the Function constructor is less efficient, produces an anonymous function, and do not create a closure to its creation context. Function objects are always created in the global scope.

Function, the type of functions, is built upon Object. This can be easily seen by inspecting any function you declare:

function test() {}
//  prints  "object"
console.log(typeof test.prototype);
//  prints  function Function() { [native code] }
console.log(test.constructor);

This means that functions may and do have properties. Some of them are assigned to the functions on creation like name or length. These properties return the name and number of arguments in the function definition respectively.

Consider the following example:

function func(a, b, c) { }
//  prints "func"
console.log(func.name);
//  prints 3
console.log(func.length);

But you can even set new properties for any function by yourself:

function test() {
  console.log(test.custom);
}
test.custom = 123;
//  prints 123
test();

Functions in a Nutshell

The following table describes functions in Java, Python, and JavaScript:

Feature Java Python JavaScript Warnings
Functions as built-in types Lambdas, Java 8 Yes Yes
Callbacks / Command Pattern Objects (or lambdas for Java 8) Yes Yes Functions (callbacks) have properties that can be modified by the “client”
Dynamic creation No No `eval` – `Function` object `eval` has security concerns and `Function` objects might work unexpectedly
Properties No No Can have properties Access to function’s properties can’t be restricted

Closures

If I had to choose my favorite JavaScript feature, I’d go for closures, no doubt. JavaScript was the first mainstream programming language to introduce closures. As you might know, Java and Python have had a weakened version of closures for a long time, where you could only read (some) values from enclosing scopes.

In Java, for instance, anonymous inner class provides closure-like functionality with some restrictions. For example, only final local variables can be used in their scope – better said, their values can be read.

JavaScript allows full access to the outer scope variables and functions. They can be read, written, and if needed even hidden by local definitions: you can see examples of all these situations in the “Scoping” section.

Even more interesting, a function created in a closure remembers the environment in which it was created. By combining closures and function nesting, you can have outer functions returning inner functions without executing them. Besides, you can have local variables of the outer function surviving in the closure of the inner one long after the execution of the function in which they are declared has ended. This is a very powerful feature but it has also its drawback as it’s a common cause of memory leaks in JavaScript applications.

A few examples will clarify these concepts:

function makeCounter () {
  var i = 0;

  return function displayCounter () {
    console.log(++i);
  };
}
var counter = makeCounter();
//  prints 1
counter();
//  prints 2
counter();

The makeCounter() function above creates and returns another function that keeps track of the environment in which it’s created. Although the execution of makeCounter() is over when the variable counter is assigned, the local variable i is kept in displayCounter‘s closure, and can be therefore accessed inside its body.

If we were to run makeCounter again, it would create a new closure, with a different entry for i:

var counterBis = makeCounter();
//  prints 1
counterBis();
//  prints 3
counter();
//  prints 2
counterBis();

To make it a bit more interesting, we could update the makeCounter() function so that it takes an argument:

function makeCounter(i) {
  return function displayCounter () {
    console.log(++i);
  };
}
var counter = makeCounter(10);
//  prints 11
counter();
//  prints 12
counter();

Outer function arguments are kept in the closure as well, so we don’t need to declare a local variable this time. Every call to makeCounter() will remember the initial value we set, and count on.

Closures are paramount for many fundamental JavaScript patterns: namespacing, module, private vars, memoization are just the best known.

As an example, let’s see how we can simulate a private variable for an object:

function Person(name) {
  return {
    setName: function(newName) {
      if (typeof newName === 'string' && newName.length > 0) {
        name = newName;
      } else {
        throw new TypeError("Not a valid name");
      }
    },
    getName: function () {
      return name;
    }
  };
}

var p = Person("Marcello");

// prints "Marcello"
a.getName();

// Uncaught TypeError: Not a valid name
a.setName();

// Uncaught TypeError: Not a valid name
a.setName(2);
a.setName("2");

// prints "2"
a.getName();

With this pattern, exploiting closures, we can create a wrapper for a property name, with our own setter and getter. ES5 made this a lot easier, since you can create objects with getters and setters for their properties, and control access to the properties themselves at the finest grain.

Closures in a Nutshell

The following table describes closure in Java, Python, and JavaScript:

Feature Java Python JavaScript Warnings
Closure Weakened, read-only, in anonymous inner classes Weakened, read-only, in nested def Yes Memory leaks
Memoization Pattern Must use shared objects Possible using lists or dictionaries Yes Better use lazy evaluation
Namespace/Module Pattern Not needed Not needed Yes
Private Attributes Pattern Not needed Not possible Yes Might get confusing

Conclusion

In this article I covered three features of JavaScript that are often misunderstood by developers coming from different languages, especially Java and C. In particular, we’ve discussed concepts as scoping, hosting, functions, and closures. In case you want to study in deep these topics, here is a list of articles you can read:


Sponsors

No Reader comments