JavaScript’s syntax is fairly straightforward. This chapter describes things to watch out for.

An Overview of the Syntax

This section gives you a quick impression of what JavaScript’s syntax looks like.

The following are five fundamental kinds of values:

  • Booleans:
  • Numbers:
  1. 7.851
  • Strings:
  1. 'hello'
  2. "hello"
  • Plain objects:
  1. {
  2. firstName: 'Jane',
  3. lastName: 'Doe'
  4. }
  • Arrays:
  1. [ 'apple', 'banana', 'cherry' ]

Here are a few examples

  1. // Two slashes start single-linecomments
  2.  
  3. var x; // declaring a variable
  4.  
  5. x = 3 + y; // assigning a value to the variable `x`
  6.  
  7. foo(x, y); // calling function `foo` with parameters `x` and `y`
  8. obj.bar(3); // calling method `bar` of object `obj`
  9.  
  10. // A conditional statement
  11. if (x === 0) { // Is `x` equal to zero?
  12. x = 123;
  13. }
  14.  
  15. // Defining function `baz` with parameters `a` and `b`
  16. function baz(a, b) {
  17. return a + b;
  18. }

Note the two different uses of the equals sign:

  • A single equals sign (=) is used to assign a value to a variable.
  • A triple equals sign (===) is used to compare two values (see ).

Comments

There are two kinds of comments:

  • Single-line comments via // extend to the rest of the line. Here’s an example:
  1. var a = 0; // init
  • Multiline comments via / / can extend over arbitrary ranges of text. They cannot be nested. Here are two examples:
  1. /* temporarily disabled
  2. processNext(queue);
  3. */
  4.  
  5. function (a /* int */, b /* str */) {
  6. }

This section looks

An expression produces a value and can be written wherever a value is expected—for example, as an argument in a function call or at the right side of an assignment. Each of the following lines contains an expression:

  1. 3 + x
  2. myfunc('a', 'b')

Statements

Roughly, a statement performs an action. Loops and if statements are examples of statements. A program is basically a sequence of statements.[]

Wherever JavaScript expects a statement, you can also write an expression. Such a statement is called an expression statement. The reverse does not hold: you cannot write a statement where JavaScript expects an expression. For example, an if statement cannot become the argument of a function.

Conditional statement versus conditional expressions

The difference

The following is an example of an if statement:

  1. var salutation;
  2. if (male) {
  3. salutation = 'Mr.';
  4. } else {
  5. salutation = 'Mrs.';
  6. }

There is a similar kind of expression, the conditional operator. The preceding statements are equivalent to the following code:

  1. var salutation = (male ? 'Mr.' : 'Mrs.');

The code between the equals sign and the semicolon is an expression. The parentheses are not necessary, but I find the conditional operator easier to read if I put it in parens.

Using ambiguous expressions as statements

Two

  • Object literals (expressions) look like blocks (statements):
  1. {
  2. foo: bar(3, 5)
  3. }

The preceding construct is either an object literal (details: Object Literals) or a block followed by the label foo:, followed by the function call bar(3, 5).

  • Named function expressions look like function declarations (statements):
  1. function foo() { }

The preceding construct is either a named function expression or a function declaration. The former produces a function, the latter creates a variable and assigns a function to it (details on both kinds of function definition: ).

In order to prevent ambiguity during parsing, JavaScript does not let you use object literals and function expressions as statements. That is, expression statements must not start with:

  • A curly brace
  • The keyword function

If an expression starts with either of those tokens, it can only appear in an expression context. You can comply with that requirement by, for example, putting parentheses around the expression. Next, we’ll look at two examples where that is necessary.

Evaluating an object literal via eval()

eval parses its argument in statement context. You have to put parentheses around an object literal if you want eval to return an object:

  1. > eval('{ foo: 123 }')
  2. 123
  3. > eval('({ foo: 123 })')
  4. { foo: 123 }

Immediately invoking a function expression

The followingIntroducing a New Scope via an IIFE):

  1. > (function () { return 'abc' }())
  2. 'abc'

If you omit the parentheses, you get a syntax error, because JavaScript sees a function declaration, which can’t be anonymous:

  1. > function () { return 'abc' }()
  2. SyntaxError: function statement requires a name

If you add a name, you also get a syntax error, because function declarations can’t be immediately invoked:

Whatever follows a function declaration must be

Control Flow Statements and Blocks

For control flow statements, the body is a single statement. Here are two examples:

  1. if (obj !== null) obj.foo();
  2.  
  3. while (x > 0) x--;

However, any statement can containing zero or more statements. Thus, you can also write:

  1. if (obj !== null) {
  2. obj.foo();
  3. }
  4.  
  5. while (x > 0) {
  6. x--;
  7. }

I prefer the latter form of control flow statement. Standardizing on it means that there is no difference between single-statement bodies and multistatement bodies. As a consequence, your code looks more consistent, and alternating between one statement and more than one statement is easier.

Rules for Using Semicolons

