2011-10-30

A quick overview of JavaScript

Update 2013-06-22:Basic JavaScript: an introduction to the language” is an improved longer version of this post.

This post gives an overview of JavaScript that is as short as possible, but explains every major feature. Give the language a chance! You have to learn its quirks, but then it is fun to program in.

1. Preliminaries

You should start a JavaScript command line to try out the examples.

Comments:

    // a single-line comment (at the end of a line)
    /*
    A multi-line comment (anywhere)
    */
Various statements:
    console.log("hello"); // print something
    var myvar = 7; // declare a variable

2. Values

In JavaScript, you work with the following values:
  • Booleans: true or false
  • Numbers: 1, 1.0 (same number, there is no distinction between integers and floating point numbers)
  • Strings: "abc", 'abc'
  • Functions:
        function twice(x) {
            return 2 * x;
        }
        console.log(twice(4)); // 8
    
  • Objects: Objects store data in properties. Each property has a name and a value.
        var obj = {
            propName1: 123,
            propName2: "abc"
        }
        obj.propName1 = 456;
        obj["propName1"] = 456; // same as previous statement
    
    Don’t mistake JavaScript objects for simple maps (dictionaries)! They can be used as maps from strings to values, but they are also real objects. Object literals are one of JavaScripts standout features: They allow you to directly create objects – no classes necessary. That is true object-orientation: You can start with concrete objects and introduce abstractions later, as needed. The abstraction for an object factory is a class in most mainstream languages, in JavaScript it is the constructor function (see below).
  • Arrays:
        var arr = [true, "abc", 123];
        console.log(arr[1]); // abc
        console.log(arr.length); // 3
    
  • undefined (see below)
  • null (see below)
Note: Both functions and arrays are also objects. For example, they can have properties:
    function foo() {}
    foo.bar = 123;

Primitives versus objects

Booleans, numbers, and strings are primitive types (if undefined and null are considered as having their own types then those types are primitive, too). You test for primitive types via typeof:
    > typeof 123
    'number'
    > typeof true
    'boolean'
    > typeof {}
    'object'
All other types are object types. You test whether a value is an instance of a given type via instanceof:
    > 123 instanceof Object
    false
    > {} instanceof Object
    true
Note that, apart from typeof and instanceof, the difference between primitives and objects is practically unnoticeable in JavaScript.

Not having a value

JavaScript assigns undefined to variables that don’t have a value yet:
    > var x;
    > typeof x === "undefined"
    true
    > typeof undeclaredVariable === "undefined"
    true
Programmers should prefer null for this purpose:
    function someFunction(x) {
        // test for null:
        if (x === null) {
            // ...
        }
    }
    someFunction(null);
Because both undefined and null are interpreted as false by an if statement, you can check for either value as follows:
    function hasAValue(x) {
        if (x) {
            // x is neither undefined nor null
            return "YES";
        } else {
            return "NO";
        }
    }
Beware: All of the following values are interpreted as false. If you check for a value via hasAValue() then, for example, 0 is not considered a value.
  • undefined
  • null
  • false
  • 0
  • ""
All other values are interpreted a true:
    > hasAValue(undefined)
    'NO'
    > hasAValue(null)
    'NO'
    > hasAValue(0)
    'NO'
    > hasAValue("")
    'NO'
    > hasAValue({})
    'YES'
    > hasAValue([])
    'YES'

3. Operators

Comparison operators (JavaScript also has an == operator, but it’s best to never use it):

Equals myvar === "abc"
Not equals myvar !== "def"
Less than myvar < 10
Less or equals myvar <= 10
Greater myvar > 0
Greater or equals myvar >= 0

Boolean operators:

Not !myvar
And 0 <= a && a < arr.length
Or a === "foo" || a === "bar"

4. Conditionals

The if statement lets a boolean condition decide between a then clause and an else clause:
    if (myvar === 3) {
        // then
    } else {
        // else
    }
In the following switch statement, the value of fruit decides which case is executed.
    switch (fruit) {
        case "banana":
            // ...
            break;
        case "apple":
            // ...
            break;
        default:
            // ...
    }

5. Loops

The for loop has the format
for(initialization; loop while this condition holds; next step)
Example:
    for (var i=0; i < arr.length; i++) {
        console.log(arr[i]);
    }
The while loop continues looping over its body while its condition holds.
    // Same as for loop above:
    var i = 0;
    while (i < arr.length) {
        console.log(arr[i]);
        i++;
    }
The do-while loop continues looping over its body while its condition holds. As the condition follows the body, the body is always executed at least once.
    do {
        // ...
    } while(condition);
In all loops:
  • break leaves the loop.
  • continue starts a new loop iteration.

6. Functions and variable declarations

