JavaScript Basics

Overview

JavaScript is a powerful and versatile scripting language that is continuing to evolve in order to meet the requirements of the constantly changing online environment it was created to serve - the World Wide Web. It is often characterised as a "multi-paradigm" language because it readily lends itself to a number of different approaches, including both functional and object-oriented programming techniques.

JavaScript syntax is the set of rules that must be followed in order to produce correctly structured JavaScript code. According to JavaScript's creator Brendan Eich, JavaScript borrows most of its syntax from the Java programming language, although he acknowledges the influence of other languages, including Perl. In terms of program logic and flow control, JavaScript perhaps most closely resembles the C programming language, although it also exhibits features found in a number of other languages.

Because of the nature of the environment in which JavaScript is used, there is some latitude as to where JavaScript code can be placed. It is often found, for example, between the opening and closing <head> ... </head> tags of an HTML document, although it can also be found between the opening and closing <body> ... </body> tags. For most of the examples in these pages, the example code we provide will be in one of these two locations for the sake of convenience.

There are good arguments, however, for removing JavaScript code to an external file and linking to that file from your HTML document. Doing so allows you to keep your JavaScript code separate from your HTML code, making it easier to maintain both. It also means that the code used to implement any common JavaScript functionality can be stored in a single source file rather than being duplicated across multiple HTML documents.

If JavaScript code does appear within an HTML document, whether in the head of the document or somewhere within the body of the document, it must appear inside a pair of opening and closing <script> ... </script> tags so that the browser knows that it is dealing with JavaScript code. Consider the following code examples, which all do the same thing:

<script language = "javascript">
  document.write("Hello World!")
</script>

<script type = "text/javascript">
  document.write("Hello World!")
</script>

<script>
  document.write("Hello World!")
</script>

The first example tells the browser specifically that the scripting language being used is JavaScript using the language attribute. You may see this from time to time in legacy code, but use of the language attribute was deprecated in HTML4 in favour of the type attribute which, as you can see, we have used for the second example. As you can see from the third example, we no longer need the type attribute because JavaScript is the default client-side scripting language for HTML5.

Coding style is often a matter of personal choice although if you work with an established development team there may be a set of style guidelines in place that you are expected to follow. Even if this is not the case, it is wise to adhere to common programming conventions in order to ensure that your code is both readable and easy to maintain. Above all, your coding style should be consistent.

A script can consist of a single JavaScript statement, or a whole series of JavaScript statements, function declarations and control structures. Individual program statements should appear on a separate line to make your code easier to read and debug, and should be terminated with a semicolon. As with any programming language, adding comments to your code will make it easier to maintain, and the judicious use of indentation and whitespace will make your code more readable.

Naming conventions

Your JavaScript code will be easier to read and maintain if you give meaningful names (formally called identifiers) to user-defined variables, functions, and objects. As a programmer, you are free to use whatever naming conventions you like (subject, of course, to any organisational restrictions), but try and be consistent. There are also some rules and conventions that you should be aware of.

First of all, names can include both uppercase and lowercase letters, numbers, the underscore character (_), and the dollar character ($). They may not contain hyphens, and must begin with either a letter, an underscore character, or a dollar sign. Remember also that JavaScript is case sensitive. The variable names myvar, myVar and MYVAR represent three different variables.

Be careful also not to use any JavaScript reserved words. The keyword let, for example, is used to declare variables in JavaScript. Having a variable named let in your code will result in a syntax error (a list of the words reserved by the JavaScript language is provided below). There are some generally accepted naming conventions among JavaScript programmers, and we will be adopting those conventions in these pages. These can be summarised as follows:

Function, object and variable names should begin with a lowercase letter, e.g.

function myFunction() { . . . }
let person = {firstName:"John", lastName:"Smith"};
let myVar;

Constants are usually written in all uppercase letters, e.g.

const PI = 3.141592653589793238;

Function, object and variable names that consist of more than a single word are generally written using camel case (or camel caps, or more formally medial capitals). This is where the first word appears in all lowercase letters, and the first letter of each subsequent word is capitalised, e.g.

myVeryFirstVariable = 1.0;

Obviously, this convention doesn't work for constant names consisting of two or more words if you have used all uppercase characters. The convention most often adopted here is to separate the words using the underscore character, e.g.

const MAXIMUM_OPERATING_TEMPERATURE = 220.0;

Reserved words

The JavaScript language uses a number of keywords that have a special meaning within the language. Use of these keywords is reserved. They may not be used as identifiers for user-defined JavaScript objects, functions or variables. Listed below are the reserved keywords as of ECMAScript 2015:


argumentsextendsprotected
awaitfalsepublic
breakfinallyreturn
caseforstatic
catchfunctionsuper
classifswitch
constimplementsthis
continueimportthrow
debuggerintrue
defaultinstanceoftry
deleteinterfacetypeof
doletvar
enumnewvoid
elsenullwhile
evalpackagewith
exportprivateyield


There are also a number of keywords that have been removed from the ECMAScript specification as of ECMAScript 2015, which are listed below. These words should nevertheless be treated as if they were still reserved words, and should therefore not be used as variable names, labels, or function names (there are still a number of older browsers in circulation that do not support recent ECMA standards).


abstractfloatsynchronized
booleangotothrows
byteinttransient
charlongvolatile
doublenative
finalshort


Comments

JavaScript allows the programmer to intersperse their code with comments to explain what is going on at various points in the code. A comment can consist of a short phrase at the end of a line of code, or may appear on its own line. It may even be several lines of text, depending on how much information the programmer wants to convey at a particular point in the code. Here are some examples:

document.write("Hello World!"); // outputs the text "Hello World!"


// My first JavaScript program
document.write("Hello World!");


document.write("Hello World!");
/* This line of code implements the traditional
"Hello World!" program that everybody writes when
they start learning a new programming language. */

In the first and second examples, the comment in each case follows a double solidus (forward slash). Everything that follows a double solidus on a line is ignored by the JavaScript interpreter. The last example is a JavaScript block comment. A block comment can span several lines, and begins with a solidus followed by an asterisk (/*). The end of the block comment is marked using the same symbols, but in the reverse order (*/).

Those of you familiar with the C and/or C++ programming languages will no doubt realise that comments are written in JavaScript in exactly the same way as they are in those languages. The reason for including comments is also pretty much the same - namely to provide some documentation within your code to make it easier for you (or someone else) to understand what is going on with the code if you need to revisit it at a later date.

One thing to bear in mind when using comments is that JavaScript is often embedded within an HTML document. JavaScript comments in HTML documents should only appear inside the <script> ... </script> tags that enclose the JavaScript code. Note also that JavaScript recognises the opening character sequence of an HTML comment (<!--), and treats it as a single-line comment. It does not, however, recognise the closing sequence of an HTML comment (-->), which should instead be written as //-->.

In addition to providing a form of internal program documentation, comments can be used to mark the major divisions within a script and keep distinct areas of functionality separate. They can also be used in a debugging capacity. If, for example, there is a bug in the program and you think that a particular piece of code is causing the problem, you can temporarily comment out that piece of code to see if the problem disappears.

Variables and constants