In this section,

  • Normally, statements are terminated by semicolons.
  • The exception is statements ending with blocks.

No Semicolon After a Statement Ending with a Block

The following statements are not terminated by semicolons

  • Loops: for, while (but not do-while)
  • Branching: if, switch, try
  • Function declarations (but not function expressions)

Here’s an example of while versus do-while:

  1. while (a > 0) {
  2. a--;
  3. } // no semicolon
  4.  
  5. do {
  6. --;
  7. } while (a > 0);

And here’s an example of a function declaration versus a function expression. The latter is followed by a semicolon, because it appears inside a var declaration (which is terminated by a semicolon):

  1. function foo() {
  2. // ...
  3. } // no semicolon
  4.  
  5. var foo = function () {
  6. // ...
  7. };

Note

If you do add a semicolon after a block, you do not get a syntax error, because it is considered an empty statement (see the next section).

Tip

That’s most of what you need to know about semicolons. If you always add semicolons, you can probably get by without reading the remaining parts of this section.

A semicolon on

  1. while (processNextItem() > 0);
  2. while (processNextItem() > 0) {}

The function processNextItem is assumed to return the number ofremaining items. The following program, consisting of three empty statements, is also syntactically correct:

  1. ;;;

Automatic Semicolon Insertion

The goal of automatic semicolon insertion (ASI) is to make semicolons optional at the end of a line. The image invoked by the term automatic semicolon insertion is that the JavaScript parser inserts semicolons for you (internally, things are usually handled differently).

Put another way, ASI helps the parser to determine when a statement ends. Normally, it ends with a semicolon. ASI dictates that a statement also ends if:

  • A line terminator (e.g., a newline) is followed by an illegal token.
  • A closing brace is encountered.
  • The end of the file has been reached.

Example: ASI via illegal token

The following code contains a line terminator followed by an illegal token:

  1. if (a < 0) a = 0
  2. console.log(a)

The token console is illegal after 0 and triggers ASI:

  1. if (a < 0) a = 0;
  2. console.log(a);

Example: ASI via closing brace

In the following code, the statement inside the braces is not terminated by a semicolon:

  1. function add(a,b) { return a+b }

ASI creates a syntactically correct version of the preceding code:

  1. function add(a,b) { return a+b; }

Pitfall: ASI can unexpectedly break up statements

ASI is also triggered if there is a line terminator after the keyword return. For example:

  1. // Don't do this
  2. return
  3. {
  4. name: 'John'
  5. };

ASI turns the preceding into:

  1. return;
  2. {
  3. name: 'John'
  4. };

That’s an empty return, followed by a block with the label name in front of the expression statement 'John'. After the block, there is an empty statement.

Pitfall: ASI might unexpectedly not be triggered

Sometimes a statement in a new line starts with a token that is allowed as a continuation of the previous statement. Then ASI is not triggered, even though it seems like it should be. For example:

  1. func()
  2. [ 'ul', 'ol' ].forEach(function (t) { handleTag(t) })

The square brackets in the second line are interpreted as an index into the result returned by func(). The comma inside the brackets is interpreted as the commaThe Comma Operator). Thus, JavaScript sees the previous code as:

  1. func()['ol'].forEach(function (t) { handleTag(t) });

Identifiers are used for naming things and appear in various syntactic roles in JavaScript. For example, the names of variables and unquoted property keys must be valid identifiers. Identifiers are case sensitive.

The first character of an identifier is one of:

  • Dollar sign ($)
  • Underscore (_)

Subsequent characters are:

  • Any legal first character
  • Any Unicode digit in the Unicode category “Decimal number (Nd)”; this includes European numerals such as 7 and Indic numerals such as ٣
  • Various other Unicode marks and punctuations

Examples of legal identifiers:

  1. var ε = 0.0001;
  2. var строка = '';
  3. var _tmp;
  4. var $foo2;

Even though this enables you to use a variety of human languages in JavaScript code, I recommend sticking with English, for both identifiers and comments. That ensures that your code is understandable by the largest possible group of people, which is important, given how much code can spread internationally these days.

The following identifiers are reserved words—they are part of the syntax and can’t be used as variable names (including function names and parameter names):

The following three identifiers are not reserved words, but you should treat them as if they were:

Infinity
NaN
undefined

Lastly, you should also stay away from the names of standard global variables (see ). You can use them for local variables without breaking anything, but your code still becomes confusing.

Note that you can use reserved words as unquoted property keys (as of ECMAScript 5):

You can look up the precise rules for identifiers in Mathias Bynens’s blog post “Valid JavaScript variable names”.

Invoking Methods on Number Literals

  1. 1..toString()
  2. .toString() // space before dot
  3. (1).toString()
  4. 1.0.toString()

Strict Mode

ECMAScript 5 has a strict mode that results in cleaner JavaScript, with fewer unsafe features, more warnings, and more logical behavior. The normal (nonstrict) mode is sometimes called “sloppy mode.”

Switching on Strict Mode

You switchinside your <script> element:

  1. 'use strict';

