JavaScript: Foundation — Grammar

JavaScript Grammar

Learning outcomes:

  • What are statements
  • What are expressions
  • What are keywords
  • Semicolons in JavaScript and ASI

What are statements?

One of the foundational aspects of any programming language is that of statements.

A statement is a unitary piece of code that denotes a single logical task in a program.

Often times, you'll hear that "a statement in JavaScript is a piece of code separated by a semicolon (;)."

We feel that this definition isn't that precise in going to the root of the concept. For instance, a piece of code could be completely void of semicolons but that doesn't mean that there aren't any statements in there.

It's more precise to think of a statement in terms of the task, the action, it carries out.

Some statements declare identifiers, some define conditional code, some define iterative code, some are merely expressions, and so on.

For example, consider the following code:

JavaScript
var x = 10;
console.log(x):

Here, we have 2 statements: one declares a variable x whereas the other calls a method, in particular, the console.log() method.

Since JavaScript is a high-level language, it's important to remember that a statement in JavaScript might not convert to a single, unitary instruction on the underlying machine but rather to a series of discrete instructions.

As another example, consider the following code:

JavaScript
if (true) {
   console.log('Hello');
}

Here we have one statement at the top-level of the source code, that is, the if statement. It denotes a piece of code to be executed conditionally.

As statements can themselves contain statements, the if statement is itself comprised of a statement that denotes its body. In our case, the body of if is a block statement.

Remember what's a block statement?

A block statement in JavaScript is a statement that can contain any number of statements within it. It is denoted via a pair of curly braces ({}).

But as we also know, there's no necessity to have a block statement as the body of an if statement as long as the body consists of just one statement:

JavaScript
if (true)
   console.log('Hello');

While this latter approach is valid in JavaScript, it's generally better to stick to just one style of if. And because we'll use the block statement style at one point or another anyway, that one style should be to make the body of if a block statement, always.

Consistency is always a good thing.

What are expressions?

The idea of an expression is fundamental to JavaScript, and programming, just like the idea of a statement. Both of these are amongst the building blocks of the language.

So what exactly are expressions?

An expression is a piece of code that gets resolved down to a value.

An expressions is said to evaluate to a particular value.

Let's see some expressions in JavaScript.

Consider the following snippet:

5 + 5
10

Here, 5 + 5 is an expression. It resolves down to the value 10. This value is displayed back in the console.

Expressions can be comprised of further expressions. This is exactly the case below:

20 * (5 + 5)
200

The expression 5 + 5 is part of a larger expression, 20 * (5 + 5), where we multiply its return value with 20.

Moving on, all expressions can be used as statements, but not all statements can be used as expressions.

For instance, document.write(), which is actually an expression, is also a statement, as we saw earlier. So is the expression console.log() — it's a statement as well. Even 5 + 5, which is an arithmetic expression, is a statement.

Contrary to this, the if statement is NOT an expression. Hence, it can't be used anywhere where an expression is required.

There are different kinds of expressions in JavaScript. One kind that is particularly useful to know about is primary expressions.

What are primary expressions?

Primary expressions are the simplest, atomic, expressions in JavaScript. They don't contain any other expression within them.

As a quick example, consider the value 5; it's a primary expression since it contains no other expression in it, i.e. it's a standalone value.

Similarly, consider the value 'Hello World!'; it's also a primary expression.

Shown below are instances of the different kinds of primary expressions in JavaScript:

JavaScript
5;
'Hello';
true;
undefined;
null;
this;
/abc/;

The first one is a number; the second one is a string; the third one is a Boolean; the fourth one is a special value called undefined, used to denote the absence of a value; and the fifth one is null, used to denote an empty value.

Starting from the sixth expression, however, we have new instances of primary expressions:

  • this refers to the current execution context. One of the most challenging things that newbie learners find in JavaScript is this, however it's really not that difficult. We'll explore it in detail when we learn how to work with objects in the JavaScript Objects unit.
  • /abc/ represents one of the most interesting and geeky parts of programming — regular expressions. The value /abc/ is called a regular expression and is simply used to search for given patterns in strings.

What are keywords

Every high-level programming language's grammar is comprised of special words meant for carrying out special operations. These special words are commonly referred to as keywords.

To define it precisely:

A keyword is a special word that denotes a particular operation in a language.

For instance, var is a keyword in JavaScript. It has a special meaning associated with it: it declares a variable. Another keyword is let. So is if. And else.

The table below lists all the keywords in JavaScript:

awaitbreakcasecatchclassconst
continuedebuggerdefaultdeletedoelse
exportextendsfalsefinallyforfunction
ifimportininstanceofnewnull
returnsuperswitchthisthrowtrue
trytypeofvarvoidwhilewith
yield

In this course and the Advanced JavaScript course, we'll cover all of these keywords. Yes, that's right — all of them!

As we learnt in the JavaScript Variables chapter, a variable's name in JavaScript can NOT be the same as the name of any keyword. Doing so leads to a syntax error.

An illustration follows:

JavaScript
var typeof = 10;
Uncaught SyntaxError: Unexpected token 'typeof'

Here we're trying to declare a variable called typeof, but because there's a keyword with the same name, we get an error in return.

Besides this, keyword names are case-sensitive. That is, var is different than VAR (which isn't obviously a keyword).

This means that we can create a variable called TYPEOF (but doing so won't make much sense as there is a similarly-named keyword, typeof), as shown below:

JavaScript
var TYPEOF = 10; // This is perfectly valid!

Moving on, sometimes, keywords denote expressions while sometimes they denote statements.

For instance, for denotes an iteration statement whereas typeof denotes an expression.

At this stage, it's worthwhile knowing that there's yet another (small) set of keywords in JavaScript that is made active only in strict mode.

By 'active', we mean that the parser takes these keywords into account as well, in addition to the set of core keywords that we saw above, when evaluating identifier names for validity.

For example, private is a keyword from this set. In non-strict mode, private would be a valid variable name, however, in strict mode, it won't.

Here's this set of keywords:

enumimplementsinterfacepackageprivateprotected
publicstaticlet

Some of these keywords are already part of JavaScript — such as let — while others could possibly be added in future versions.

The reason for having this extra set of keywords reserved is because they can ultimately be used in future versions of JavaScript, and so it's better to start implementing strict checks right now than in the future.

In either way, these keywords are disallowed to be used as identifier names but only when we are running JavaScript in strict mode — otherwise, they're perfectly valid.

What are operators?

Yet another crucial component of a language's grammar is that of operators.

We already covered operators in detail in the JavaScript Operators chapter, but let's quickly review the basic terminology again.

At the core:

An operator is a symbol or a keyword that denotes a special operation to be carried out on given values.

The values that an operator operates upon are commonly known as operands.

For example, the addition operator, denoted as +, represents the arithmetic addition operation which is to add two numbers together. Similarly, the typeof operator represents the operation of figuring out the type of an arbitrary value.

Operators can be classified into three distinct categories based on the number of operands they involve:

  1. Unary operators, involving just one operand. For example, typeof, postfix increment (++), logical NOT (!).
  2. Binary operators, involving two operands. For example, addition (+), assignment (=), logical AND (&&).
  3. Ternary operators, involving three operands. There's only one ternary operator in JavaScript and that is the conditional operator (often just referred to as the ternary operator).

Note that all operators represent expressions.

Semicolons and ASI

Languages such as C and PHP enforce upon the programmer to end every statement with a semicolon (;). In JavaScript, however, this isn't the case.

In JavaScript, inserting semicolons at the end of statements is completely optional.

Some people prefer to stay away from semicolons completely, arguing that they lead to verbosity in code, while some feel that it makes code more readable, by making it crystal clear as to where one statement ends and the other begins.

So should you use semicolons in your JavaScript code or not?

Well, at the end of the day, which approach to take largely depends on your personal preference.

However, this is not to say that there aren't any recommendations for whether semicolons should be used in JavaScript or not. And the general recommendation is to use semicolons.

There are two reasons for this:

  • JavaScript has a C-like syntax and, thus, it feels very natural to be using semicolons in it. (C also uses semicolons as a statement terminator.)
  • To prevent unexpected outcomes arising due to ASI in JavaScript (as discussed below).

So what is ASI?

ASI, or Automatic Semicolon Insertion, is a feature of JavaScript whereby semicolons are automatically inserted where there is a need to.

For example, if the parser can't make sense of a series of two tokens in source code, it'll add a semicolon between them automatically to make the code grammatically correct. This is ASI.

ASI doesn't always guarantee that the post-semicolon-insertion code would be grammatically correct — if the code is ill-formed, no number of semicolons can save it from failure. Plus, ASI has surprises of its own, as we shall find out later on below.

Let's see a concrete example of ASI in action.

JavaScript
var x = 10
var y = x

As soon as the JavaScript parser reads this code, it evaluates the token var (from var y) following the token 10 and the newline character. Since a var keyword can't come after a number, ASI takes over and adds a semicolon right after 10.

The code above thus becomes the following, after the application of ASI:

JavaScript
var x = 10;
var y = x

As stated before, while ASI can be helpful in allowing us to keep from using semicolons explicitly in code, it might sometimes lead to surprises.

Let's consider such a surprise case.

Here's a piece of code without semicolons:

JavaScript
var x = 10
var y = x

(1 + 2) + 5

One might think that it's absolutely fine, but surprisingly it ain't (as you might've already guessed from the background color of the snippet)!

Uncaught TypeError: x is not a function at <file>:2:9

Here's how the JavaScript parser sees the code:

JavaScript
var x = 10
var y = x(1 + 2) + 5

But why is this so? Wasn't (1 + 2) + 5 on a new line?

Well yes, (1 + 2) + 5 was indeed on a new line but that's not enough to warrant it to be treated as a separate statement in JavaScript. (JavaScript doesn't behave like Python which otherwise treats whitespace as part of its syntax.)

The JavaScript parser treats the new lines following the statement var y = x simply as any other whitespace character (such as a space, a tab, etc.) and strips them off before combining var y = x with (1 + 2) + 5.

Now, let's add semicolons to the same code and see the difference:

JavaScript
var x = 10;
var y = x;

(1 + 2) + 5;

This time, the code runs successfully without any errors, all thanks to the usage of semicolons. The semicolons make it clear that the statement var y = x ends right at that very point and that (1 + 2) + 5 is a separate statement.

As this example demonstrates, ASI troubles developers only when semicolons aren't used in source code. So, to keep from getting surprising results produced by ASI, it's a desirable thing to always use semicolons in JavaScript.

Spread the word

Think that the content was awesome? Share it with your friends!

Join the community

Can't understand something related to the content? Get help from the community.

Open Discord

 Go to home Explore more courses