This article is an excerpt from Closure: The Definitive Guide, which discusses many aspects of JavaScript in the course of detailing how to leverage the Google Closure Tools to build large-scale JavaScript applications.

Frequently Misunderstood JavaScript Concepts


This is a complete "reprint" of Appendix B from my book, Closure: The Definitive Guide. Even though my book was designed to focus on Closure rather than JavaScript in general, there were a number of pain points in the language that I did not think were covered well in other popular JavaScript books, such as JavaScript: The Good Parts or JavaScript: The Definitive Guide. So that the book did not lose its focus, I relegated this and my essay, "Inheritance Patterns in JavaScript," to the back of the book among the appendices. However, many people have told me anecdotally that Appendix B was the most valuable part of the book for them, so it seemed like this was worth sharing more broadly in hopes that it helps demystify a language that I have enjoyed so much.

This book is not designed to teach you JavaScript, but it does recognize that you are likely to have taught yourself JavaScript and that there are some key concepts that you may have missed along the way. This section is particularly important if your primary language is Java as the syntactic similarities between Java and JavaScript belie the differences in their respective designs.

JavaScript Objects are Associative Arrays whose Keys are Always Strings

Every object in JavaScript is an associative array whose keys are strings. This is an important difference from other programming languages, such as Java, where a type such as java.util.Map is an associative array whose keys can be of any type. When an object other than a string is used as a key in JavaScript, no error occurs: JavaScript silently converts it to a string and uses that value as the key instead. This can have surprising results:

var foo = new Object();
var bar = new Object();
var map = new Object();

map[foo] = "foo";
map[bar] = "bar";

// Alerts "bar", not "foo".
alert(map[foo]);

In the above example, map does not map foo to "foo" and bar to "bar". When foo and bar are used as keys for map, they are converted into strings using their respective toString() methods. This results in mapping the toString() of foo to "foo" and the toString() of bar to "bar". Because both foo.toString() and bar.toString() are "[object Object]", the above is equivalent to:

var map = new Object();
map["[object Object]"] = "foo";
map["[object Object]"] = "bar";

alert(map["[object Object]"]);

Therefore, map[bar] = "bar" replaces the mapping of map[foo] = "foo" on the previous line.

There are Several Ways to Look Up a Value in an Object

There are several ways to look up a value in an object, so if you learned JavaScript by copy-and-pasting code from other web sites, it may not be clear that the following code snippets are equivalent:

// (1) Look up value by name:
map.meaning_of_life;

// (2) Look up value by passing the key as a string:
map["meaning_of_life"];

// (3) Look up value by passing an object whose toString() method returns a
// string equivalent to the key:
var machine = new Object();
machine.toString = function() { return "meaning_of_life"; };
map[machine];

Note that the first approach, "Look up value by name," can only be used when the name is a valid JavaScript identifier. Consider the example from the previous section where the key was "[object Object]":

alert(map.[object Object]); // throws a syntax error

This may lead you to believe that it is safer to always look up a value by passing a key as a string rather than by name. In Closure, this turns out not to be the case because of how variable renaming works in the Compiler. This will be explained in more detail in Chapter 13.

Single-quoted Strings and Double-quoted Strings are Equivalent

In some programming languages, such as Perl and PHP, double-quoted strings and single-quoted strings are interpreted differently. In JavaScript, both types of strings are interpreted in the same way; however, the convention in the Closure Library is to use single-quoted strings. (By comparison, Closure Templates mandate the use of single-quoted strings.) The consistent use of quotes makes it easier to perform searches over the codebase, but they make no difference to the JavaScript interpreter or the Closure Compiler.

The one caveat is that the JSON specification requires that strings be double-quoted, so data that is passed to a strict JSON parser (rather than the JavaScript eval() method) must use double-quoted strings.

There are Several Ways to Define an Object Literal

In JavaScript, the following statements are equivalent methods for creating a new, empty object:

