[Coding] JavaScript Patterns (Ch. 4)

Posted by Khatharsis on June 17, 2013

This chapter focused on functions in JavaScript.

The interesting thing about functions in JavaScript is that they are first-class objects, meaning they can be assigned to variables, have their references copied to other variables, and can be augmented and deleted. In other words, they can be passed as parameters to other functions and returned by other functions. They can also have their own properties and methods, like any other object. Functions can be created dynamically at runtime. Functions are very flexible and it’s easy to not see them as objects, but that is what they are. They are invokable objects (executable).

Functions play an important role in scoping. In the overview from Chapter 2, variables not declared with var inside of a function and variables declared outside of functions, whether with var or not, belong to the global scope. The ideal is to reduce global variables, so functions are used for this purpose. One of the issues I’m still working on is forgetting to declare my variables at the top of functions particularly with if-else statements and loops. I’m at the stage where I feel safe when I know I’m inside a function and it’s habit to use var whenever I make new variables, but I still forget to pull new variable declarations outside of if-else and loops. I also feel a little uncomfortable in the few occasions when I copy-paste if-else and loops from one function to another because I might forget to re-declare those variables in the new function, accidentally making the variables within become global.

A named function expression simply gives a name to a function while an anonymous function omits the name:

var add = function add(a, b) { ... };
var add = function (a, b) { ... };


The difference between the two is the name property (note: this property is not a standard and not all environments may support it). In the anonymous function, the name property will be an empty string. The reason to give a name to a function comes in handy when debugging and/or using recursion. Named function expressions also highlight that functions are objects and not a special language construct.

A function declaration looks similar to a function expression:

function add() { ... }

The author isn’t very clear on the difference between the two. Yes, a function expression is assigned to a variable and has a semicolon at the end to finish the statement whereas a function declaration does neither. However, he goes on to say that in order to tell the difference, you have to consider “the context in which the function occurs.” Function expressions (named or anonymous) can be passed as arguments to functions whereas function declarations only appear in the global space or inside the bodies of other functions. Function declarations cannot be assigned to variables or properties, or be used as parameters.

The reason to distinguish the two comes in the concept of hoisting. Just as variables are hoisted to the top of functions, functions also get hoisted because they are objects like variables are. The difference between function expressions and function declarations in terms of hoisting is the definition of a function declaration gets hoisted as well, not just its declaration. Recall that when a variable gets hoisted, it is originally given the value of undefined until it is given a value later in the code. Hoisting with function expressions work the same way.

Callbacks occur when a function is passed as a parameter to another function with the intent of invoking that passed function within the body. This pattern is often used with asynchronous calls. When functions are passed as a parameter, only their reference should be passed, i.e., without the use of parentheses as parentheses execute a function. For example:

function callbackFunction() { ... }
function aFunction(callback) {
    // Some code
    callbackFunction();
    // More code
}
aFunction(callbackFunction);

Callback functions can be existing functions or anonymous functions. Callbacks are useful when decoupling purposes, like retrieval and modification. While retrieving a large set of objects, such as nodes in a DOM tree, a callback function can be used to modify select nodes without needing to loop again through the list of nodes gathered in the retrieval process. In short, it is more efficient (in this example) to delegate the modification process as you are traversing the DOM tree.

Callbacks can become messy, such as when the callback is a method of an object and relies on the use of this. When a callback is used in this manner, this actually refers to the global object because the callback is invoked as a function, rather than a method. Recall from Ch. 3 that when an object’s keys point to functions, the values are methods and methods have this pointing to the object it is a property of. The solution for this problem is to additionally pass the object the callback is a property of and use the call() function to properly set the value of this. Another solution is to pass a string of the property name containing the method, rather than pointing to the method itself. For example:

var anObj = {};
anObj.callback = function() { ... };
var aFunction (callback, callbackObj) {
    if (typeof callback === "string") {
        callback = callbackObj[callback];
    }

    // ... or ...
    if (typeof callback === "function") {
        callback.call(callbackObj, param);
    }
}
aFunction('callback', anObj);
aFunction(anObj.callback, anObj);

Callbacks can be used in asynchronous calls. JavaScript is event-driven and the callback pattern allows for things to occur out of order (with the upside being the user doesn’t have to wait for a series of prior events to occur before his input is recognized and responded to). Callbacks are also used in setTimeout() and setInterval(). Finally, callbacks or “hooks” are often used in libraries, allowing others to build upon, extend, and customize the core library functionality.

Since functions are objects, they can be returned. Returning functions becomes useful when considering closure or storing private variables that are accessible by the returned function but not accessible by outside code. The author, unfortunately, doesn’t spend a lot of time discussing closure or the concept behind it.

Functions can be overwritten or self-defined. The purpose of redefining a function is if there is code that only needs to be executed once (sort of like a constructor in classical languages setting up an object into a valid state), then the function can redefine itself to avoid re-executing that set of code. Another name for this behavior is “lazy function definition.” Care must be taken to make sure the name of the function is consistent, otherwise the redefinition does not take place. Also, any properties added to the original function will be lost unless re-added to the new definition.

Immediate functions are functions that are invoked as soon as it has been defined. There are two ways it can be written:

(function() {}());
(function() {})();

One use of immediate functions is similar to the reason to redefine a function. Immediate functions can be used to sandbox away initialization code that only needs to be run once (e.g., attaching event handlers, creating objects). Any variables within the immediate function are temporary and don’t need to be in the global scope.

Immediate functions can also be used with parameters, as any other function:

(function(a, b) {})('hi', 'foo');

An immediate function can return values and assigned to a variable:

var result = (function() {
    return 2 + 2;
})();

Where result obtains the value of 4. Like there are multiple ways to write an immediate function, there are multiple ways to assign the result of an immediate function to a variable. It can be misleading to think result points to a function on a quick scan. So, when assigning something to a variable, to determine if you want a function vs. an immediate function, ask if you want the variable to point to a function (i.e., it will be used as a function) or if the variable should contain the value of having called that function.

A couple of other uses for immediate functions is to return the result of closures and to assign object properties a value that requires some additional logic before it can be properly set. In the second use, I am reminded of the use of the static function in C# to set static variables. A more common use of this pattern is its use with bookmarklets, which run on any page, to help keep the global namespace clean. Another use is to consider it as a self-contained module which can make testing and integration easier.
--

A variant of the immediate function pattern is the immediate object initialization pattern. From the examples given in the book, I feel the syntax is like declaring an anonymous object (if such a thing exists as the object is not assigned to a variable) coupled with an init() method, which gets executed right after the object is created:

({
    property: 1,
    init: function() {
        // whatever init tasks
    }
}).init();

The purpose of the wrapping parentheses is to tell the JavaScript engine that it encases an object literal as opposed to a code block (such as those found in if-statements or loops). The purpose of the immediate object initialization pattern is to protect the global namespace while performing one-time tasks such as initialization. Since the object is temporary and is no longer accessible once init() completes, if you did want to store the object in a variable, add a return this; line at the end of the init() function. I'm not clear on why you would want to use immediate object initialization over immediate functions, but it has to do with complexity and immediate object initialization provides more structure than immediate functions.
--

Init-time branching is an optimization pattern involving the testing of a certain condition only once during the life of the program. For example, if special code needs to be written to handle IE over Firefox, it is unlikely that the browser will change while the script is running (I don't think it's even possible without a page refresh, restarting the life cycle). The pattern removes the need to make subsequent, repeated checks throughout the code and establishes a method of handling condition-specific code once.
--

Since functions are objects, just as arrays are objects, they have a set of properties that they inherit like length which returns the number of parameters passed in. Properties can be added, like a cache to temporarily store data (i.e., memoization). The following is an example of memoization from the book:

var myFunc = function (param) {
    if (!myFunc.cache[param]) {
        var result = {};
        // ... expensive operation ...
        myFunc.cache[param] = result;
    }
    return myFunc.cache[param];
};
// cache storage
myFunc.cache = {};


The cache property of the function is simply a hash table.
--

One pattern that I particularly like is the concept of using configuration objects rather than a long list of parameters to a function. I have run into the problem of realizing I need more parameters to a specific function and it has a tendency to get uncomfortably long. Not only that, trying to remember which order the parameters need to be passed also becomes a hassle. So, passing in an object makes sense:

var aFunction = function (type, size, color, legs, age, weight) { ... }
var anotherFunction = function (configurationObj) { ... }
var confObj = {
    type: "mammal",
    size: "medium",
    age: 1,
    weight: 16
}
aFunction("mammal", "medium", null, null, 1, 16);
anotherFunction(confObj);


For a configuration object, the order of the properties doesn't matter and properties can be left out if they are optional. One con of using this pattern is rather than memorizing order, you have to now keep track of the property names.
--

Functions can be invoked by simply calling it (e.g, aFunction()), but it can also be invoked by "applying" it. The concept of applying a function reaches back to purely functional languages and the prototype of Function even includes an apply() method just as it includes a call() method (recall that functions are objects and properties which point to functions are called methods). The difference between these two methods is apply takes an array of parameters like the configuration object pattern and call takes the traditional list of parameters.

var addTwo = function(obj) {
    return (obj ? obj : 2) + 2;
}
addTwo(2);
addTwo.apply(null, [2]);
addTwo.call(null, 2);

var add = {
    addTwo: function(obj) {
        return (obj ? obj : 2) + 2;
    }
}
add.addTwo.apply(add, [2]);
add.addTwo.call(add, 2);

The first parameter in both apply and call is what the this variable should point to. When a function exists outside of an object, null should be used, otherwise it should be set to the object that the function belongs to. There may be other reasons to set this to something else, but it was not discussed or brought up.

Currying is "the process of making a function understand and handle partial application" or transforming a function. The concept is passing in parameters to a function piece-wise and the function stores up the parameters that are passed in by returning a new function that takes less parameters (via closure). The following example is from the book:

var add = function(x, y) {
    var old x = x, old y = y;
    if (typeof oldy === "undefined") {
        return function(newy) {
            return oldx + newy;
        }
    }

    return x + y;
}
add(3)(4);    // 7
var add2000 = add(2000);
add2000(10);    // 2010


If only one parameter is passed into add, it will return a function expecting a second parameter. If both parameters are passed, then it returns the sum.

A general purpose currying function looks like the following (also taken from the book):

function schonfinkelize(fn) {
    var slice = Array.prototype.slice,
    stored_args = slice.call(arguments, 1);
    return function () {
        var new_args = slice.call(arguments),
        args = stored_args.concat(new_args);
    return fn.apply(null, args);
    };
}


The arguments variable is a property of the function object, so it isn't something that was pulled out of no where. arguments contains the list of parameters passed into the function.

Currying still gives me pause, even after encountering it in Crockford's book I'm still not used to the idea. It's the case of understanding the theory, but not its application. One functional use of currying is when a function is used often with the same subset of parameters. The function can still be open to accept a different set of parameters in the other, less frequent cases.
--

A lot of material in this chapter. I expect my next entries to get longer as well as taking more time to absorb the information. I'm hoping with enough repeated exposure to theories and maybe a little bit of practical application (like currying), it will eventually sink in and I'll be able to better understand what's going on the next time I encounter it.