Note that JavaScript engines that don’t support ECMAScript 5 will simply ignore the preceding statement, as writing strings in this manner (as an expression statement; see ) normally does nothing.

You can also switch on strict mode per function. To do so, write your function like this:

  1. function foo() {
  2. 'use strict';
  3. ...
  4. }

This is handy when you are working with a legacy code base where switching on strict mode everywhere may break things.

In general, the changes

  • Enabling strict mode for existing code may break it
  • The code may rely on a feature that is not available anymore, or it may rely on behavior that is different in sloppy mode than in strict mode. Don’t forget that you have the option to add single strict mode functions to files that are in sloppy mode.
  • Package with care
  • When you concatenate and/or minify files, you have to be careful that strict mode isn’t switched off where it should be switched on or vice versa. Both can break code.

The following sections explain the strict mode features in more detail. You normally don’t need to know them, as you will mostly get more warnings for things that you shouldn’t do anyway.

Variables Must Be Declared in Strict Mode

All variables must be explicitly declared in strict mode. This helps to prevent typos. In sloppy mode, assigning to an undeclared variable creates a global variable:

  1. function sloppyFunc() {
  2. sloppyVar = 123;
  3. }
  4. sloppyFunc(); // creates global variable `sloppyVar`
  5. console.log(sloppyVar); // 123

In strict mode, assigning to an undeclared variable throws an exception:

  1. function strictFunc() {
  2. 'use strict';
  3. strictVar = 123;
  4. }
  5. strictFunc(); // ReferenceError: strictVar is not defined

Strict mode limits function-related features.

Functions must be declared at the top level of a scope

In strict mode, all functions must be declared at the top level of a scope (global scope or directly inside a function). That means that you can’t put a function declaration inside a block. If you do, you get a descriptive SyntaxError. For example, V8 tells you: “In strict mode code, functions can only be declared at top level or immediately within another function”:

  1. function strictFunc() {
  2. 'use strict';
  3. if (true) {
  4. // SyntaxError:
  5. function nested() {
  6. }
  7. }
  8. }

That is something that isn’t useful anyway, because the function is created in the scope of the surrounding function, not “inside” the block.

If you want to work around this limitation, you can create a function inside a block via a variable declaration and a function expression:

  1. function strictFunc() {
  2. 'use strict';
  3. if (true) {
  4. // OK:
  5. var nested = function () {
  6. };
  7. }
  8. }

Stricter rules for function parameters

The rules for function parameters are less permissive: using the same parameter name twice is forbidden, as are local variables that have the same name as a parameter.

The arguments objects has fewer properties

The arguments object is simpler in strict mode: the properties arguments.callee and arguments.caller have been eliminated, you can’t assign to the variable arguments, and arguments does not track changes to parameters (if a parameter changes, the corresponding array element does not change with it). explains the details.

this is undefined in nonmethod functions

In sloppy mode, the value of this in nonmethod functions is the global object (window in browsers; see ):

  1. function sloppyFunc() {
  2. console.log(this === window); // true
  3. }

In strict mode, it is undefined:

  1. function strictFunc() {
  2. 'use strict';
  3. console.log(this === undefined); // true
  4. }

This is useful for

  1. function Point(x, y) {
  2. 'use strict';
  3. this.x = x;
  4. this.y = y;
  5. }

Due to strict mode, you get a warning when you accidentally forget new and call it as a function:

  1. > var pt = Point(3, 1);
  2. TypeError: Cannot set property 'x' of undefined

In sloppy mode, you don’t get a warning, and global variables x and y are created. Consult Tips for Implementing Constructors for details.

Setting and Deleting Immutable Properties Fails with an Exception in Strict Mode

Illegal manipulations of

  1. var str = 'abc';
  2. function sloppyFunc() {
  3. str.length = 7; // no effect, silent failure
  4. console.log(str.length); // 3
  5. }
  6. function strictFunc() {
  7. 'use strict';
  8. str.length = 7; // TypeError: Cannot assign to
  9. // read-only property 'length'
  10. }

Unqualified Identifiers Can’t Be Deleted in Strict Mode

In sloppy mode, you can

  1. delete foo

In strict mode, you get a syntax error whenever you try to delete unqualified identifiers. You can still delete global variables like this:

  1. delete window.foo; // browsers
  2. delete global.foo; // Node.js
  3. delete this.foo; // everywhere (in global scope)

eval() Is Cleaner in Strict Mode

In strict mode, the eval() function becomes less quirky: variables declared in the evaluated string are not added to the scope surrounding anymore. For details, consult .

Features That Are Forbidden in Strict Mode

Two more JavaScript features are forbidden in strict mode:

  • The with statement is not allowed anymore (see ). You get a syntax error at compile time (when loading the code).
  • No more octal numbers: in sloppy mode, an integer with a leading zero is interpreted as octal (base 8). For example:
  1. true

In strict mode, you get a syntax error if you use this kind of literal:


[8] To keep things simple, I’m pretending that declarations are statements.