// This syntax is equivalent to the syntax used in Java (and other C-style
// languages) for creating a new object.
var obj1 = new Object();

// Parentheses are technically optional if no arguments are passed to a
// function used with the 'new' operator, though this is generally avoided.
var obj2 = new Object;

// This syntax is the most succinct and is used exclusively in Closure.
var obj3 = {};

The third syntax is called an "object literal" because the properties of the object can be declared when the object is created:

// obj4 is a new object with three properties.
var obj4 = {
  'one': 'uno',
  'two': 'dos',
  'three': 'tres'
};

// Alternatively, each property could be added in its own statement:
var obj5 = {};
obj5['one'] = 'uno';
obj5['two'] = 'dos';
obj5['three'] = 'tres';

// Or some combination could be used:
var obj6 = { 'two': 'dos' };
obj6['one'] = 'uno';
obj6['three'] = 'tres';

Note that when using the object literal syntax, each property is followed by a comma except for the last one. Care must be taken to keep track of commas, as it is often forgotten when later editing code to add a new property:

// Suppose the declaration of obj4 were changed to include a fourth property.
var obj4 = {
  'one': 'uno',
  'two': 'dos',
  'three': 'tres'   // Programmer forgot to add a comma to this line...
  'four': 'cuatro'  // ...when this line was added.
};

The above will result in an error from the JavaScript interpreter because it cannot parse the object literal due to the missing comma. Currently, all browsers other than Internet Explorer allow a trailing comma in object literals to eliminate this issue (support for the trailing comma is mandated in ES5, so it should appear in IE soon):

var obj4 = {
  'one': 'uno',
  'two': 'dos',
  'three': 'tres', // This extra comma is allowed on Firefox, Chrome, and Safari.
};

Unfortunately, the trailing comma produces a syntax error in Internet Explorer, so the Closure Compiler will issue an error when it encounters the trailing comma.

Because of the popularity of JSON, it is frequent to see the keys of object literals as double quoted strings. The quotes are required in order to be valid JSON, but they are not required in order to be valid JavaScript. Keys in object literals can be expressed in any of the following three ways:

var obj7 = {
  one: 'uno',      // No quotes at all
  'two': 'dos',    // Single-quoted string
  "three": 'tres'  // Double-quoted string
};

Using no quotes at all may seem odd at first, particularly if there is a variable in scope with the same name. Try to predict what happens in the following case:

var one = 'ONE';
var obj8 = { one: one };

The above creates a new object, obj8, with one property whose name is one and whose value is 'ONE'. When one is used on the left of the colon, it is simply a name, but when it is used on the right of the colon, it is a variable. This is perhaps more obvious if obj8 were defined in the following way:

var obj8 = {};
obj8.one = one;

Here it is clearer that obj8.one identifies the property on obj8 named one which is distinct from the variable one to the right of the equals sign.

The only time that quotes must be used with a key in an object literal is when the key is a JavaScript keyword (note this is no longer a restriction in ES5):

var countryCodeMap = {
  fr: 'France',
  in: 'India',  // Throws a syntax error because 'in' is a JavaScript keyword
  ru: 'Russia'
};

Despite this edge case, keys in object literals are rarely quoted in Closure. This has to do with variable renaming, which is explained in more detail in Chapter 13 on the Compiler. As a rule of thumb, only quote keys that would sacrifice the correctness of the code if they were renamed. For example, if the code were:

var translations = {
  one: 'uno',
  two: 'dos',
  three: 'tres'
};

var englishToSpanish = function(englishWord) {
  return translations[englishWord];
};

englishToSpanish('two'); // should return 'dos'

Then the Compiler might rewrite this code as:

var a = {
  a: 'uno',
  b: 'dos',
  c: 'tres'
};

var d = function(e) {
  return a[e];
};

d('two'); // should return 'dos' but now returns undefined