A variable always exists within the complete function, even if it is declared inside a block: JavaScript performs hoisting – any var declaration is always moved to the beginning of a function. The following is code where hoisting is performed (internally).
    function foo(x) {
        x++;
        if (x > 100) {
            var tmp = x - 100;
        }
    }
Due to hoisting, the above code is equivalent to
    function foo(x) {
        var tmp;
        x++;
        if (x > 100) {
            tmp = x - 100;
        }
    }
You should be fine, as long as you remember that JavaScript is function-scoped: Wherever a variable is declared, its scope is the complete innermost enclosing function. You can simulate a block by defining a function and immediately invoking it.
    function foo(x) {
        x++;
        if (x > 100) {
            (function () {
                // The rest of foo can’t access tmp
                var tmp = x - 100;
            }());
        }
    }
This pattern is called an IIFE (immediately-invoked function expression) and pronounced “iffy”. Notes:
  • The IIFE must be wrapped in parentheses. Otherwise, you will get a syntax error. (Explanation: Without parentheses, JavaScript assumes that you are starting a statement – a function declaration. Such a declaration must have a name and cannot be immediately invoked.)
  • Adding a trailing semicolon is safer. (Explanation: If not and you write two IIFEs in a row, the first one will be interpreted as something callable – which it isn’t – and the second one as its argument.)

7. Array methods

Arrays have many methods. Especially the iteration methods are very useful, because you can avoid the for loop.
    [3, 4, 5].forEach(function (elem) {
        console.log(elem);
    });

8. Simple objects

The following is a simple object.
    var jane = {
        name: "Jane",
        
        // a method:
        sayHello: function () {
            return this.name + " says 'hello'"
        }
    };
    console.log(jane.sayHello()); // Jane says 'hello'
A method is a property whose value is a function. For example, sayHello() in the code above. In contrast to many mainstream languages, JavaScript lets you directly work with objects. That is very handy, because you can work with concrete objects first and introduce abstractions (constructors in JavaScript, classes in most other languages) later on.

Pitfall: every function has its own this

Problem: If a method m hands a function f to another method or function then f cannot access m’s this, because it has its own this. And that is unfortunate, because with JavaScript’s asynchronous programming style (on either the web or Node.js), one frequently uses callbacks. Example:
    function beep(callback) {
        console.log("beep");
        callback();
    }
    
    var counter = {
        count: 0,
        beepAndInc: function () {
            var that = this;
            beep(function () {
                // the function shadows counter’s "this"
                // => must use "that"
                that.count++;
            });
        }
    }
Array iteration methods have a second parameter that allows you to specify a value that is to be used for this:
    var jane2 = {
        name: "Jane",
        
        logHello: function (friends) {
            friends.forEach(function (friend) {
                console.log(this.name + " says hello to " + friend)
            }, this);
        }
    }

Pitfall: methods lose their this if passed around

    function repeat(n, func) {
        for(var i = 0; i < n; i++) {
            func();
        }
    }
    var counter = {
        count: 0,
        inc: function () {
            this.count++;
        }
    }
When calling repeat(), you might be tempted to hand it counter.inc:
    > repeat(2, counter.inc)
However, that doesn’t work:
    > counter.count
    0
The reason is that by accessing the value of counter.inc, you have turned the method into a function. Then this does not point to counter, any more and counter.count does not get incremented. The solution is to use the bind() method that every function has. It creates a new function where this has the value specified by its first argument.
    > repeat(2, counter.inc.bind(counter))
This time, everything worked fine:
    > counter.count
    2

9. Constructors

The closest equivalent to classes that JavaScript has are constructor functions, normal functions that are invoked via new:
    // The constructor sets up an instance
    function Person(name) {
        this.name = name;
    }
    // The prototype property of the constructor holds an object whose
    // properties are shared between all instances (usually methods)
    Person.prototype.sayHello = function () {
        return this.name + " says 'hello'"
    };
    var jane = new Person("Jane");
Extending a constructor is non-trivial. You can either do it yourself or use an API. Note that you don’t need inheritance as often in JavaScript, because you can add arbitrary properties to an instance.

10. Strict mode

ECMAScript 5 has a so-called strict mode that performs more error checks and has less quirks. It is recommended that you use it. You switch it on by putting the following line at the beginning of a file or at the beginning of a function. In the latter case, strict mode is only active inside that function.
    "use strict";
Older versions of ECMAScript simply ignore the above statement.

11. Where to go from here

The 2ality blog has many posts on JavaScript (including ones on iterating over arrays, exception handling, primitives versus objects, etc.). The posts you should read next are: The Mozilla Development Network (MDN) has much good material on JavaScript. Especially recommended: More resources can be found on JS Central. Upcoming: my book on JavaScript (free online).

No comments: