Mastering functions is an essential skill for the JavaScript programmer because the language has many uses for them. They perform a variety of tasks for which other languages may have special syntax.
In this chapter you will learn about the different ways to define a function in JavaScript, you will learn about function expressions and function declarations, and you will see how the local scope and the variable hoisting works. Then you will learn about a number of patterns that help your APIs (providing better interfaces to your functions), code initializations (with fewer globals), and performance (in other words—work avoidance).
Let’s dive into functions, starting by first reviewing and clarifying the important basics.
There are two main features of the functions in JavaScript that make them special—the first is that functions are first-class objects and the second is that they provide scope.
Can be created dynamically at runtime, during the execution of the program
Can be assigned to variables, can have their references copied to other variables, can be augmented, and, except for a few special cases, can be deleted
Can be passed as arguments to other functions and can also be returned by other functions
Can have their own properties and methods
So it could happen that a function A, being an object, has properties and methods, one of which happens to be another function B. Then B can accept a function C as an argument and, when executed, can return another function D. At first sight, that’s a lot of functions to keep track of. But when you’re comfortable with the various applications of the functions, you get to appreciate the power, flexibility, and expressiveness that functions can offer. In general, when you think of a function in JavaScript, think of an object, with the only special feature that this object is invokable, meaning it can be executed.
The fact that functions are objects becomes obvious when you see
the new
Function()
constructor in action:
// antipattern // for demo purposes only var add = new Function('a, b', 'return a + b'); add(1, 2); // returns 3
In this code, there’s no doubt that add()
is an object; after all it was created
by a constructor. Using the Function()
constructor
is not a good idea though (it’s as bad as eval()
) because code is passed around as a
string and evaluated. It’s also inconvenient to write (and read) because
you have to escape quotes and take extra care if you want to properly
indent the code inside the function for readability.
The second important feature is that functions provide scope. In JavaScript there’s no curly braces local
scope; in other words, blocks don’t create scope. There’s only function
scope. Any variable defined with var
inside of a function is a local variable, invisible outside the function. Saying
that curly braces don’t provide local scope means that if you define a
variable with var
inside of an
if
condition or inside of a for
or a while
loop, that doesn’t mean the variable is
local to that if
or for
. It’s only local to the wrapping function,
and if there’s no wrapping function, it becomes a global variable. As discussed in Chapter 2, minimizing the number of globals is a good
habit, so functions are indispensable when it comes to keeping the
variable scope under control.
Let’s take a moment to discuss the terminology surrounding the code used to define a function, because using accurate and agreed-upon names is just as important as the code when talking about patterns.
Consider the following snippet:
// named function expression var add = function add(a, b) { return a + b; };
The preceding code shows a function, which uses a named function expression.
If you skip the name (the second add
in the example) in the function
expression, you get an unnamed function
expression, also known as simply as function expression or most
commonly as an anonymous function. An example
is:
// function expression, a.k.a. anonymous function var add = function (a, b) { return a + b; };
So the broader term is “function expression” and the “named function expression” is a specific case of a function expression, which happens to define the optional name.
When you omit the second add
and end up with an unnamed function expression, this won’t affect the
definition and the consecutive invocations of the function. The only
difference is that the name
property of the function object will be a blank string. The name
property is an extension of the language (it’s not part of the ECMA
standard) but widely available in many environments. If you keep the
second add
, then the property
add.name
will contain the string
“add.” The name
property is useful
when using debuggers, such as Firebug, or when calling the same
function recursively from itself; otherwise you can just skip
it.
Finally, you have function declarations. They look the most similar to functions used in other languages:
function foo() { // function body goes here }
In terms of syntax, named function expressions and function declarations look similar, especially if you don’t assign the result of the function expression to a variable (as we’ll see in the callback pattern further in the chapter). Sometimes there’s no other way to tell the difference between a function declaration and a named function expression other than looking at the context in which the function occurs, as you’ll see in the next section.
There’s syntax difference between the two in the trailing semicolon. The semicolon is not needed in function declarations but is required in function expressions, and you should always use it even though the automatic semicolon insertion mechanism might do it for you.
Note
The term function literal is also commonly used. It may mean either a function expression or a named function expression. Because of this ambiguity, it’s probably better if we don’t use it.
So what should you use—function declarations or function expressions? In cases in which syntactically you cannot use a declaration, this dilemma is solved for you. Examples include passing a function object as a parameter or defining methods in object literals:
// this is a function expression, // pased as an argument to the function `callMe` callMe(function () { // I am an unnamed function expression // also known as an anonymous function }); // this is a named function expression callMe(function me() { // I am a named function expression // and my name is "me" }); // another function expression var myobject = { say: function () { // I am a function expression } };
Function declarations can only appear in “program code,” meaning inside of the
bodies of other functions or in the global space. Their definitions
cannot be assigned to variables or properties, or appear in function
invocations as parameters. Here’s an example of the allowed usage of
function declarations, where all the functions foo()
, bar()
, and local()
are defined using the function
declaration pattern:
// global scope function foo() {} function local() { // local scope function bar() {} return bar; }
Another thing to consider when choosing a function definition
pattern is the availability of the read-only name
property. Again, this property is not
standard but available in many environments. In function declarations
and named function expressions, the name
property is defined. In anonymous
function expressions, it depends on the implementation; it could be
undefined (IE) or defined with an empty string (Firefox,
WebKit):
function foo() {} // declaration var bar = function () {}; // expression var baz = function baz() {}; // named expression foo.name; // "foo" bar.name; // "" baz.name; // "baz"
The name
property is useful
when debugging code in Firebug or other debuggers. When the
debugger needs to show you an error in a function, it can check for
the presence of the name
property
and use it as an indicator. The name
property is also used to call the same
function recursively from within itself. If you were not interested in
these two cases, then an unnamed function expression would be easier
and less verbose.
The case against function declarations and the reason to prefer function expressions is that the expressions highlight that functions are objects like all other objects and not some special language construct.
From the previous discussion you may conclude that the behavior of function declarations is pretty much equivalent to a named function expression. That’s not exactly true, and a difference lies in the hoisting behavior.
Note
The term hoisting is not defined in ECMAScript, but it’s common and a good way to describe the behavior.
As you know, all variables, no matter where in the function body they are declared, get hoisted to the top of the function behind the scenes. The same applies for functions because they are just objects assigned to variables. The only “gotcha” is that when using a function declaration, the definition of the function also gets hoisted, not only its declaration. Consider this snippet:
// antipattern // for illustration only // global functions function foo() { alert('global foo'); } function bar() { alert('global bar'); } function hoistMe() { console.log(typeof foo); // "function" console.log(typeof bar); // "undefined" foo(); // "local foo" bar(); // TypeError: bar is not a function // function declaration: // variable 'foo' and its implementation both get hoisted function foo() { alert('local foo'); } // function expression: // only variable 'bar' gets hoisted // not the implementation var bar = function () { alert('local bar'); }; } hoistMe();
In this example you see that, just like with normal variables,
the mere presence of foo
and
bar
anywhere in the hoistMe()
function moves them to the top,
overwriting the global foo
and
bar
. The difference is that local
foo()
’s
definition is hoisted to the top and works fine;
although it’s defined later. The definition of bar()
is not hoisted, only its declaration.
That’s why until the code execution reaches bar()
’s definition, it’s undefined
and not usable as a function
(while still preventing the global bar()
from being “seen” in the scope
chain).
Now that the required background and terminology surrounding functions is out of the way, let’s see some of the good patterns related to functions that JavaScript has to offer, starting with the callback pattern. Again, it’s important to remember the two special features of the functions in JavaScript:
They are objects.
They provide local scope.
Functions are objects, which means that they can be passed as arguments
to other functions. When you pass the function introduceBugs()
as a parameter to the
function writeCode()
, then at some point writeCode()
is likely to
execute (or call) introduceBugs()
. In this case introduceBugs()
is
called a callback function or simply a
callback:
function writeCode(callback) { // do something... callback(); // ... } function introduceBugs() { // ... make bugs } writeCode(introduceBugs);
Note how introduceBugs()
is
passed as an argument to writeCode()
without the parentheses. Parentheses execute a function whereas in this case we want to pass
only a reference to the function and let writeCode()
execute it (in other words, call
it back) when appropriate.
Let’s take an example and start without a callback first and then
refactor later. Imagine you have a general-purpose function that does
some complicated work and returns a large data set as a result. This
generic function could be called, for example, find
Nodes()
, and its task
would be to crawl the DOM tree of a page and return an array of page
elements that are interesting to you:
var findNodes = function () { var i = 100000, // big, heavy loop nodes = [], // stores the result found; // the next node found while (i) { i -= 1; // complex logic here... nodes.push(found); } return nodes; };
It’s a good idea to keep this function generic and have it
simply return an array of DOM nodes, without doing anything with the
actual elements. The logic of modifying nodes could be in a different
function, for example a function called hide()
which, as the name suggests, hides
the nodes from the page:
var hide = function (nodes) { var i = 0, max = nodes.length; for (; i < max; i += 1) { nodes[i].style.display = "none"; } }; // executing the functions hide(findNodes());
This implementation is inefficient, because hide()
has to loop again through the array
of nodes returned by findNodes()
.
It would be more efficient if you could avoid this loop and hide the
nodes as soon as you select them in findNodes()
. But if you implement the hiding
logic in findNodes()
, it will no
longer be a generic function because of the
coupling of the retrieval and modification logic.
Enter the callback pattern—you pass your node hiding logic as a
callback function and delegate its execution:
// refactored findNodes() to accept a callback var findNodes = function (callback) { var i = 100000, nodes = [], found; // check if callback is callable if (typeof callback !== "function") { callback = false; } while (i) { i -= 1; // complex logic here... // now callback: if (callback) { callback(found); } nodes.push(found); } return nodes; };
The implementation is straightforward; the only additional task
that findNodes()
performs is
checking if an optional callback has been provided, and if so,
executing it. The callback is optional, so the refactored findNodes()
can still be used as before and
won’t break the old code that relies on the old API.
The hide()
implementation
will be much simpler now because it doesn’t need to loop through
nodes:
// a callback function var hide = function (node) { node.style.display = "none"; }; // find the nodes and hide them as you go findNodes(hide);
The callback can be an existing function as shown in the preceding
code, or it can be an anonymous function, which you create as you call
the main function. For example, here’s how you can show nodes using
the same generic findNodes()
function:
// passing an anonymous callback findNodes(function (node) { node.style.display = "block"; });
In the previous examples, the part where the callback is executed was like so:
callback(parameters);
Although this is simple and will be good enough in many cases,
there are often scenarios where the callback is not a one-off
anonymous function or a global function, but it’s a method of an
object. If the callback method uses this
to refer to the
object it belongs to, this can cause unexpected behavior.
Imagine the callback is the function paint()
, which is a method of the object
called myapp
:
var myapp = {}; myapp.color = "green"; myapp.paint = function (node) { node.style.color = this.color; };
The function findNodes()
does
something like this:
var findNodes = function (callback) { // ... if (typeof callback === "function") { callback(found); } // ... };
If you call findNodes(myapp.paint)
, it won’t work as
expected, because this.color
will
not be defined. The object this
will refer to the global object because findNodes()
is invoked as a function, not as
a method. If findNodes()
was
defined as a method of an object called dom
(like dom.
find
Nodes()
), then this
inside of the callback would refer to
dom
instead of the expected
myapp
.
The solution to this problem is to pass the callback function and in addition pass the object this callback belongs to:
findNodes(myapp.paint, myapp);
Then you also need to modify findNodes()
to bind that object you
pass:
var findNodes = function (callback, callback_obj) { //... if (typeof callback === "function") { callback.call(callback_obj, found); } // ... };
There will be more on the topics of binding and using call()
and apply()
in future chapters.
Another option for passing an object and a method to be used as a callback is to pass the method as a string, so you don’t repeat the object twice. In other words:
findNodes(myapp.paint, myapp);
can become:
findNodes("paint", myapp);
Then findNodes()
would do
something along these lines:
var findNodes = function (callback, callback_obj) { if (typeof callback === "string") { callback = callback_obj[callback]; } //... if (typeof callback === "function") { callback.call(callback_obj, found); } // ... };
The callback pattern has many everyday uses; for example, when you attach an
event listener to an element on a page, you’re actually providing a
pointer to a callback function that will be called when the event
occurs. Here’s a simple example of how console.
log()
is passed as a callback when
listening to the document’s click event:
document.addEventListener("click", console.log, false);
Most of the client-side browser programming is event-driven.
When the page is done loading, it fires a load
event. Then the
user interacts with the page and causes various events to fire, such
as click
, keypress
, mouseover
, mousemove
, and so on. JavaScript is
especially suited for event-driven programming, because of the
callback pattern, which enables your programs to work
asynchronously, in other words, out of
order.
“Don’t call us, we’ll call you” is a famous phrase in Hollywood, where many candidates audition for the same role in a movie. It would be impossible for the casting crew to answer phone calls from all the candidates all the time. In the asynchronous event-driven JavaScript, there is a similar phenomenon. Only instead of giving your phone number, you provide a callback function to be called when the time is right. You may even provide more callbacks than needed, because certain events may never happen. For example, if the user never clicks “Buy now!” then your function that validates the credit card number format will never be called back.
Another example of the callback pattern in the wild is when you use the timeout
methods provided by the browser’s window
object: setTimeout()
and setInterval()
. These methods also accept and
execute callbacks:
var thePlotThickens = function () { console.log('500ms later...'); }; setTimeout(thePlotThickens, 500);
Note again how the function thePlotThickens
is passed as a variable,
without parentheses, because you don’t want it executed right away,
but simply want to point to it for later use by setTimeout()
. Passing the string "thePlotThickens()"
instead of a function
pointer is a common antipattern similar to eval()
.
The callback is a simple and powerful pattern, which can come in handy when you’re designing a library. The code that goes into a software library should be as generic and reusable as possible, and the callbacks can help with this generalization. You don’t need to predict and implement every feature you can think of, because it will bloat the library, and most of the users will never need a big chunk of those features. Instead, you focus on core functionality and provide “hooks” in the form of callbacks, which will allow the library methods to be easily built upon, extended, and customized.
Functions are objects, so they can be used as return values. This means that a function doesn’t need to return some sort of data value or array of data as a result of its execution. A function can return another more specialized function, or it can create another function on-demand, depending on some inputs.
Here’s a simple example: A function does some work, probably some one-off initialization, and then works on its return value. The returned value happens to be another function, which can also be executed:
var setup = function () { alert(1); return function () { alert(2); }; }; // using the setup function var my = setup(); // alerts 1 my(); // alerts 2
Because setup()
wraps the
returned function, it creates a closure, and you can use this closure to
store some private data, which is accessible by the returned function
but not to the outside code. An example would be a counter that gives
you an incremented value every time you call it:
var setup = function () { var count = 0; return function () { return (count += 1); }; }; // usage var next = setup(); next(); // returns 1 next(); // 2 next(); // 3
Functions can be defined dynamically and can be assigned to variables. If you create a new function and assign it to the same variable that already holds another function, you’re overwriting the old function with the new one. In a way, you’re recycling the old function pointer to point to a new function. And all this can happen inside the body of the old function. In this case the function overwrites and redefines itself with a new implementation. This probably sounds more complicated than it is; let’s take a look at a simple example:
var scareMe = function () { alert("Boo!"); scareMe = function () { alert("Double boo!"); }; }; // using the self-defining function scareMe(); // Boo! scareMe(); // Double boo!
This pattern is useful when your function has some initial preparatory work to do and it needs to do it only once. Because there’s no reason to do repeating work when it can be avoided, a portion of the function may no longer be required. In such cases, the self-defining function can update its own implementation.
Using this pattern can obviously help with the performance of your application, because your redefined function simply does less work.
Note
Another name for this pattern is “lazy function definition,” because the function is not properly defined until the first time it’s used and it is being lazy afterwards, doing less work.
A drawback of the pattern is that any properties you’ve previously added to the original function will be lost when it redefines itself. Also if the function is used with a different name, for example, assigned to a different variable or used as a method of an object, then the redefinition part will never happen and the original function body will be executed.
Let’s see an example where the scareMe()
function is used in a way that a
first-class object would be used:
A new property is added.
The function object is assigned to a new variable.
The function is also used as a method.
Consider the following snippet:
// 1. adding a new property scareMe.property = "properly"; // 2. assigning to a different name var prank = scareMe; // 3. using as a method var spooky = { boo: scareMe }; // calling with a new name prank(); // "Boo!" prank(); // "Boo!" console.log(prank.property); // "properly" // calling as a method spooky.boo(); // "Boo!" spooky.boo(); // "Boo!" console.log(spooky.boo.property); // "properly" // using the self-defined function scareMe(); // Double boo! scareMe(); // Double boo! console.log(scareMe.property); // undefined
As you can see, the self-definition didn’t happen as you probably
expected when the function was assigned to a new variable. Every time
prank()
was called, it alerted “Boo!”
At the same time it overwrote the global scareMe()
function, but prank()
itself kept seeing the old definition
including the property property
. The
same happened when the function was used as the boo()
method of the spooky
object. All these invocations kept
rewriting the global scareMe()
pointer so that when it was eventually called, it had the updated body
alerting “Double boo” right from the first time. It was also no longer
able to see scareMe.property
.
The immediate function pattern is a syntax that enables you to execute a function as soon as it is defined. Here’s an example:
(function () { alert('watch out!'); }());
This pattern is in essence just a function expression (either named or anonymous), which is executed right after its creation. The term immediate function is not defined in the ECMAScript standard, but it’s short and helps describe and discuss the pattern.
The pattern consists of the following parts:
You define a function using a function expression. (A function declaration won’t work.)
You add a set of parentheses at the end, which causes the function to be executed immediately.
You wrap the whole function in parentheses (required only if you don’t assign the function to a variable).
The following alternative syntax is also common (note the placement of the closing parentheses), but JSLint prefers the first one:
(function () { alert('watch out!'); })();
This pattern is useful because it provides a scope sandbox for your initialization code. Think about the following common scenario: Your code has to perform some setup tasks when the page loads, such as attaching event handlers, creating objects, and so on. All this work needs to be done only once, so there’s no reason to create a reusable named function. But the code also requires some temporary variables, which you won’t need after the initialization phase is complete. It would be a bad idea to create all those variables as globals. That’s why you need an immediate function—to wrap all your code in its local scope and not leak any variables in the global scope:
(function () { var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], today = new Date(), msg = 'Today is ' + days[today.getDay()] + ', ' + today.getDate(); alert(msg); }()); // "Today is Fri, 13"
If this code weren’t wrapped in an immediate function, then the
variables days
, today
, and msg
would all be global variables, leftovers
from the initialization code.
You can also pass arguments to immediate functions, as the following example demonstrates:
// prints: // I met Joe Black on Fri Aug 13 2010 23:26:59 GMT-0800 (PST) (function (who, when) { console.log("I met " + who + " on " + when); }("Joe Black", new Date()));
Commonly, the global object is passed as an argument to the
immediate function so that it’s accessible inside of the function
without having to use window
: this
way makes the code more interoperable in environments outside the
browser:
(function (global) { // access the global object via `global` }(this));
Note that in general you shouldn’t pass too many parameters to an immediate function, because it could quickly become a burden to constantly scroll to the top and to the bottom of the function to understand how it works.
Just like any other function, an immediate function can return values and these return values can be assigned to variables:
var result = (function () { return 2 + 2; }());
Another way to achieve the same is to omit the parentheses that wrap the function, because they are not required when you assign the return value of an immediate function to a variable. Omitting the first set of parentheses gives you the following:
var result = function () {
return 2 + 2;
}();
This syntax is simpler, but it may look a bit misleading.
Failing to notice the ()
at the end
of the function, someone reading the code might think that result
points to a function. Actually
result
points to the value returned
by the immediate function, in this case the number 4.
Yet another syntax that accomplishes the same results is:
var result = (function () {
return 2 + 2;
})();
The previous examples returned a primitive integer value as the result of executing the immediate function. But instead of a primitive value, an immediate function can return any type of value, including another function. You can then use the scope of the immediate function to privately store some data, specific to the inner function you return.
In the next example, the value returned by the immediate
function is a function, which will be assigned to the variable
getResult
and will simply return
the value of res
, a value that was
precomputed and stored in the immediate function’s closure:
var getResult = (function () { var res = 2 + 2; return function () { return res; }; }());
Immediate functions can also be used when you define object properties. Imagine you need to define a property that will likely never change during the life of the object, but before you define it a bit of work needs to be performed to figure out the right value. You can use an immediate function to wrap that work and the returned value of the immediate function will become the value of the property. The following code shows an example:
var o = { message: (function () { var who = "me", what = "call"; return what + " " + who; }()), getMsg: function () { return this.message; } }; // usage o.getMsg(); // "call me" o.message; // "call me"
In this example, o.message
is
a string property, not a function, but it needs a function, which
executes while the script is loading and which helps define the
property.
The immediate function pattern is widely used. It helps you wrap an amount of work you want to do without leaving any global variables behind. All the variables you define will be local to the self-invoking functions and you don’t have to worry about polluting the global space with temporary variables.
Note
Other names for the immediate function pattern include “self-invoking” or “self-executing” function, because the function executes itself as soon as it’s defined.
This pattern is also often used in bookmarklets, because bookmarklets run on any page and keeping the global namespace clean (and your bookmarklet code unobtrusive) is critical.
The pattern also enables you to wrap individual features into self-contained modules. Imagine your page is static and works fine without any JavaScript. Then, in the spirit of progressive enhancement, you add a piece of code that enhances the page somehow. You can wrap this code (you can also call it a “module” or a “feature”) into an immediate function and make sure the page works fine with and without it. Then you can add more enhancements, remove them, split-test them, allow the user to disable them, and so on.
You can use the following template to define a piece of
functionality; let’s call it module1
:
// module1 defined in module1.js (function () { // all the module 1 code ... }());
Following the same template, you can code your other modules. Then when it’s time for releasing the code to the live site, you decide which features are ready for prime time and merge the corresponding files using your build script.
Another way to protect from global scope pollution, similar to the
immediate functions pattern previously described, is the following
immediate object initialization pattern. This
pattern uses an object with an init()
method, which
is executed immediately after the object is created. The init()
function takes care of all
initialization tasks.
Here’s an example of the immediate object pattern:
({ // here you can define setting values // a.k.a. configuration constants max_width: 600, max_height: 400, // you can also define utility methods gimmeMax: function () { return this.max_width + "x" + this.max_height; }, // initialize init: function () { console.log(this.gimmeMax()); // more init tasks... } }).init();
In terms of syntax, you approach this pattern as if you’re
creating a normal object using the object literal. You also wrap the
literal in parentheses (grouping operator), which instructs the
JavaScript engine to treat the curly braces as an object literal, not as
a code block. (It’s not an if
or a
for
loop.) After you close the
parentheses, you invoke the init()
method immediately.
You can also wrap the object and the
init()
invocation into grouping
parentheses instead of wrapping the object only. In other words, both of
these work:
({...}).init(); ({...}.init());
The benefits of this pattern are the same as the immediate function pattern: you protect the global namespace while performing the one-off initialization tasks. It may look a little more involved in terms of syntax compared to just wrapping a bunch of code in an anonymous function, but if your initialization tasks are more complicated (as they often are) it adds structure to the whole initialization procedure. For example, private helper functions are clearly distinguishable because they are properties of the temporary object, whereas in an immediate function pattern, they are likely to be just functions scattered around.
A drawback of this pattern is that most JavaScript minifiers may not minify this pattern as efficiently as the code simply wrapped into a function. The private properties and methods will not be renamed to shorter names because, from a minifier’s point of view, it’s not trivial to do so safely. At the moment of writing, Google’s Closure Compiler in “advanced” mode is the only minifier that renames the immediate object’s properties to shorter names, turning the preceding example into something like:
({d:600,c:400,a:function(){return this.d+"x"+this.c},b:function(){console.log(this. a())}}).b();
Note
This pattern is mainly suitable for one-off tasks, and there’s
no access to the object after the init()
has completed. If you want to keep a
reference to the object after it is done, you can easily achieve this
by adding return this;
at the end
of init()
.
Init-time branching (also called load-time branching) is an optimization pattern. When you know that a certain condition will not change throughout the life of the program, it makes sense to test the condition only once. Browser sniffing (or feature detection) is a typical example.
For example, after you’ve sniffed that XMLHttpRequest
is supported as a native
object, there’s no chance that the underlying browser will change in the
middle of your program execution and all of a sudden you’ll need to deal
with ActiveX objects. Because the environment doesn’t change, there’s no
reason for your code to keep sniffing (and reaching the same conclusion)
every time you need another XHR object.
Figuring out the computed styles of a DOM element or attaching event handlers are other candidates that can benefit from the init-time branching pattern. Most developers have coded—at least once in their client-side programming life—a utility with methods for attaching and removing event listeners, like in the following example:
// BEFORE var utils = { addListener: function (el, type, fn) { if (window.addEventListener) { el.addEventListener(type, fn, false); } else if (document.attachEvent) { // IE el.attachEvent('on' + type, fn); } else { // older browsers el['on' + type] = fn; } }, removeListener: function (el, type, fn) { // pretty much the same... } };
The problem with this code is that it’s a bit inefficient. Every
time you call utils.
add
Listener()
or utils.removeListener()
, the same checks get
executed over and over again.
Using init-time branching, you sniff the browser features once, during the initial loading of the script. At that time you redefine how the function will work throughout the lifespan of the page. The following is an example of how you can approach this task:
// AFTER // the interface var utils = { addListener: null, removeListener: null }; // the implementation if (window.addEventListener) { utils.addListener = function (el, type, fn) { el.addEventListener(type, fn, false); }; utils.removeListener = function (el, type, fn) { el.removeEventListener(type, fn, false); }; } else if (document.attachEvent) { // IE utils.addListener = function (el, type, fn) { el.attachEvent('on' + type, fn); }; utils.removeListener = function (el, type, fn) { el.detachEvent('on' + type, fn); }; } else { // older browsers utils.addListener = function (el, type, fn) { el['on' + type] = fn; }; utils.removeListener = function (el, type, fn) { el['on' + type] = null; }; }
Here is the moment to mention a word of caution against browser
sniffing. When you use this pattern, don’t over-assume browser features.
For example, if you’ve sniffed that the browser doesn’t support window.addEventListener
, don’t just assume the
browser you’re dealing with is IE and it doesn’t support XMLHttpRequest
natively either, although it
was true at some point in the browser’s history. There might be cases in
which you can safely assume that features go together, such as .addEventListener
and .remove
Event
Listener
, but in general browser features
change independently. The best strategy is to sniff features separately
and then use load-time branching to do the sniffing only once.
Functions are objects, so they can have properties. In fact, they do have
properties and methods out-of-the-box. For example, every
function, no matter what syntax you use to create it, automatically gets
a length
property containing the
number of arguments the function expects:
function func(a, b, c) {} console.log(func.length); // 3
You can add custom properties to your functions at any time. One use case for custom properties is to cache the results (the return value) of a function, so the next time the function is called, it doesn’t have to redo potentially heavy computations. Caching the results of a function is also known as memoization.
In the following example, the function myFunc
creates a property cache
, accessible as usual via myFunc.cache
. The cache
property is an object (a hash) where the
parameter param
passed to the
function is used as a key and the result of the computation is the
value. The result can be any complicated data structure you might
need:
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 preceding code assumes that the function takes only one
argument param
and it’s a primitive
data type (such as a string). If you have more parameters and more
complex ones, a generic solution would be to serialize them. For
example, you can serialize the arguments object as a JSON string and use
that string as a key in your cache
object:
var myFunc = function () {
var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)),
result;
if (!myFunc.cache[cachekey]) {
result = {};
// ... expensive operation ...
myFunc.cache[cachekey] = result;
}
return myFunc.cache[cachekey];
};
// cache storage
myFunc.cache = {};
Be aware that in serialization, the “identify” of the objects is lost. If you have two different objects that happen to have the same properties, both will share the same cache entry.
Another way to write the previous function is to use arguments.callee
to
refer to the function instead of hardcoding the function name. Although
this is currently possible, be aware that arguments.callee
is not allowed in ECMAScript
5 strict mode:
var myFunc = function (param) { var f = arguments.callee, result; if (!f.cache[param]) { result = {}; // ... expensive operation ... f.cache[param] = result; } return f.cache[param]; }; // cache storage myFunc.cache = {};
The configuration object pattern is a way to provide cleaner APIs, especially if you’re building a library or any other code that will be consumed by other programs.
It’s a fact of life that software requirements change as the software is developed and maintained. It often happens that you start working with some requirements in mind, but more functionality gets added afterward.
Imagine you’re writing a function called addPerson()
, which accepts a first and last
name and adds a person to a list:
function addPerson(first, last) {...}
Later you learn that actually the date of birth needs to be stored, too, and optionally the gender and the address. So you modify the function adding the new parameters (carefully putting the optional parameters at the end of the list):
function addPerson(first, last, dob, gender, address) {...}
At this point the signature of this function is already getting a little longer. And then you learn you need to add a username and it’s absolutely required, not optional. Now the caller of the function will have to pass even the optional parameters and be careful not to mix the order of the parameters:
addPerson("Bruce", "Wayne", new Date(), null, null, "batman");
Passing a large number of parameters is not convenient. A better
approach is to substitute all the parameters with only one and make it
an object; let’s call it conf
, for
“configuration”:
addPerson(conf);
Then the user of the function can do:
var conf = { username: "batman", first: "Bruce", last: "Wayne" }; addPerson(conf);
The pros of the configuration objects are:
No need to remember the parameters and their order
You can safely skip optional parameters
Easier to read and maintain
Easier to add and remove parameters
The cons of the configuration objects are:
You need to remember the names of the parameters
Property names cannot always be safely minified, especially by simpler minifiers
This pattern could be useful when your function creates DOM elements, for example, or in setting the CSS styles of an element, because elements and styles can have a great number of mostly optional attributes and properties.
The rest of the chapter discusses the topic of currying and partial function application. But before we dive into this topic, let’s first see what exactly function application means.
In some purely functional programming languages, a function is not
described as being called or
invoked, but rather it’s
applied. In JavaScript we have the same thing—we
can apply a function using the method Function.prototype.apply()
, because
functions in JavaScript are actually objects and they have
methods.
Here’s an example of a function application:
// define a function var sayHi = function (who) { return "Hello" + (who ? ", " + who : "") + "!"; }; // invoke a function sayHi(); // "Hello" sayHi('world'); // "Hello, world!" // apply a function sayHi.apply(null, ["hello"]); // "Hello, hello!"
As you can see in the example, both
invoking a function and
applying it have the same result. apply()
takes two parameters: the first one
is an object to bind to this
inside
of the function, the second is an array or arguments, which then
becomes the array-like arguments
object available inside the function. If the first parameter is
null
, then this
points to the global object, which is
exactly what happens when you call a function that is not a method of
a specific object.
When a function is a method of an object, there’s no null
reference passed around (as in the
previous example). Here the object becomes the first argument to
apply()
:
var alien = { sayHi: function (who) { return "Hello" + (who ? ", " + who : "") + "!"; } }; alien.sayHi('world'); // "Hello, world!" alien.sayHi.apply(alien, ["humans"]); // "Hello, humans!"
In the preceding snippet, this
inside of sayHi()
points to alien
. In the previous example this
points to the global object.
As the two examples demonstrate, it turns out that what we think of calling a function is not much more than syntactic sugar, equivalent to a function application.
Note that in addition to apply()
,
there’s a call()
method of the
Function.prototype
object, but it’s
still just syntax sugar on top of apply()
. Sometimes it’s better to use the
sugar: When you have a function that takes only one parameter, you can
save the work of creating arrays with just one element:
// the second is more efficient, saves an array alien.sayHi.apply(alien, ["humans"]); // "Hello, humans!" alien.sayHi.call(alien, "humans"); // "Hello, humans!"
Now that we know that calling a function is actually applying a set of arguments to a function, is it possible to pass just a few of the arguments, not all of them? This is actually similar to how you would normally do it, if you were dealing with a math function manually.
Say you have a function add()
that adds two numbers together: x
and y
. The following snippet shows
how you can approach a solution given that x
is 5 and y
is 4:
// for illustration purposes // not valid JavaScript // we have this function function add(x, y) { return x + y; } // and we know the arguments add(5, 4); // step 1 -- substitute one argument function add(5, y) { return 5 + y; } // step 2 -- substitute the other argument function add(5, 4) { return 5 + 4; }
In this snippet the steps 1 and 2 are not valid JavaScript, but
this is how you would solve that problem by hand. You take the value
of the first argument and substitute the unknown x
with the known value 5 throughout the
function. Then repeat with the same until you run out of
arguments.
Step 1 in this example could be called partial application: we only applied the first argument. When you perform a partial application you don’t get a result (a solution), but you get another function instead.
The next snippet demonstrates the use of an imaginary partialApply()
method:
var add = function (x, y) { return x + y; }; // full application add.apply(null, [5, 4]); // 9 // partial application var newadd = add.partialApply(null, [5]); // applying an argument to the new function newadd.apply(null, [4]); // 9
As you can see, the partial application gives us another
function, which can then be called with the other arguments. This
would actually be equivalent to something like add(5)(4)
, because add(5)
returns a function that can then be
called with (4)
. Again, the
familiar add(5, 4)
may be thought
of as not much more than syntactic sugar instead of using add(5)(4)
.
Now, back to Earth: there’s no partialApply()
method and functions in
JavaScript don’t behave like this by default. But you can make them,
because JavaScript is dynamic enough to allow this.
The process of making a function understand and handle partial application is called currying.
Currying has nothing to do with the spicy Indian dish; it comes from the name of the mathematician Haskell Curry. (The Haskell programming language is also named after him.) Currying is a transformation process—we transform a function. An alternative name for currying could be schönfinkelisation, after the name of another mathematician, Moses Schönfinkel, the original inventor of this transformation.
So how do we schönfinkelify (or
schönfinkelize or curry) a
function? Other functional languages may have that built right into
the language itself and all functions are curried by default. In
JavaScript we can modify the add()
function into a curried one that will handle partial
application.
Let’s take an example:
// a curried add() // accepts partial list of arguments function add(x, y) { var oldx = x, oldy = y; if (typeof oldy === "undefined") { // partial return function (newy) { return oldx + newy; }; } // full application return x + y; } // test typeof add(5); // "function" add(3)(4); // 7 // create and store a new function var add2000 = add(2000); add2000(10); // 2010
In this snippet, the first time you call add()
, it creates a closure around the inner
function it returns. The closure stores the original values x
and y
into private variables oldx
and
oldy
. The first one, oldx
, is used when the inner function
executes. If there’s no partial application and both x
and y
are passed, the function proceeds to simply add them. This
implementation of add()
is a little
more verbose than needed, just for illustration purposes. A more
compact version is shown in the next snippet, where there’s no
oldx
and oldy
, simply because the original x
is stored in the closure implicitly and we
reuse y
as a local variable instead
of creating a new variable newy
as
we did in the previous example:
// a curried add // accepts partial list of arguments function add(x, y) { if (typeof y === "undefined") { // partial return function (y) { return x + y; }; } // full application return x + y; }
In these examples, the function add()
itself took care of partial
applications. But can we do the same in a more generic fashion? In
other words, can we transform any function into a new one that accepts
partial parameters? The next snippet shows an example of a general-purpose function, let’s call it schonfinkelize()
, which does just that. We
use the name schonfinkelize()
partially because it’s a challenge to pronounce and partially because
it sounds like a verb (using “curry” could be ambiguous) and we need a
verb to denote that this is a transformation of a function.
Here is the general-purpose currying function:
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 schonfinkelize()
function
is probably a little more complicated than it should be, but only
because arguments
is not a real
array in JavaScript. Borrowing the slice()
method from Array.prototype
helps us turn arguments
into an array and work more
conveniently with it. When schonfinkelize()
is called the first time,
it stores a private reference to the slice()
method (called slice
) and also stores the arguments it was
called with (into stored_args
),
only stripping the first, because the first argument is the function
being curried. Then schonfinkelize()
returns a new function.
When the new function is called, it has access (via the closure) to
the already privately stored arguments stored_args
and the slice
reference. The new function has to
merge only the old partially applied arguments (stored_args
) with the new ones (new_args
) and then apply them to the
original function fn
(also
privately available in the closure).
Now armed with a general-purpose way of making any function curried, let’s give it a try with a few tests:
// a normal function function add(x, y) { return x + y; } // curry a function to get a new function var newadd = schonfinkelize(add, 5); newadd(4); // 9 // another option -- call the new function directly schonfinkelize(add, 6)(7); // 13
The transformation function schonfinkelize()
is not limited to single
parameters or to single-step currying. Here are some more usage
examples:
// a normal function function add(a, b, c, d, e) { return a + b + c + d + e; } // works with any number of arguments schonfinkelize(add, 1, 2, 3)(5, 5); // 16 // two-step currying var addOne = schonfinkelize(add, 1); addOne(10, 10, 10, 10); // 41 var addSix = schonfinkelize(addOne, 2, 3); addSix(5, 5); // 16
When you find yourself calling the same function and passing mostly the same parameters, then the function is probably a good candidate for currying. You can create a new function dynamically by partially applying a set of arguments to your function. The new function will keep the repeated parameters stored (so you don’t have to pass them every time) and will use them to pre-fill the full list of arguments that the original function expects.
In JavaScript the knowledge and proper use of functions is critical. This chapter discussed the background and terminology related to functions. You learned about the two important features of functions in JavaScript, namely:
Functions are first-class objects; they can be passed around as values and augmented with properties and methods.
Functions provide local scope, which other curly braces do not. Also something to keep in mind is that declarations of local variables get hoisted to the top of the local scope.
The syntax for creating functions includes:
Named function expressions
Function expressions (the same as the above, but missing a name), also known as anonymous functions
Function declarations, similar to the function syntax in other languages
After covering the background and syntax of functions, you learned about a number of useful patterns, which can be grouped into the following categories:
API patterns, which help you provide better and cleaner interfaces to your functions. These patterns include:
- Callback patterns
Pass a function as an argument
- Configuration objects
Help keep the number of arguments to a function under control
- Returning functions
When the return value of one function is another function
- Currying
When new functions are created based on existing ones plus a partial list of arguments
Initialization patterns, which help you perform initialization and setup tasks (very common when it comes to web pages and applications) in a clearer, structured way without polluting the global namespace with temporary variables. These include:
- Immediate functions
Executed as soon as they are defined
- Immediate object initialization
Initialization tasks structured in an anonymous object that provides a method to be called immediately
- Init-time branching
Helps branch code only once during initial code execution, as opposed to many times later during the life of the application
Performance patterns, which help speed up the code. These include:
- Memoization
Using function properties so that computed values are not computed again
- Self-defining functions
Overwrite themselves with new bodies to do less work from the second invocation and after
Get JavaScript Patterns now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.