A JavaScript refresh | TypedArray.org

 

We will cover here some of the key concepts of JavaScript to get us started. If you have not checked JavaScript for the past few years or if you are new to JavaScript, I hope you find this useful.

We will start by covering the language basics like variables, functions, scope, and the different types, but we will not spend much time on the absolute basics like operators, or what is a function or a variable, you probably already know all that as a developer. We will discover JavaScript by going through simple examples and for each of these, highlight specific behaviors and approach the language from an interactive developer standpoint, coming from other technologies like Flash (ActionScript 3), Java, C# or simply native (C++).

Like other managed languages, JavaScript runs inside a JavaScript VM (Virtual Machine), one key difference to note is that unlike executing bytecode, JavaScript VMs are source based, translating JavaScript source code directly to native code by using what is called a JIT (Just in Time compiler) when available. The JIT performs optimization at runtime (just in time) to leverage platform specific optimizations depending on the architecture the code is being run on. Of course, most browsers available today run JavaScript, the list below highlights the most popular JavaScript VMs today in the industry:

JavaScript can provide some serious advantages over low-level languages like automatic memory allocation and collection through garbage collectors. This, however comes at the cost of speed. But managed languages provides so much value in terms of productivity and platform reach that developers today tend to favor them over low-level languages, despite the loss of performance because of the higher cost for those languages when it comes to targeting multiple platforms.

Before we get started, it is important to differentiate how browsers work. On one side we have the core language, JavaScript, and on the other side we have the browser APIs. Historically, JavaScript and the DOM used to be tightly coupled and most tutorials would cover both at the same time, but they evolved into strong separate entities. So we we will cover JavaScript first, then deep dive into the DOM and browser APIs later on. Everything in this first article could run on the shell without any interaction with the browser APIs, just pure core JavaScript. The list below represents the general-purpose core objects defined in JavaScript:

For a complete list of the global objects, check the Global Objects page from Mozilla. Other objects you might have seen in JavaScript, like Window, CanvasRenderingContext2D, or XMLHttpRequest object or any other, have nothing to do with the JavaScript language itself and are just objects to leverage specific browser capabilities, like network access, audio, rendering and more. We will cover all of these APIs in future articles.

Versions

JavaScript today is implemented across browsers following the ECMAScript specification. As of today, five editions of ECMA-262 have been published. “Harmony”, the latest revision is a work in progress:

Edition

Date Published

1June 1997
2June 1998
3December 1999
4Abandoned
5December 2009
5.1June 2011
6 (Harmony)In progress

Most browsers today support ECMAScript 5.1, the table below illustrates the conformance test suite used:

Product

Version

Test Suite Version

Chrome24.0.1312.57 mES5.1 (2012-12-17)
Firefox19ES5.1 (2013-02-07)
Internet Explorer10.0 (10.0.9200.16384)ES5.1 (2012-12-17)
Maxthon3.4.2.3000ES5.1 (2012-08-26)
Opera12.14 (build 1738)ES5.1 (2013-02-07)
Safari6.0.2 (8536.26.17)ES5.1 (2012-12-17)

There is also separate versioning for JavaScript with some additional features not necessarily implemented in ECMAScript. The latest JavaScript version is 1.8. The table below illustrates the correspondence between JavaScript and ECMAScript versions:

JavaScript version

Relation with ECMAScript

JavaScript 1.1ECMA-262 - Edition 1 is based on JavaScript 1.1.
JavaScript 1.2ECMA-262 was not complete when JavaScript 1.2 was released. JavaScript 1.2 is not fully compatible with ECMA-262 - Edition 1.
JavaScript 1.3JavaScript 1.3 is fully compatible with ECMA-262 Edition 1. JavaScript 1.3 resolved the inconsistencies that JavaScript 1.2 had with ECMA-262 while keeping all the additional features of JavaScript 1.2 except == and != which were changed to conform with ECMA-262.
JavaScript 1.4JavaScript 1.4 is fully compatible with ECMA-262 Edition 1. The third version of the ECMAScript specification was not finalized when JavaScript 1.4 was released.
JavaScript 1.5JavaScript 1.5 is fully compatible with ECMA-262 Edition 3.

