You already know how to define or assign functions directly, use multi parameter arguments in anonymous calls and know all the different variants of the function-constructor? Then you won’t find anything new about the subtle differences in using functions in JavaScript.
The work title of this article was “Different ways to define JavaScript functions”. Some code examples look quite similar but the differences can be important. While I am explaining the different ways to define JavaScript functions, you can test the code directly: install the firebug plugin and use the JavaScript console to play interactively below the article.
Function Statement
In the first example here I define a function to wrap the “window.alert”-function to open the default javascript popup-Message box:
function hello(msg) { alert(msg); }
It defines a function object in the relative namespace, named hello with one parameter msg. If you define it directly in a script-part of an html-document the function name is available in the global namespace of the document.
function fib(n) { return (n<2)?n:fib(n-1)+fib(n-2); }
Here you can see a example with recursion - the classic Fibonacci function. While using the function statement you can use the function name in the function directly because the function name is defined before the function is interpreted. Dont try start the function with more than 50 - its very expensive.
function two(n) { return one(n) * 2; } document.write(two(4)); function one(n) { return n; }
A special case is to call a function before (JavaScript is based on an interpreter, not a compiler) it's defined: function two calls function one. The JavaScript parser works bottom up but all names of functions statements are known at start. The example can not used with function literals - it will produce an "one is not defined"-Error.
Function constructor
Functions in JavaScript can be used as variables and passed as arguments to other functions: they are objects like String or Object. They can also be created by using the Function constructor.
var product = new Function("a", "b", "return x*y;");
The last string argument is the function body with the JavaScript code, all arguments before it define the argument names. This variant can be used to generate new function with generated JavaScript code.
But the function body has to be parsed on every run, so performance is rather bad and the function is almost impossible to be precompiled. But be careful:
var msg = "global"; function test() { var msg = "local"; var intern = new Function("return msg;"); return intern(); } test(); // -> "global"
The local function intern runs in global scope and returns "global" instead of "local" like expected!
Function literals
var hello = function(msg) { alert(msg); }
The difference to the first function statement example is that the anonymous function can be assigned directly to the variable hello (typeof(hello)=="function"). With a function literal you can assign function into a defined namespace instead the relative scope like the function statement. The problem namespacing in JavaScript is an other interesting thing you can find by fotiweb.com or phpied.com more. In JavaScript you use simple objects with the default syntax "{}" to define namespace holder. This is the first step to write clean JavaScript without having namespace problems if an other JavaScript lib defines the same global variable (There exists more than one popular lib with defining the variable "$" global).
var com = com||{}; com.unitedCoders = com.unitedCoders||{}; com.unitedCoders.hello = function(msg) { alert(msg); }; //Using this function with name-space: com.unitedCoders.hello("Calling with name-space!");
But back to the function literal and how we can write the example "fib" - the above example show that this wont work.
var fib = function(n) { return (n<2)?n:fib(n-1) + fib(n-2); } var fib2 = fib; fib = undefined;
You can assign a function value to another variable or overwrite variables (for example with undefined). In the example the first line will be correct, but after deleting fib the inner part in the function fib2 will produce a "TypeError: fib is not a function". The first, but rarely seen solution is the usage of the local function name.
var fib = function myfib(n) { return (n<2)?n:myfib(n-1) + myfib(n-2); }
The function name myfib is only available in the function - say the local scope. You can assign the variable fib to any other name the local name myfib will always available. If you assign the function to a variable myfib the local scope will exist.
The other solution is using the callee - object who is available with the special identifier arguments (a list of all arguments) when calling the function. There is also no need to define the parameter names in the function header.
var sum = function() { var sum = 0; for (var i=0; i<arguments.length; i++) { sum += arguments[i]; } return sum; };
The identifier arguments offer the callee-property and it's a reference to the local function itself. With this reference there is the clean solution for recursion without a local function name:
var fib = function(n) { return (n<2)?n:arguments.callee(n-1)+arguments.callee(n-2); };
See on the next page what anonymous functions are and more examples for eval.
Anonymous functions
Sometimes you need a simple function without the need of assigning it to a name. This is called a anonymous function and I will give some examples of the usage.
var nums = [1, 4, 3, 2, 6, 2, 0]; nums.sort( function(a,b){ return a-b; } );
To sort a list of integers the sort-method expects a function as a parameter. This function can be defined directly within the parameter section of the sort-method by using the function literal.
var calculator = { 'add': function(a, b) { return a + b}, 'sub': function(a, b) { return a - b}, 'mul': function(a, b) { return a * b}, 'div': function(a, b) { return a / b} }; calculator.sub(3, 3); // 0 calculator.add(3, 3); // 6 calculator.mul(3, 3); // 9
In this example I created directly an object with some function properties. It looks like a simple calculator, the feature is to assign functions directly to object properties.
(function (){ var max = 20; function square(a) { return a*a; }; for (var i=0; i<max; i++) { document.write("The square of "+i+" is "+square(i)+"<br/>"); } })();
If you offer scriptlets to include a function directly in other peoples html-code it would be great to define no new variables in the global namespace. A common solution is to put your code in a anonymous function, work with local variables and execute this function directly. Remember: the return value of a function definition is the function himself, and the parentheses after a function will execute it: syntax is (function(){;})() .
function sequence() { return arguments.callee.internal++; } sequence.internal = 0; sequence(); // -> 0 sequence(); // -> 1 sequence(); // -> 2 sequence(); // -> 3
How to build a simple singleton - a function who produce a sequence. We don't need to define a new prototype or a global variable. First we define a function. Because its a Object we can set a property named internal and assign it with 0. When calling the function with the callee-reference we can access to the property internal (Yes, its not internal - it is public accessible for everyone).
eval is evil
eval("function square(n){return n*n;}")
eval is a global function! It evaluate the JavaScript given as the first parameter string and return the last statement (if more included). Don't use the second parameter (JS 1.5 Definition) or try to use the eval property of window or this (Rakesh Blogpost)!
Now eval works like expected:
var test = function(){return eval("var i1 = 5; i2 = 7;");}; var i1 = 1; var i2 = 2; var i3 = test(); var i4 = eval(test());
First the function test will filled with to assignments in the function body and returning the result of eval. After executing i1 is not changed, because in test there is a new local i1 defined. After this the global variable i2 is overwritten with 7. And the result of test() is 7. The end test returns only the result of test because if eval don't get a string as argument it does nothing and returning the argument[0] !
Use eval only if you really need it. For example don't use it for accessing object properties like this:
ob = {"a1": "yeah", "a2": "other", "a3": "more"); //Accessing via eval for (i=1;i<4;i++) { document.write(i + " = " + eval("ob.a"+i)); } //Accessing via [name] for (i=1;i<4; i++) { document.write(i + ": " + ob["a"+i]); }
conclusion
After reading this very detailed article about the different possibilities in which a JavaScript function can be defined, hopefully you won't ever again stumble upon a specimen without recognizing it as such.
Matthias Reuter
Latest posts by Matthias Reuter (see all)
- On the phone with Mom – Throttle and Delay DOM-Events in Javascript - November 17, 2011
- So your dropdowns won’t open in IE7? - November 11, 2011
- User-agent sniffing is back - February 15, 2011