Values in JavaScript are either variable (i.e. values that can change over time) or fixed (values that never change). Values that can change are called variables (for obvious reasons), while fixed values are called constants. You will also come across the term literal. This refers to a value like a number or a string that is used in the code to represent itself (we'll see plenty of examples of numeric and string literals in due course).

In order to use a variable in a script, we should first declare it. It is in fact possible to use variables in JavaScript without first having declared them, but this practice is now frowned upon, and should be avoided. Before the publication of ECMAScript 6 (also known as ES6 or ECMAScript 2015), the accepted way of declaring a JavaScript variable was to use the var keyword:

var firstName;
var temperature = 0.0;

In the first example, the variable is given a name, but no initial value. We can perhaps assume from the variable name used that the value will be a text string, but that is by no means a given. Variables in JavaScript do not have a specific data type associated with them. In fact, different types of value can be assigned to the same variable at different points within the same script! JavaScript is thus said to be a dynamically typed (or loosely typed) language.

In the second example, the variable is given the name temperature, which indicates that it is probably a numeric value. Indeed, we have assigned the value 0.0 to the variable using the assignment operator (=). Note that all numbers in JavaScript are stored as 64-bit floating point numbers (i.e. double precision floating point numbers) giving possible values of between -(253 -1) and 253 -1. There is no distinction between integer and non-integer values.

Variables declared inside a function with the var keyword are only accessible to code within the function body. Variables declared with var anywhere else have global scope and can be accessed from anywhere, even if they are declared within a block statement (zero or more statements delimited by a pair of braces). ECMAScript 2015 (ES6) introduced the let and const keywords, the use of which limits the scope of a variable (let) or constant value (const) to the block within which it is declared.

Because of potential problems that can arise from the use of var, variables should now be declared using the let keyword. You should avoid the use of var as far as possible, although you certainly need to be aware of how it is used because it occurs in a huge body of legacy JavaScript code, some of which you may have to deal with from time to time. It also still makes an appearance in many popular JavaScript tutorials.

We could re-write the variable declarations above as follows:

let firstName;
let temperature = 0.0;

The main difference between var and let is that a variable declared with var outside of a function has global scope. In other words, it is visible from (and can be accessed from) anywhere in your code. Even if you declare a variable inside a block structure such as a for loop, the variable will still have global scope. In fact, before ECMAScript 2015, variables could only have local or global scope; JavaScript did not support block level scopes.

For reasons we won't elaborate on here, having a large number of global variables in a script is not a particularly desirable state of affairs. Obviously, since any variable declared with var outside of a function automatically becomes a global variable, the potential exists for our scripts to contain lots of global variables. The let keyword changes all that because a variable declared with let inside a block structure has block scope; it cannot be seen or accessed from outside the block.

Another change introduced by ECMAScript 2015 is the const keyword, which allows us to declare constant values whose value will not change over time. Unlike variables, which are declared with let (or var), a constant value must be initialised (i.e. given a value) at the same time it is declared. We saw the following example above:

const PI = 3.141592653589793238;

Any attempt to declare a constant value using the const keyword without assigning a value to it will cause an error to occur. An error will also occur if you attempt to assign a different value to a constant after it has been declared. Just as for variables declared with let, if a constant value is declared inside a block with the const keyword, it has block scope; it cannot be seen or accessed from outside the block.

Even though JavaScript does not have specific datatypes like integer or float or char, it does differentiate between the three most frequently used types of primitive datatype: strings, numbers and Boolean values. A string variable can be declared and initialised using a string literal inside either single or double quotes. Both of the following statements achieve exactly the same result:

let greetingString = "Hello World!";
let greetingString = 'Hello World!';

We have already seen that numeric variables are declared using numeric literals. If we enclose the numeric literal within quotes, however, we get a string:

let myValue = "12345.67"; // creates a string variable

Boolean variables can take one of two values, represented by the Boolean literals true or false. A Boolean variable is typically assigned the result of a logical operation or a comparison, for example:

let myBool;
myBool = (x === y);
if (myBool) {
  document.write("x is equal to y");
}
else {
  document.write("x is not equal to y");
}

In addition to numbers, strings and Boolean values, there are two additional primitive value types that you will encounter: the literal values null and undefined. The difference between the two is subtle and can lead to confusion. Essentially, however, a value of undefined indicates that a variable has been declared, but has not yet been assigned a value. A value of null, on the other hand, can be explicitly assigned to a variable to indicate that it currently has no value.

There are also two special numeric values that are rarely used in scripts but that can nevertheless be quite useful on occasion. The first is NaN, which represents the value Not-A-Number. This value, which can be tested for, is the value returned when a Math function fails, or when a function trying to parse a number fails. The second is Infinity, which represents a number larger than the largest number that can actually be represented. An operation resulting in division by zero will return a value of Infinity.

The availability of these two values means that mathematical operations in JavaScript are safe. Even if we attempt to divide a numeric value by zero, or perform a mathematical operation on a non-numeric value, this will not result in a fatal error that causes our script to crash. The worst-case scenario is that the offending operation will return a value of NaN or Infinity.

There are two additional primitive datatypes that we have not so far mentioned that were introduced with ECMAScript 2015 - BigInt and Symbol. The BigInt datatype can represent integers larger than 253 – 1 (the size of the largest integer that could be expressed in JavaScript prior to ECMAScript 2015). Be advised, however, that the BigInt datatype cannot be used with existing JavaScript numbers. We will not be dealing with its use in these pages.

The Symbol datatype represents a token that is generated to serve as a unique identifier in place of a standard string identifier. Its primary purpose seems to be to serve as an alternative form of property key that avoids the possibility of the same string value inadvertently being used as the key for two or more different properties. Use of the Symbol datatype is a somewhat advanced topic, and we will not be dealing with it in these pages.

The datatypes we have described so far have all been primitive datatypes. A primitive datatype can only take a single value, and does not have properties and methods of its own (more on properties and methods in due course). There is however another broad class of datatype that you will frequently encounter in JavaScript - the object. In fact, anything that is not a primitive datatype is classed as an object. We will talk about objects in some detail elsewhere in this section.

One last point concerning JavaScript primitives is that they are described in various sources as being immutable, which means that once a primitive value (number, string or Boolean) has been created, its value can never change. You will probably find the concept of immutability somewhat counter intuitive, especially since we are applying it here to variables as well as constant values.

Obviously, as far as our program logic is concerned, the value of a variable must be able to change by definition. So how can the value of a variable be fixed? What actually happens is that, when you create a variable and assign a value to it, JavaScript allocates a block of memory big enough to hold the value, stores the value in it, and then associates the variable name with the address of that block of memory.

If you subsequently change the value of the variable programmatically, JavaScript creates a new block of memory to hold the new value, assigns the value to that block, and associates the variable name with the address of the new memory location. The contents of the original memory location remain unchanged, but that location is no longer referenced by any variable. Java Script has an automatic "garbage collector" that frees up memory that is no longer used so that it can be reused.

Statements and expressions

A JavaScript script consist of a series of instructions called statements. You can think of a statement as a piece of code that does something. Statements come in two basic flavours - simple statements and block statements (or compound statements). A simple statement occupies a single line and is usually terminated with a semicolon. Here are some examples of simple statements:

let temperature = 0.0;
y = 2x;
const e = 2.718281828459045;
fullName = firstName + " " + lastName;

Note that the use of a semicolon to terminate a simple statement is not mandatory so long as it is the only statement on a line. It is however considered good practice to do so, and is the convention we will be adopting this in these pages.

A block statement is a series of zero or more statements enclosed within curly braces. Block statements are usually used to group together a number of statements to perform a complex task that cannot be carried out using a simple statement. Using a block statement allows you to use multiple statements in situations where JavaScript expects a single statement. The following if statement is an example of a block statement:

if ( loginAttempts > 3 ) {
  accessAllowed = false;
  alert ("Your access has been blocked.");
}

Note that a block statement should not be terminated with a semi-colon. The only exceptions are function expressions and object declarations. A function expression is an expression that contains a function definition whose value may be assigned to a variable, as shown in the following example:

let x = function (a, b) {return a * b};

An object declaration is the declaration of an object (or complex) variable. For example:

let person = {
  firstName: "John",
  lastName: "Doe",
  age: 30,
  eyeColor: "blue"
};

JavaScript statements often contain expressions. An expression is some combination of values and/or variables, together with operators that can be evaluated to produce another value (the result of the expression). Consider the following statement:

x = 5 * y + 10;

We have highlighted the expression part of the statement. As you can see, it consists of the values 5, y and 10, together with the multiplication operator (*) and the and addition operator (+). Expressions do not have to be numeric. They can be implemented using any kind of values. Consider the following code:

firstName = "Charles";
secondName = "Darwin";
fullName = firstName + " " + secondName;
alert (fullName);

In this example, the expression consists of three string variables. The addition operator (+) is used in this example to concatenate (join together) the three separate strings to create a new string value, which is then assigned to the variable fullName. Note that you should avoid using expressions that involve different kinds of variable, as the result will almost certainly be meaningless. For example:

amount = "10";
multiplier = 2;
result = amount * multiplier;
alert (result);

In this particular instance you would actually get a numeric result (20) from evaluating the expression, because JavaScript will assume that the value in quotes in the first line of code is should be interpreted as a numeric literal (i.e. 10). Suppose, however, that we had this instead:

amount = "Ten";
multiplier = 2;
result = amount * multiplier;
alert (result);

This time around the result will be Not-a-Number (NaN) because we are attempting to add a string value to a numeric value, and the result will be meaningless. JavaScript can't convert the word "Ten" to a numeric value, so we're essentially trying to apply a mathematical operator to a non-numeric variable.

Interestingly enough, swapping the multiplication operator for the addition operator will produce a result - the string value "2Ten" - because JavaScript converts the numeric value 2 to the string value "2" and then concatenates it with the word "Ten". You are, of course, unlikely to do anything like this in your code. But it's worth bearing in mind. We will, incidentally, be covering the topic of JavaScript operators in some detail in a separate article.

Functions

Functions are the essential building blocks of all programming languages, and JavaScript is no exception. In some programming languages, such as Visual Basic, they are called subroutines or procedures. We have already seen a number of examples in this article of the use of functions. But what exactly are they? Those of you with experience of using programming languages such as C or C++ will already know the answer to that question.

A function is a block of code that carries out a specific task. In JavaScript, a function is declared using the function keyword, followed by the function's name, followed by a comma separated list of parameters enclosed within parentheses, followed by one or more program statements - the code to be executed, sometimes called the function body - enclosed within curly braces:

function functionName(parameter1, parameter2, . . .) {
  // code to be executed
}

Note that the convention used by most JavaScript programmers is to place the function body's opening curly brace immediately after the parenthesised parameter list, with the closing curly brace sitting on its own on the line below the function's main block of code. This is not an absolute requirement, but if you choose to do things differently you should at least be consistent.

We can think of parameters as placeholders for the values that must be passed to the function when it is invoked (i.e. called by another part of the program). A parameter is essentially the name of a local variable that will be used by the function in some way. For example, suppose we want to write a simple function to add two numbers together. The function parameters will represent the two numbers to be added, so the function might look something like this:

function addTwoNumbers(a, b) {
  return a + b;
}

Because the function addTwoNumbers() has two parameters (a and b), it is expecting two values (or arguments) to be passed to it. The purpose of the function is to add the two values together, and return the result of that addition. For example:

function addTwoNumbers(a, b) {
  return a + b;
}
let result = addTwoNumbers(3, 4);
console.log(result); // 7

If a variable with a primitive datatype (i.e. a string, a number or a Boolean value) is passed as an argument to a function, it is passed by value. That means essentially that the function receives a copy of that variable, which then becomes a local variable used by the function. Any changes made to the local copy do not affect the original variable. For example:

function timesFive(a) {
  a = a * 5;
  return a;
}

let x = 7;
let result = timesFive(x);
console.log(result); // 35
console.log(x); // 7

As you can see, despite the fact that we passed the variable x to the timesFive() function as an argument, the value of x does not change after we call the function. When we pass an object to a function, things are a little different because we pass an object variable by reference. In other words, the value we are passing to the function is a reference to the object itself. If the function changes one of the object's properties in some way, the change is reflected in the object. For example:

function changeAge(subject, age) {
  subject.age = age;
  return;
}

const person = {
  firstName:"Chris",
  lastName:"Wells",
  age:21
};

console.log(person.age); // 21
console.log(changeAge(person, 50)); // undefined
console.log(person.age); // 50

Note that all functions will have a return value. The reserved word return can be used to specify what value is being returned. If a function does not explicitly return a value (this might be the case if the return keyword is used on its own, as in the above example, or is omitted altogether), a value of undefined is returned by default. Otherwise, the value returned by the function is determined by the expression that follows the return statement.

Functions allow us to write reusable code. Creating a function is a good idea if you need to carry out the same task many times in a program. Rather than writing the same code over and over again at different places in the program, we can encapsulate that code within a function, and then call that function as and when we need it. This has the advantages that it makes our program both easier to write and more compact. It also means that if we need to change the implementation of the function - perhaps to make it more efficient, or to extend its functionality - we only need to change the code in one place.

As you may have realised, there is a lot more we can say about functions and how they are used. So far, we have really only scratched the surface. A more in-depth look at how we can use functions in JavaScript will be undertaken elsewhere in this section.

Indentation and whitespace

Blank spaces between the lines, words and symbols in a script are collectively known as whitespace. The purpose of some of this whitespace is to prevent the words and symbols from running together, which would render them more or less meaningless as far as the JavaScript interpreter is concerned. However, not all of the whitespace you will see in a typical script is actually necessary; much of it - probably most of it in fact - is ignored by the interpreter. So why is it there?

The simple answer is readability. Computers read program code sequentially, ignoring any additional white space they encounter. Once they have executed one program instruction, they move onto the next - whatever that happens to be. Human beings - or at least none of the human beings we know - can't cope very well with a continuous stream of written information, even when the information consists of simple sentences written in plain English (or whatever language you are familiar with).

That's why text on the page of a book, magazine or newspaper - or pretty well anywhere else for that matter - is broken up into headings, paragraphs and sentences. It gives us a chance to digest one chunk of information before we have to deal with the next. Programs and scripts are ultimately intended for the consumption of intelligent machines that are not encumbered by such constraints, but they are (so far at least!) written and maintained by human programmers.

Let's start by thinking about whitespace in individual statements. Consider the following statements, both of which do exactly the same thing:

let y=x*12;
let y = x * 12;

As far as the JavaScript interpreter is concerned, both of these statements will be treated in exactly the same way. From a human perspective, however, the second version is more readable then the first because we have left a space between the individual tokens that make up the statement (in the context of a script or program, the word token is a generic term for things like keywords, operators and variables).

We can also use one or more blank lines in our code to separate blocks of code that implement different programming tasks. This is a bit like the blank line that is often used to separate one paragraph from another on the printed page. Again, the JavaScript interpreter will completely ignore the additional lines, but to a human programmer it signals a break between different parts of the program, enabling them to concentrate on one logical construct at a time. Consider the following code fragment:

// This code uses three helper functions
let myObject = createObject();
objHandler(object);
doSomething();


// --- helper functions ---
function createObject() {
  ...
  ...
}


function objHandler(myObject) {
  ...
  ...
}


function doSomething() {
  ...
  ...
}

In this example, the script does whatever it does by calling three helper functions. The first block of code makes up the main body of the script, and consists of three program statements. The first calls a function that creates an object; the second calls a function that does something to the object; and the third calls a function that performs some final task. A pair of blank lines separates the main body of the script from the first function definition, and additional pairs of blank lines separate the function definitions from one another.

Note that the number of blank lines used between blocks of code is up to the programmer. We feel that two lines gives a good degree of separation, but do whatever you feel comfortable with. Note also that, although the order in which function definitions appear in a script usually makes no difference to the JavaScript interpreter, it's probably a good idea to keep them in the order in which they will be called as far as possible. That way, they will be easier to find if you need to change something at a later date.

You have probably noticed that we give each individual program statement its own line. You may also have noticed that not all program statements begin at the same level of indentation. To illustrate why we use newlines and indentation in the way that we do, consider the following piece of code, which defines a function to implement a simple bubble sort that sorts an array of string values alphabetically:

function bubbleSort(list){let sorted,temp;let n=list.length-1;let newList=Array.from(list);do{sorted=false;for(let i=0;i<n;i++){if (newList[i+1].localeCompare(newList[i])===-1){temp=newList[i+1];newList[i+1]=newList[i];newList[i]=temp;sorted=true;}}}while(sorted);return newList;}

Believe it or not this code works, despite the fact that it probably looks completely unintelligible to most people. The interpreter doesn't care about aesthetics, however, and will happily execute the code. We mere humans, on the other hand, need to make some kind of sense of the code, and as things stand that's going to be an uphill struggle. Let's see what we can do with some newlines and a bit of indentation:

function bubbleSort(list) {
  let sorted, temp;
  let n = list.length-1;
  let newList = Array.from(list);
  do {
    sorted = false;
    for (let i=0; i < n; i++) {
      if (newList[i+1].localeCompare(newList[i]) === -1) {
        temp = newList[i+1];
        newList[i+1] = newList[i];
        newList[i] = temp;
        sorted = true;
      }
    }
  } while (sorted);
  return newList;
}

Now we have given each program statement its own line and made some judicious use of indentation, our function definition is a lot more readable and (hopefully) easier to understand. Note that all of the code in the body of the function definition (i.e. all of the code between the opening and closing curly braces) is indented by two spaces in relation to the function header (the number of spaces you use for each level of indentation is usually a matter of choice; we tend to use two spaces in these pages).

The code inside the do ... while loop is also indented by a further two spaces to draw attention to the fact that we have a block of code here that will execute repeatedly while some condition is true. We're not done yet, however. Nested within the do ... while loop is a for loop, and within the for loop we have a nested if conditional statement. Each nested block of code is indented to a degree that reflects the level at which it is nested (don't worry if the loop and conditional programming constructs are not familiar to you. We'll be covering them elsewhere in this section).

You may have noticed that we tend to add spaces before opening parentheses and after closing parentheses (but not usually inside parentheses). The same goes for pairs of curly braces. The exceptions are function calls and function definitions; there should never be a space between the function name and the opening parenthesis that follows it (actually, doing so won't break your script; it's just considered bad practice for some reason).

As we have seen, an entire JavaScript script could, in theory, be written on a single line. It would however rapidly become very difficult to read and maintain. Using indentation and whitespace to make your programs readable, together with strategically placed comments to let you (or another programmer) know what is happening at various points in the code, is largely a matter of common sense. By all means adopt your own style in this respect, but bear in mind that there are many established conventions that have stood the test of time.

It is probably worth noting that the production versions of many JavaScript libraries are compressed by removing all whitespace that is not strictly necessary. This is done in order to reduce the file size of the library to optimise download times and performance, although it can be a little disconcerting when you open up the file and look at the code!

The JavaScript Engine

JavaScript is an interpreted language, which means that the source code is not compiled before it is sent to the browser. This means that the browser software must implement a JavaScript Engine that is capable of translating the human-readable JavaScript source code into a language that the CPU can understand. Precisely how the JavaScript engine achieves this task is not specified in the ECMAScript standard - each browser vendor is free to design their own implementation.

It should be noted at this point that performance was not an important consideration when Brendan Eich first developed the language for Netscape back in 1995 - a task that he achieved in just ten days. One of the limiting factors in terms of performance is that, unlike other high-level programming languages, JavaScript is dynamically typed (as opposed to statically typed). This means that, not only can a variable store any kind of data type, the kind of data type they store can be changed. Statically typed languages run faster than dynamically typed languages because the type and size of each variable is known at run time, and will not change.

Most major web browser vendors have developed their own JavaScript engine. At the time of writing, the JavaScript engine used by Google Chrome is called V8. Mozilla Firefox has an engine called SpiderMonkey, and Miscrosoft Edge uses an engine called Chakra. Opinions tend to differ over the relative merits of competing implementations, but the one thing they have in common is that they are designed with both standards compliance and performance optimisation in mind.

All JavaScript engines essentially take the JavaScript source code and turn it into machine code (i.e. binary instructions) that can be executed by a CPU. This process is achieved in two stages. In the first stage, a baseline compiler turns the source code into bytecode (a kind of machine-readable code). In the second stage, an interpreter turns the bytecode into a set of machine code instructions that the CPU can execute.

The priority for the baseline compiler in early browsers was to produce the bytecode as fast as possible so that the interpreter could begin its task immediately. Because the bytecode was not optimized, however, program execution tended to be slow. Modern browser implementations use various tricks to optimise performance, with the ultimate aim being to launch script execution as fast as possible whilst at the same time making the machine code sent to the CPU as efficient as possible.

Another factor that distinguishes JavaScript from many other modern programming languages is that it is a single-threaded language. That means that code is executed sequentially, rather than different parts of the program being executed in parallel. Consequently, a block of code that takes a significant amount of time to execute can cause a web page to “freeze” while the code runs to completion. Many modern browsers now implement multiple baseline compilers and interpreters so that each browser tab can have its own JavaScript thread. This means that even if a web page that is open in one tab freezes, web pages that are open in other tabs should still work normally.