Source (Mozilla)

Throughout this article we will be sticking to the ECMAScript 5.1 feature set, which is the latest revision implemented by all major browsers. From time to time we will have a quick look at some specific features outside the ECMAScript scope just for general information. When such features are covered it will be explicitly mentioned.

Assembly language of the web

In the past years, more and more languages have been targeting JavaScript. Recently projects like Emscripten have proved that it is even possible to take native C/C++ code and compile it to JavaScript. Some people have mentioned the idea of JavaScript being the assembly language of the web. It is indeed an interesting analogy. Languages like TypeScript or Dart have demonstrated this too by cross-compiling into JavaScript.

TypeScript is an implementation of ECMAScript 6 with optional strong typing, whereas Dart is a more aggressive approach as a different language that would ideally a Dart VM for Chrome. Recently, efforts like asm.js from Mozilla have pushed the idea even further by proposing a low-level subset of JavaScript that would be targeted by compilers more efficiently. Some other initiatives like CoffeScript (often called transpilers) help developers by exposing language syntactic sugar not necessarily exposed in JavaScript today.

No Compiling

As we just mentioned, one of the beauty of JavaScript is that you do not have to pre-compile your code to get it to run. Your source code is loaded directly then compiled to native code at runtime by the VM using a JIT. As a developer writing JavaScript code and coming probably from a compiled language like C#, Java or ActionScript, you have to remember at all times that there will not be any optimizations done ahead of time by a static compiler. Everything will be figured out at runtime when you will be hitting refresh.

VMs like V8 introduced engines like CrankShaft which perform key optimizations at runtime like constant folding, inlining, or loop-invariant code motion that will help with performance. However, always keep in mind what you can do in your code to help with performance too. Throughout this article, we will cover key optimizations you can rely on to help your code perform better.

Memo

Tools

Feel free to use any text editor you want to write your JavaScript code. Examples in this article will be using the console from Chrome for small code samples to test things quickly. For bigger projects, WebStorm will be used. You can follow along using these tools or use your own. Here is below a set of popular tools to write JavaScript:

Let’s get started and write some code now.

REPL

As a developer reading this, you will probably want to test small things quickly and iteratively. Traditionally, when using bytecode based languages, you would type your code, hit compile, bytecode would be generated and then executed. For every modification, you would change the source code, then hit compile again and observe the changes. With JavaScript, you could be using REPL and have a way more natural and flexible way to test things and compile them on the fly. So what does REPL stands for? It stands for read-eval-print loop. Some bytecode based languages like C#, F# offer a similar functionality to easily test some pieces of your code where you can type in the command line and evaluate pieces of you code quickly and naturally.

With Chrome Developer Tools or any other console available like Firebug with Firefox, you can use the console and just start typing some code. The figure below shows the Chrome console:

REPL

When pressing enter, your code is injected and executed. In our first example, when declaring a variable, the console will just return undefined as variable declarations return no value. Just referencing the variable we declared will automatically return its value:

REPL

If we want to retrieve the string length, we also get auto-completion directly from the console:

REPL

In the figure below, we retrieve the string length:

REPL

This provides a very nice way to test quickly some pieces of your code. Note that in the Chrome developer tools console for multiline entries, you will need to use the Shift+Enter since Enter triggers code execution. In the next figure, we define a foo function then execute it:

REPL

Note that with Firebug in Firefox, the console supports multiline code in an expanded editor view where Enter creates a new line and Shift+Enter executes it. Firefox also provides ScratchPad as part of their developer tools. In ScratchPad, JavaScript multi-line code can be entered and tested interactively by just selecting the lines needed and press Shift+F4 (MacOS) or CTRL+R (Windows). The figure below shows the ScratchPad window and the console displaying the result:

REPL

Memo

Getting started

If you come from other languages like C#, C++ or ActionScript, you may have been used to installing lots of tools, compilers, debuggers. One of the very cool things with JavaScript is that all your really need is a text editor and a browser. When embedded in HTML, JavaScript code can be either inlined inside a script tag:

<script>

// some javascript code

</script>

Or referenced and placed in external .js files, which is a better practice:

<script src="js/main.js"></script>

By default, our JavaScript code will be executed synchronously in the order the browser parses the page, from top to bottom. As a result, placing your JavaScript code at the beginning of the page is discouraged; this would cause the browser to wait until the script is executed to start displaying anything on the page. For now, we will stick to a general good practice and place our code just before the end of the body tag:

...

<script src="js/main.js"></script>

</body>

That way, content on the page be displayed first, and once the display list (DOM) is loaded, our code will be executed. This will also ensure that our scripts have access to the DOM and that all objects can be scripted. We will be spending some time on the order of execution of JavaScript in a future article about the DOM. HTML5 introduced some interesting new capabilities in terms of sequencing and order of JavaScript execution that we will cover too.

A dynamically typed language

JavaScript is a dynamically typed language, meaning that any variable can hold any data type. If you are coming from a statically typed language, this may sound scary to you. In JavaScript there is no static typing and there will probably never be. Sadly, it is often perceived that having an explicit typing of variables is required to get type checking. It is actually not necessarily required. Types can be inferred (type-inference) automatically and propagated everywhere and provide solid code completion and type checking even with languages like JavaScript. TypeScript from Microsoft is a good example of this.

Given the absence of a typing system, JavaScript will never enforce the type of a variable, so you would not be able to rely on types to implicitly perform conversions. Remember, JavaScript does not rely on a static compiler, the VM directly interprets the source code and compiles it to native code at runtime using the JIT. To create an array in JavaScript, you could use the new keyword with the Array function constructor:

var scores = new Array();

Or simply (using the literal syntax):

Note that no types are specified. At runtime the scores variable will be evaluated as an array:

var scores = [];

// outputs: true

console.log ( scores instanceof Array );

Because types cannot be enforced, variables can hold any type at anytime:

// store an array

var scores = [];

// store a number

scores = 5;

// store an object

scores = {}; 

// store a string

scores = "Hello";

If we try to call an undefined API, no errors will be captured at compile time given that there is no compilation happening statically:

var scores = []; 

scores.foo();

Because the foo method is not available on Array, we will get a runtime exception:

Uncaught TypeError: Object  has no method 'foo'

Same thing for even the simplest object:

var myObject = {};

myObject.foo();

Which would trigger the following runtime exception:

Uncaught TypeError: Object  has no method 'foo'

Browsers report errors like this one through the JavaScript console, and usually include the line that triggered the exception. The exception was uncaught in the example, but we update the code to catch it using the try catch clause:

var scores = [];

try {

  scores.foo();

} catch (e) {

  console.log ('API not available!');

}

We will get back to error handling soon, but for now let’s move for now on to some more essentials concepts like variable declaration.

Memo

  • JavaScript is a dynamically typed language.
  • No typing is required, types are evaluated at runtime.
  • Types cannot be enforced.
  • Therefore, a variable can hold any type at anytime.
  • If an object does not have an API available, the error will be triggered at runtime.
Variables and scope

Variables are declared using the var keyword:

But omitting the var keyword will also work and declare the variable as global:

// declare a global variable

score = 12;

As you can imagine, this is not recommended and you should always use var when declaring variables. So why is that? The var keyword actually dictates the scope. In the code below we use a local variable inside the foo function, making it inaccessible from outside:

function foo() {

    // declare the variable locally

    var score = 12;

    console.log ( score );

}

function foo2() {

    console.log ( score );

}

// triggers: Uncaught ReferenceError: score is not defined

foo2();

Notice that when running the code above, the error is caught at runtime; remember that there is no static compiler involved which would catch this error ahead of time. Omitting the var keyword would make the variable global to all functions:

function foo() {

    // define the variable globally

    score = 12;

    console.log ( score );

}

function foo2() {

    console.log ( score );

}

// outputs: 12

foo();

// outputs: 12

foo2();

Another important behavior when working with variables is hoisting. This behavior allows you to reference a variable before it is defined. Trying to reference an nonexistent variable will trigger an exception:

// triggers: Uncaught ReferenceError: a is not defined

console.log ( a );

But, referencing a variable declared later with var works, returning its default, unset value undefined:

// outputs: undefined

console.log ( a );

// variable a declared later

var a = 'Hello';

What happens behind the scenes is that all the variables are moved to the top of the context block and declared first, but initialization happens where the variables are defined by our code. We will see in an upcoming section that the same behavior applies to functions too. JavaScript 1.5 introduces the concept of constants that we also have in some other languages. Constants are very important and should actually be the default in most of your programs.

Mutability is a common source of bugs. Some languages like functional programming languages rely on immutability by default. Using the keyword const will guarantee your value cannot be changed after initialization. In the code below, we define a constant named LIMIT:

// define a constant

const LIMIT = 512;

// outputs: 512

console.log ( LIMIT );

Note that our constant is uppercase, which is a best practice to easily spot immutability. If you try to change the value at runtime, the original value is preserved:

// define a constant

const LIMIT = 512;

// outputs: 512

console.log ( LIMIT ); 

// try to overwrite

LIMIT = 45;

// outputs: 512

console.log ( LIMIT );

You may be surprised that no runtime exception is triggered. Actually, some browsers do, like Firefox, since version 13. Unfortunately, as of today, the const keyword is supported in Firefox and Chrome but not in Safari, or IE 9 and 10, which dramatically reduces the reach of this feature. As a result, the const keyword should not be used if you intend to reach a broad audience and a wide variety of browsers. ECMAScript 6 defines const but with different semantics and similar to variables declared with the let statement, where constants declared as const will be block scoped (a concept we will cover in the Functions section).

Memo

  • The var keyword defines the scope.
  • Omitting the var keyword will make the variable global.
  • It is always recommend to use local variables inside functions to prevent conflicts and introduction of states.
  • The const keyword introduced in JavaScript 1.5 introduces immutability but is not widely supported yet.
  • Constants are part of the Harmony proposal (ECMAScript 6).
Type conversions

As we saw earlier, because of JavaScript’s dynamic nature, types cannot be enforced. This can be a limitation for debugging, because any variable can basically hold any type, you may be taken by surprise if, for instance, no runtime exception will be triggered due to an implicit runtime conversion failing. JavaScript actually performs implicit type conversions at runtime at different occasions. First, when using numeric and string values with the + operator, the String type has precedence and concatenation is always performed:

// gives: "3hello";

var a = 3 + "hello";

// gives: "hellotrue"

var b = "hello" + true;

When using other arithmetic operators, the Number type has precedence:

// outputs: 9

var a = 10 - "1";

// outputs: 20

var b = 10 * "2";

// outputs: 5

var c = 10 / "2";

Implicit conversions to Number will also happen when using the == or != operators with the Number, String and Boolean types:

// outputs: true

console.log ("1" == 1); // equals to 1 == 1

// outputs: true

console.log ("1" == true); // equals to 1 == 1

// outputs: false

console.log ("1" != 1); // equals to 1 != 1

// outputs: false

console.log ("1" != true); // equals to 1 != 1

// outputs: false

console.log ("true" == true); // equals to NaN == 1

To avoid implicit conversions and verify that both types and values are equal, you can rely on the strict equality (===) or strict inequality (!==) operators, which will never perform automatic conversion implicitly:

// outputs: false

console.log ("1" === 1);

// outputs: false

console.log ("1" === true);

// outputs: true

console.log ("1" !== 1);

// outputs: true

console.log ("1" !== true);

