Understanding JavaScript – part II.


Attributes

We have discussed in the previous article what functions are. Let’s continue our discovery and find out the important attributes of functions.

Name and invoking

As you have seen in the previous article, a function does not necessarily require a name: function () {} (anonymous functions) is a completely valid expression. Since functions are objects, they will have a name property which contains the name of the function as a string. For anonymous functions the value of this property is an empty string: "".

Invoking a function is very very hard:

myFunc();

In JavaScript, a function is also able to invoke itself:

function myFunc() {}();

(self-invoking functions). This is a shorthand solution for:

function myFunc() {
     //...
}

myFunc();

or depending on the context

var that = {};

(function myFunc() {}).call(that);

which is a shorthand for

function myFunc() {
     //...
}

myFunc.call(that);

Anonymous, self-invoking functions get invoked and they live once. They cannot be invoked afterwards, but their scope is still there, this is called a closure – see later. Most JavaScript libraries use anonymous self-invoking functions to define their core context or namespace. For example Backbone.js:

(function () {
    //global context
    var root = this;
    var Backbone;

    //...

    if ( /* condition */ ) {
        //...
    } else {
        Backbone = root.Backbone = {};
    }

    //...
}).call(this);

Context

The context of a function is the object which we refer with the keyword this inside. By default, the context of each function is the global object (this), in browsers it will be global Window object, in Node.js it will be the scope of a module.

But!!!

The context of a function can be changed in 4.5 ways. This is a longer but very important story so allow me to tear this out into a separate article. I promise it is fun and will be explained in detail!

Scope

In JavaScript, each function has a scope and there is no such thing as a block-scope. You are also able to nest functions (nested scope) but in that case the inner function(s) are not accessible from the parent function/scope. This is not a very good practice – unless you are willing to create a closure (Patience, my young apprentice, patience!) – because you will not be able to unit test your behavior properly and also creating a function inside another each time you invoke the outer is not very efficient, and I haven’t even mentioned code-transparency yet… Also, scopes live after the function has returned.

Let’s see what you should avoid:

/**
 * alerts the name of the band
 * @param band {String} name of the band
 *
 * TODO: avoid this pattern in this way
 */
function rock(band) {

    // rock scope
    function roll() {
        // roll scope, also we have
        // access to parent rock scope
        alert("Rock & Roll: " + band);
    }

    // invoking roll
    if (band) {
        //still rock scope
        return roll();
    }
}

// this will open a small popup window
// saying: "Rock & Roll band: Awesomeness"
rock("Awesomeness");

Important!

Remember the 3 ways of defining a function? The “local” way creates a named function accessible throughout the whole scope but the other two are only accessible if they get assigned to a variable or property.

Example:

(function () {
    // local is accessible in throughout the whole scope
    local();

    //Huuuge ReferenceError, because the type
    //of the myFunc variable is still undefined
    myFunc();

    function local() {
        return "local";
    }

    var myFunc = function () {
        return "variable";
    };
}());

This is because the runtime will look for function and variable definitions when creating a scope. It will not be interested about the value until the expression contains a value assignment.

Closure

Closures are usually used for *information hiding*, or “argument gluing”. Closures are one of the most important services provided by a function since there is no other way of controlling visibility. Let’s see it in work by creating a singleton:

var singleton = (function () {
    //this variable will hold the instance of the Singleton
    var instance;

    /**
     * Singleton class constructor
     */
    function Singleton() {}

    return {
        /**
         * function is responsible to manage the singleton and return it
         * @return Singleton
         */
        "get": function () {
            if (!instance) {
                console.log("creating Singleton instance");
                instance = new Singleton();
            }

            return instance;
        }
    };
}());

The singleton variable will now hold an object which has a get function property on it. This property method will always return the Singleton instance. The Singleton constructor function is nested, it is not visible from the outside world so it cannot be created again.

singleton.get();

Also, the scope of the original function still lives, and so instance will not be garbage collected, because the singleton.get() method still returns the reference to the Singleton instance, any time we invoke it.

You can read more about the Singleton design pattern in JavaScript by Addy Osmani: Essential JavaScript design patterns. Also, I recommend this book to see how design patterns can be applied in JavaScript.

Related Posts with Thumbnails

There are no comments yet, add one below.

Leave a Comment