In this case, the behavior of the compiled code is different from that of the original code, which is a problem. This is because the keys of translations do not represent properties that can be renamed, but strings whose values are significant. Because the Compiler cannot reduce string literals, defining translations as follows would result in the compiled code having the correct behavior:

var translations = {
  'one': 'uno',
  'two': 'dos',
  'three': 'tres'
};

The "prototype" Property is Not the Prototype You are Looking For

For all the praise for its support of prototype-based programming, manipulating an object's prototype is not straightforward in JavaScript.

Recall that every object in JavaScript has a link to another object called its prototype. Cycles are not allowed in a chain of prototype links, so a collection of JavaScript objects and prototype relationships can be represented as a rooted tree where nodes are objects and edges are prototype relationships. Many modern browsers (though not all) expose an object's prototype via its __proto__ property. (This causes a great deal of confusion because an object's __proto__ and prototype properties rarely refer to the same object.) The root of such a tree will be the object referenced by Object.prototype in JavaScript. Consider the following JavaScript code:

// Rectangle is an ordinary function.
var Rectangle = function() {};

// Every function has a property named 'prototype' whose value is an object
// with a property named 'constructor' that points back to the original
// function. It is possible to add more properties to this object.
Rectangle.prototype.width = 3;
Rectangle.prototype.height = 4;

// Creates an instance of a Rectangle, which is an object whose
// __proto__ property points to Rectangle.prototype. This is discussed
// in more detail in Chapter 5 on Classes and Inheritance.
var rect = new Rectangle();

Figure B-1 contains the corresponding object model:

In the diagram, each box represents a JavaScript object and each circle represents a JavaScript primitive. Recall that JavaScript objects are associative arrays whose keys are always strings, so each arrow exiting a box represents a property of that object, the target being the property's value. For simplicity, the closed, shaded arrows represent a __proto__ property while closed, white arrows represent a prototype property. Open arrows have their own label indicating the name of the property.

The prototype chain for an object can be found by following the __proto__ arrows until the root object is reached. Note that even though Object.prototype is the root of the graph when only __proto__ edges are considered, Object.prototype also has its own values, such as the built-in function mapped to hasOwnProperty.

When resolving the value associated with a key on a JavaScript object, each object in the prototype chain is examined until one is found with a property whose name matches the specified key. If no such property exists, the value returned is undefined. This is effectively equivalent to the following:

var lookupProperty = function(obj, key) {
  while (obj) {
    if (obj.hasOwnProperty(key)) {
      return obj[key];
    }
    obj = obj.__proto__;
  }
  return undefined;
};

For example, to evaluate the expression rect.width, the first step is to check whether a property named width is defined on rect. From the diagram, it is clear that rect has no properties of its own because it has no outbound arrows besides __proto__. The next step is to follow the __proto__ property to Rectangle.prototype which does have an outbound width arrow. Following that arrow leads to the primitive value 3, which is what rect.width evaluates to.

Because the prototype chain always leads to Object.prototype, any value that is declared as a property on Object.prototype will be available to all objects, by default. For example, every object has a property named hasOwnProperty that points to a native function. That is, unless hasOwnProperty is reassigned to some other value on an object, or some object in its prototype chain. For example, if Rectangle.prototype.hasOwnProperty were assigned to alert, then rect.hasOwnProperty would refer to alert because Rectangle.prototype appears earlier in rect's prototype chain than Object.prototype. Although this makes it possible to grant additional functionality to all objects by modifying Object.prototype, this practice is discouraged and error-prone as explained in Chapter 4.

Understanding the prototype chain is also important when considering the effect of removing properties from an object. JavaScript provides the delete keyword for removing a property from an object: using delete can only affect the object itself, but not any of the objects in its prototype chain. This may sometimes yield surprising results:

rect.width = 13;
alert(rect.width); // alerts 13
delete rect.width;
alert(rect.width); // alerts 3 even though delete was used
delete rect.width;
alert(rect.width); // still alerts 3

When rect.width = 13 is evaluated, it creates a new binding on rect with the key width and the value 13. When alert(rect.width) is called on the following line, rect now has its own property named width, so it displays its associated value, 13. When delete rect.width is called, the width property defined on rect is removed, but the width property on Rectangle.prototype still exists. This is why the second call to alert yields 3 rather than undefined. To remove the width property from every instance of Rectangle, delete must be applied to Rectangle.prototype:

delete Rectangle.prototype.width;
alert(rect.width); // now this alerts undefined

It is possible to modify rect so that it behaves as if it did not have a width property without modifying Rectangle.prototype by setting rect.width to undefined. It can be determined whether the property was overridden or deleted by using the built-in hasOwnProperty method:

var obj = {};
rect.width = undefined;

// Now both rect.width and obj.width evaluate to undefined even though obj
// never had a width property defined on it or on any object in its prototype
// chain.

rect.hasOwnProperty('width'); // evaluates to true
obj.hasOwnProperty('width');  // evaluates to false

Note that the results would be different if Rectangle were implemented as follows:

var Rectangle2 = function() {
  // This adds bindings to each new instance of Rectangle2 rather than adding
  // them once to Rectangle2.prototype.
  this.width = 3;
  this.height = 4;
};

var rect1 = new Rectangle();
var rect2 = new Rectangle2();

rect1.hasOwnProperty('width'); // evaluates to false
rect2.hasOwnProperty('width'); // evaluates to true

delete rect1.width;
delete rect2.width;

rect1.width; // evaluates to 3
rect2.width; // evaluates to undefined

Finally, note that the __proto__ properties in the diagram are not set explicitly in the sample code. These relationships are managed behind the scenes by the JavaScript runtime.

The Syntax for Defining a Function is Significant

There are two common ways to define a function in JavaScript:

// Function Statement
function FUNCTION_NAME() {
  /* FUNCTION_BODY */
}

// Function Expression
var FUNCTION_NAME = function() {
  /* FUNCTION_BODY */
};

Although the function statement is less to type and is commonly used by those new to JavaScript, the behavior of the function expression is more straightforward. (Despite this, the Google style guide advocates using the function expression, so Closure uses it in almost all cases.) The behavior of the two types of function definitions is not the same, as illustrated in the following examples:

function hereOrThere() {
  return 'here';
}

alert(hereOrThere()); // alerts 'there'

function hereOrThere() {
  return 'there';
}

It may be surprising that the second version of hereOrThere is used before it is defined. This is due to a special behavior of function statements called hoisting which allows a function to be used before it is defined. In this case, the last definition of hereOrThere() wins, so it is hoisted and used in the call to alert().

By comparison, a function expression associates a value with variable, just like any other assignment statement. Because of this, calling a function defined in this manner uses the function value most recently assigned to the variable:

var hereOrThere = function() {
  return 'here';
};

alert(hereOrThere()); // alerts 'here'

hereOrThere = function() {
  return 'there';
};

For a more complete argument of why function expressions should be favored over function statements, see Appendix B of Douglas Crockford's JavaScript: The Good Parts (O'Reilly).

What "this" Refers to When a Function is Called

When calling a function of the form foo.bar.baz(), the object foo.bar is referred to as the receiver. When the function is called, it is the receiver that is used as the value for this:

var obj = {};

obj.value = 10;

/** @param {...number} additionalValues */
obj.addValues = function(additionalValues) {
  for (var i = 0; i < arguments.length; i++) {
    this.value += arguments[i];
  }
  return this.value;
};

// Evaluates to 30 because obj is used as the value for 'this' when
// obj.addValues() is called, so obj.value becomes 10 + 20.
obj.addValues(20);

If there is no explicit receiver when a function is called, then the global object becomes the receiver. As explained in "goog.global", window is the global object when JavaScript is executed in a web browser. This leads to some surprising behavior:

var f = obj.addValues;

// Evaluates to NaN because window is used as the value for 'this' when
// f() is called. Because and window.value is undefined, adding a number to
// it results in NaN.
f(20);

// This also has the unintentional side-effect of adding a value to window:
alert(window.value); // Alerts NaN

Even though obj.addValues and f refer to the same function, they behave differently when called because the value of the receiver is different in each call. For this reason, when calling a function that refers to this, it is important to ensure that this will have the correct value when it is called. To be clear, if this were not referenced in the function body, then the behavior of f(20) and obj.addValues(20) would be the same.

Because functions are first-class objects in JavaScript, they can have their own methods. All functions have the methods call() and apply() which make it possible to redefine the receiver (i.e., the object that this refers to) when calling the function. The method signatures are as follows:

/**
 * @param {*=} receiver to substitute for 'this'
 * @param {...} parameters to use as arguments to the function
 */
Function.prototype.call;

/**
 * @param {*=} receiver to substitute for 'this'
 * @param {Array} parameters to use as arguments to the function
 */
Function.prototype.apply;

Note that the only difference between call() and apply() is that call() receives the function parameters as a individual arguments whereas apply() receives them as a single array:

// When f is called with obj as its receiver, it behaves the same as calling
// obj.addValues(). Both of the following increase obj.value by 60:
f.call(obj, 10, 20, 30);
f.apply(obj, [10, 20, 30]);

The following calls are equivalent, as f and obj.addValues refer to the same function:

obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);

However, the following will not work: neither call() nor apply() uses the value of its own receiver to substitute for the receiver argument when it is unspecified.

// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);

The value of this can never be null or undefined when a function is called. When null or undefined is supplied as the receiver to call() or apply(), the global object is used as the value for receiver instead. Therefore, the above has the same undesirable side-effect of adding a property named value to the global object.

It may he helpful to think of a function as having no knowledge of the variable to which it is assigned. This helps reinforce the idea that the value of this will be bound when the function is called rather than when it is defined.

The "var" Keyword is Significant

Many self-taught JavaScript programmers believe that the var keyword is optional because they do not observe different behavior when they omit it. On the contrary, omitting the var keyword can lead to some very subtle bugs.

The var keyword is significant because it introduces a new variable in local scope. When a variable is referenced without the var keyword, it uses the variable by that name in the closest scope. If no such variable is defined, a new binding for that variable is declared on the global object. Consider the following example:

var foo = 0;

var f = function() {
  // This defines a new variable foo in the scope of f.
  // This is said to "shadow" the global variable foo, whose value is 0.
  // The global value of foo could be referenced via window.foo, if desired.
  var foo = 42;

  var g = function() {
    // This defines a new variable bar in the scope of g.
    // It uses the closest declaration of foo, which is in f.
    var bar = foo + 100;
    return bar;
  };

  // There is no variable bar declared in the current scope, f, so this
  // introduces a new variable, bar, on the global object. Code in g has
  // access to f's scope, but code in f does not have access to g's scope.
  bar = 'DO NOT DO THIS!';

  // Returns a function that adds 100 to the local variable foo.
  return g;
};

// This alerts 'undefined' because bar has not been added to the global scope yet.
alert(typeof bar);

// Calling f() has the side-effect of introducing the global variable bar.
var h = f();
alert(bar);  // Alerts 'DO NOT DO THIS!'

// Even though h() is called outside of f(), it still has access to scope of
// f and g, so h() returns (foo + 100), or 142.
alert(h());  // Alerts 142

This gets even trickier when var is omitted from loop variables. Consider the following function, f(), which uses a loop to call g() three times. Calling g() uses a loop to call alert() three times, so you may expect nine alert boxes to appear when f() is called:

var f = function() {
  for (i = 0; i < 3; i++) {
    g(i);
  } 
};

var g = function() {
  for (i = 0; i < 3; i++) {
    alert(i);
  }
}

// This alerts 0, 1, 2, and then stops.
f();

Instead, alert() only appears three times because both f() and g() fail to declare the loop variable i with the var keyword. When g() is called for the first time, it uses the global variable i which has been initialized to 0 by f(). When g() exits, it has increased the value of i to 3. On the next iteration of the loop in f(), i is now 3, so the test for the conditional i < 3 fails, and f() terminates. This problem is easily solved by appropriately using the var keyword to declare each loop variable:

for (var i = 0; i < 3; i++)

Understanding var is important in avoiding subtle bugs related to variable scope. Enabling Verbose warnings from the Closure Compiler will help catch these issues.

Block Scope is Meaningless

Unlike most C-style languages, variables in JavaScript functions are accessible throughout the entire function rather than the block (delimited by curly braces) in which the variable is declared. This can lead to the following programming error:

/**
 * Recursively traverses map and returns an array of all keys that are found.
 * @param {Object} map
 * @return {Array.<string>}
 */
var getAllKeys = function(map) {
  var keys = [];
  for (var key in map) {
    keys.push(key);
    var value = map[key];
    if (typeof value == 'object') {
      // Here, "var map" does not introduce a new local variable named map
      // because such a variable already exists in function scope.
      var map = value;
      keys = keys.concat(getAllKeys(map));
    }
  }
  return keys;
};

var mappings = {
  'derivatives': { 'sin x': 'cos x', 'cos x': '-sin x'},
  'integrals': { '2x': 'x^2', '3x^2': 'x^3'}
};

// Evaluates to: ['derivatives', 'sin x', 'cos x', 'integrals']
getAllKeys(mappings);

The array returned by getAllKeys() is missing the values '2x' and '3x^2'. This is because of a subtle error where map is reused as a variable inside the if block. In languages that support block scoping, this would introduce a new variable named map that would be assigned to value for the duration of the if block, and upon exiting the if block, the recent binding for map would be discarded and the previous binding for map would be restored. In JavaScript, there is already a variable named map in scope because one of the arguments to getAllKeys() is named map. Even though declaring var map within getAllKeys() is likely a signal that the programmer is trying to introduce a new variable, the var is silently ignored by the JavaScript interpreter and execution proceeds without interruption.

When the Verbose warning level is used, the Closure Compiler issues a warning when it encounters code such as this. To appease the Compiler, either the var must be dropped (to indicate the existing variable is meant to be reused) or a new variable name must be introduced (to indicate that a separate variable is meant to be used). The getAllKeys() example falls into the latter case, so the if block should be rewritten as:

if (typeof value == 'object') {
  var newMap = value;
  keys = keys.concat(getAllKeys(newMap));
}

Interestingly, because the scope of a variable includes the entire function, the declaration of the variable can occur anywhere in the function, even after its first "use":

var strangeLoop = function(someArray) {
  // Will alert 'undefined' because i is in scope, but no value has been
  // assigned to it at this point.
  alert(i);

  // Assign 0 to i and use it as a loop counter.
  for (i = 0; i < someArray.length; i++) {
    alert('Element ' + i + ' is: ' + someArray[i]);
  }

  // Declaration of i which puts it in function scope.
  // The value 42 is never used.
  var i = 42;
};

Like the case where redeclaring a variable goes unnoticed by the interpreter but is flagged by the Compiler, the Compiler will also issue a warning (again, with the Verbose warnings enabled) if a variable declaration appears after its first use within the function.

It should be noted that even though blocks do not introduce new scopes, functions can be used in place of blocks for that purpose. The if block in getAllKeys() could be rewritten as follows:

if (typeof value == 'object') {
  var functionWithNewScope = function() {
    // This is a new function, and therefore a new scope, so within this
    // function, map is a new variable because it is prefixed with 'var'.
    var map = value;

    // Because keys is not prefixed with 'var', the existing value of keys
    // from the enclosing function is used.
    keys = keys.concat(getAllKeys(map));
  };
  // Calling this function will have the desired effect of updating keys.
  functionWithNewScope();
}

Although the above will work, it is less efficient than replacing var map with var newMap as described earlier.