It is therefore a good practice to use the strict operators to reduce the risks of ambiguity. If we need to convert data explicitly, we can use the appropriates types conversion functions:

// convert a string to a number

var a = Number ("3");

// convert a boolean to a number

var b = Number (true);

// tries to convert a non numeric string to a number

var c = Number ("Hello");

// outputs: 3 1 "NaN"

console.log ( a, b, c );

In the same way, converting a string to a number can be done using the parseInt and parseFloat functions:

// convert a string to a number

var a = parseInt ( "4 chicken" ); 

// convert a boolean to a number

var b = parseFloat ( "1.5 pint" );

// outputs: 4 1.5

console.log ( a, b );

We saw earlier that JavaScript can throw runtime exceptions, let’s spend a few minutes on this now.

Memo

  • Implicit conversion to String is performed when using the + operator.
  • Implicit conversion to Number is performed when using other arithmetic operators.
  • Implicit conversion to Number is performed when using the equality and inequality operators with the Number, Boolean and String types.
  • To avoid implicit conversions, it is recommended to use the strict equality and inequality operators.
  • Explicit conversion can be done using the proper conversion functions.
Runtime exceptions

In all projects we have to deal with runtime exceptions. In JavaScript, as we saw earlier, these can be triggered by the runtime itself or explicitly by our code. For example, if you try to call a method not available on an object, this will trigger a runtime exception:

var scores = [];

// triggers: Uncaught TypeError: Object  has no method 'foo'

scores.foo();

At any time, if we need to throw an exception ourselves, we can use the throw keyword with Error object:

throw new Error ('Oops, there is a problem');

As expected, a runtime exception needs to be handled otherwise the console will output the following message:

Uncaught Error: Oops, there is a problem

To handle errors, we can use the try catch statement. The message property in thrown Errors contains the error message:

try {

    throw new Error ('Oops, there is a problem');

} catch ( e ) {

   // outputs: Oops, there is a problem catched!

   console.log ( e.message + ' catched!');

}

If we need some logic to be executed whether or not an error is thrown after code has executed in the try block, we use the finally statement:

try {

    throw new Error ('Oops, there is a problem');

} catch ( e ) {

    // outputs: Oops, there is a problem catched!

    console.log ( e.message + ' catched!');

} finally {

    // outputs: Code triggered at all times

    console.log ( 'Code triggered at all times' );

}

Note that conditional catch cannot be done the same way as in languages like ActionScript or C#, by placing the appropriate type in the catch block to redirect the exception automatically. In JavaScript we use a single catch block and have the appropriate type test inside that same block:

try {

    throw new Error ('Oops, there is a problem');

} catch ( e ) {

    if ( e instanceof BufferError ) {

           // handle buffer error

    } else if ( e instanceof ParseError ) {

           // handle parse error

    }

} finally {

    // outputs: Code triggered at all times

    console.log ( 'Code triggered at all times' );

}

Note that the JavaScript specification details the ability to inline the if directly inside the catch block.

try {

    throw new Error ('Oops, there is a problem');

} catch ( e if e instanceof BufferError ) {

    // handle buffer error

} catch ( e if e instanceof ParseError ) {

    // handle parsing error

} finally {

    // outputs: Code triggered at all times

    console.log ( 'Code triggered at all times' );

}

Unfortunately, this feature is not part of the ECMAScript specification and will not work in most browsers except Firefox, which again has excellent support for the latest JavaScript’s features. You can rely on Firefox for testing this feature, but don’t rely on it for a real project. What about performance? In JavaScript, exception handling does not have much impact on performance either, except if you use try catch inside a function. So make sure you don’t do this:

function test() {

    try {

        var s = 0;

        for (var i = 0; i < 10000; i++) s = i;

        return s;

    } catch ( e ) {};

}

But instead, move the try catch outside of the function:

function test() {

    var s = 0;

    for (var i = 0; i < 10000; i++) s = i;

    return s;

}

try {

    test();

} catch ( e )