A function is a container of code statements that can be invoked using the parentheses ()
operator. Parameters can be passed inside of the parentheses during invocation so that the statements in the function can access certain values when the function is invoked.
In the following code, we create two versions of an addNumbers
function objectone using the new
operator and another using the more common literal pattern. Both are expecting two parameters. In each case, we invoke the function, passing parameters in the parentheses ()
operator.
Sample: sample76.html
<!DOCTYPE html><html lang="en"><body><script> var addNumbersA = new Function('num1', 'num2', 'return num1 + num2'); console.log(addNumbersA(2, 2)); // Logs 4. // Could also be written the literal way, which is much more common. var addNumbersB = function (num1, num2) { return num1 + num2; }; console.log(addNumbersB(2, 2)); // Logs 4. </script></body></html>
A function can be used to return a value, construct an object, or as a mechanism to simply run code. JavaScript has several uses for functions, but in its most basic form a function is simply a unique scope of executable statements.
Function()
Parameters
The Function()
constructor takes an indefinite number of parameters, but the last parameter expected by the Function()
constructor is a string containing statements that comprise the body of the function. Any parameters passed to the constructor before the last will be available to the function being created. Its also possible to send multiple parameters as a comma-separated string.
In the following code, I contrast the usage of the Function()
constructor with the more common patterns of instantiating a function object.
Sample: sample77.html
<!DOCTYPE html><html lang="en"><body><script> var addFunction = new Function('num1', 'num2', 'return num1 + num2'); /* Alternately, a single comma-separated string with arguments can be the first parameter of the constructor, with the function body following. */ var timesFunction = new Function('num1,num2', 'return num1 * num2'); console.log(addFunction(2, 2), timesFunction(2, 2)); // Logs '4 4' // Versus the more common patterns for instantiating a function: var addFunction = function (num1, num2) { return num1 + num2; }; // Expression form. function addFunction(num1, num2) { return num1 + num2; } // Statement form. </script></body></html>
Directly leveraging the Function()
constructor is not recommended or typically ever done because JavaScript will use eval()
to parse the string containing the functions logic. Many consider eval()
to be unnecessary overhead. If its in use, a flaw in the design of the code is highly possible.
Using the Function()
constructor without the new
keyword has the same effect as using only the constructor to create function objects (new Function('x','return x')
vs. function(('x','return x')
).
No closure is created when invoking the Function()
constructor directly.
Function()
Properties and Methods
The function object has the following properties (not including inherited properties and methods):
Properties (Function.prototype;
):
Function Object Instance Properties and Methods
Function object instances have the following properties and methods (not including inherited properties and methods):
Instance Properties (var myFunction = function(x, y, z) {};
myFunction.length;
):
Instance Methods (var myFunction = function(x, y, z) {};
myFunction.toString();
):
Functions Always Return a Value
While its possible to create a function simply to execute code statements, its also very common for a function to return a value. In the following sample, we are returning a string from the sayHi
function.
Sample: sample78.html
<!DOCTYPE html><html lang="en"><body><script> var sayHi = function () { return 'Hi'; }; console.log(sayHi()); // Logs "Hi". </script></body></html>
If a function does not specify a return value, undefined
is returned. In the following sample, we call the yelp
function which logs the string 'yelp' to the console without explicitly returning a value.
Sample: sample79.html
<!DOCTYPE html><html lang="en"><body><script> var yelp = function () { console.log('I am yelping!'); // Functions return undefined even if we don't. } /* Logs true because a value is always returned, even if we don't specifically return one. */ console.log(yelp() === undefined); </script></body></html>
The concept to take away here is that all functions return a value, even if you do not explicitly provide a value to return. If you do not specify a value to return, the value returned is undefined
.
Functions Are First-Class Citizens (Not Just Syntax, but Values)
In JavaScript, functions are objects. This means that a function can be stored in a variable, array, or object. Also, a function can be passed to and returned from a function. A function has properties because it is an object. All of these factors make functions first-class citizens in JavaScript.
Sample: sample80.html
<!DOCTYPE html><html lang="en"><body><script> // Functions can be stored in variables (funcA), arrays (funcB), and objects (funcC). var funcA = function () { }; // Called like so: funcA() var funcB = [function () { } ]; // Called like so: funcB[0]() var funcC = { method: function () { } }; // too.method() or funcC['method']() // Functions can be sent to and sent back from functions. var funcD = function (func) { return func }; var runFuncPassedToFuncD = funcD(function () { console.log('Hi'); }); runFuncPassedToFuncD(); // Functions are objects, which means they can have properties. var funcE = function () { }; funcE.answer = 'yup'; // Instance property. console.log(funcE.answer); // Logs 'yup'. </script></body></html>
It is crucial that you realize a function is an object, and thus a value. It can be passed around or augmented like any other expression in JavaScript.
Passing Parameters to a Function
Parameters are vehicles for passing values into the scope of a function when it is invoked. In the following sample we invoke addFunction()
. Since we have predefined it to take two parameters, two added values become available within its scope.
Sample: sample81.html
<!DOCTYPE html><html lang="en"><body><script> var addFunction = function (number1, number2) { var sum = number1 + number2; return sum; } console.log(addFunction(3, 3)); // Logs 6. </script></body></html>
In contrast to some other programming languages, it is perfectly legal in JavaScript to omit parameters even if the function has been defined to accept these arguments. The missing parameters are simply given the value undefined
. Of course, by leaving out values for the parameters, the function might not work properly.
If you pass a function unexpected parameters (those not defined when the function was created), no error will occur. And it's possible to access these parameters from the arguments
object, which is available to all functions.
this
and arguments
Values Are Available to All Functions
Inside the scope and body of all functions, the this
and arguments
values are available.
The arguments
object is an array-like object containing all of the parameters being passed to the function. In the following code, even though we forgo specifying parameters when defining the function, we can rely on the arguments
array passed to the function to access parameters if they are sent upon invocation.
Sample: sample82.html
<!DOCTYPE html><html lang="en"><body><script> var add = function () { return arguments[0] + arguments[1]; }; console.log(add(4, 4)); // Returns 8. </script></body></html>
The this
keyword, passed to all functions, is a reference to the object that contains the function. As you might expect, functions contained within objects as properties (methods) can use this
to gain a reference to the parent object. When a function is defined in the global scope, the value of this
is the global object. Review the following code and make sure you understand what this
is returning.
Sample: sample83.html
<!DOCTYPE html><html lang="en"><body><script> var myObject1 = { name: 'myObject1', myMethod: function () { console.log(this); } }; myObject1.myMethod(); // Logs 'myObject1'. var myObject2 = function () { console.log(this); }; myObject2(); // Logs window. </script></body></html>
The arguments.callee
Property
The arguments
object has a property called callee
, which is a reference to the function currently executing. This property can be used to reference the function from within the scope of the function (arguments.callee
)a self-reference. In the following code, we use this property to gain a reference to the calling function.
Sample: sample84.html
<!DOCTYPE html><html lang="en"><body><script> var foo = function foo() { console.log(arguments.callee); // Logs foo() // callee could be used to invoke recursively the foo function (arguments.callee()) } (); </script></body></html>
This can be useful when a function needs to be called recursively.
The Function Instance length
Property and arguments.length
The arguments
object has a unique length
property. While you might think this length property will give you the number of defined arguments, it actually gives the number of parameters sent to the function during invocation.
Sample: sample85.html
<!DOCTYPE html><html lang="en"><body><script> var myFunction = function (z, s, d) { return arguments.length; }; console.log(myFunction()); // Logs 0 because no parameters were passed to the function. </script></body></html>
Using the length
property of all Function()
instances, we can actually grab the total number of parameters the function is expecting.
Sample: sample86.html
<!DOCTYPE html><html lang="en"><body><script> var myFunction = function (z, s, d, e, r, m, q) { return myFunction.length; }; console.log(myFunction()); // Logs 7. </script></body></html>
The arguments.length
property was deprecated in JavaScript 1.4, but the number of arguments sent to a function can be accessed from the length
property of the function object. Moving forward, you can get the length value by leveraging the callee
property to first gain reference to the function being invoked (arguments.callee.length
).
Redefining Function Parameters
A functions parameters can be redefined inside the function either directly, or by using the arguments
array. Take a look at this code:
Sample: sample87.html
<!DOCTYPE html><html lang="en"><body><script> var foo = false; var bar = false; var myFunction = function (foo, bar) { arguments[0] = true; bar = true; console.log(arguments[0], bar); // Logs true true. } myFunction(); </script></body></html>
Notice that I can redefine the value of the bar parameter using the arguments
index or by directly reassigning a new value to the parameter.
Return a Function Before It Is Done (Cancel Function Execution)
Functions can be cancelled at any time during invocation by using the return
keyword with or without a value. In the following sample, we are canceling the add
function if the parameters are undefined or not a number.
Sample: sample88.html
<!DOCTYPE html><html lang="en"><body><script> var add = function (x, y) { // If the parameters are not numbers, return error. if (typeof x !== 'number' || typeof y !== 'number') { return 'pass in numbers'; } return x + y; } console.log(add(3, 3)); // Logs 6. console.log(add('2', '2')); // Logs 'pass in numbers'. </script></body></html>
The concept to take away here is that you can cancel a function's execution by using the return
keyword at any point in the execution of the function.
Defining a Function (Statement, Expression, or Constructor)
A function can be defined in three different ways: a function constructor, a function statement, or a function expression. In the following example, I demonstrate each variation.
Sample: sample89.html
<!DOCTYPE html><html lang="en"><body><script> /* Function constructor: The last parameter is the function logic, everything before it is a parameter. */ var addConstructor = new Function('x', 'y', 'return x + y'); // Function statement. function addStatement(x, y) { return x + y; } // Function expression. var addExpression = function (x, y) { return x + y; }; console.log(addConstructor(2, 2), addStatement(2, 2), addExpression(2, 2)); // Logs '4 4 4'. </script></body></html>
Some have said that there is a fourth type of definition for functions, called the "named function expression." A named function expression is simply a function expression that also contains a name (e.g., var add = function add(x, y) {return x+y}
).
Invoking a Function (Function, Method, Constructor, or call()
and apply()
)
Functions are invoked using four different scenarios or patterns.
- As a function
- As a method
- As a constructor
- Using
apply()
orcall()
In the following sample, we examine each of these invocation patterns.
Sample: sample90.html
<!DOCTYPE html><html lang="en"><body><script> // Function pattern. var myFunction = function () { return 'foo' }; console.log(myFunction()); // Logs 'foo'. // Method pattern. var myObject = { myFunction: function () { return 'bar'; } } console.log(myObject.myFunction()); // Logs 'bar'. // Constructor pattern. var Cody = function () { this.living = true; this.age = 33; this.gender = 'male'; this.getGender = function () { return this.gender; }; } var cody = new Cody(); // Invoke via the Cody constructor. console.log(cody); // Logs the cody object and properties. // apply() and call() pattern. var greet = { runGreet: function () { console.log(this.name, arguments[0], arguments[1]); } } var cody = { name: 'cody' }; var lisa = { name: 'lisa' }; // Invoke the runGreet function as if it were inside of the cody object. greet.runGreet.call(cody, 'foo', 'bar'); // Logs 'cody foo bar'. // Invoke the runGreet function as if it were inside of the lisa object. greet.runGreet.apply(lisa, ['foo', 'bar']); // Logs 'lisa foo bar'. /* Notice the difference between call() and apply() in how parameters are sent to the function being invoked. */ </script></body></html>
Make sure you are aware of all four of the invocation patterns, as code you will encounter may contain any of them.
Anonymous Functions
An anonymous function is a function that is not given an identifier. Anonymous functions are mostly used for passing functions as a parameter to another function.
Sample: sample91.html
<!DOCTYPE html><html lang="en"><body><script> // function(){console.log('hi');}; // Anonymous function, but no way to invoke it. // Create a function that can invoke our anonymous function. var sayHi = function (f) { f(); // Invoke the anonymous function. } // Pass an anonymous function as a parameter. sayHi(function () { console.log('hi'); }); // Logs 'hi'. </script></body></html>
Self-Invoking Function Expression
A function expression (really any function except one created from the Function()
constructor) can be immediately invoked after definition by using the parentheses operator. In the following sample, we create a sayWord()
function expression and then immediately invoke the function. This is considered to be a self-invoking function.
Sample: sample92.html
<!DOCTYPE html><html lang="en"><body><script> var sayWord = function () { console.log('Word 2 yo mo!'); } (); // Logs 'Word 2 yo mo!' </script></body></html>
Self-Invoking Anonymous Function Statements
Its possible to create an anonymous function statement that is self-invoked. This is called a self-invoking anonymous function. In the following sample, we create several anonymous functions that are immediately invoked.
Sample: sample93.html
<!DOCTYPE html><html lang="en"><body><script> // Most commonly used/seen in the wild. (function (msg) { console.log(msg); })('Hi'); // Slightly different, but achieving the same thing: (function (msg) { console.log(msg) } ('Hi')); // The shortest possible solution. !function sayHi(msg) { console.log(msg); } ('Hi'); // FYI, this does NOT work! // function sayHi() {console.log('hi');}(); </script></body></html>
According to the ECMAScript standard, the parentheses around the function (or anything that transforms the function into an expression) are required if the function is to be invoked immediately.
Functions Can Be Nested
Functions can be nested inside of other functions indefinitely. In the following code sample, we encapsulate the goo
function inside of the bar
function, which is inside of the foo
function.
Sample: sample94.html
<!DOCTYPE html><html lang="en"><body><script> var foo = function () { var bar = function () { var goo = function () { console.log(this); // Logs reference to head window object. } (); } (); } (); </script></body></html>
The simple concept here is that functions can be nested and there is no limit to how deep the nesting can go.
Remember, the value of this
for nested functions will be the head object (the window
object in a web browser) in JavaScript 1.5, ECMA-262, Edition 3.
Passing Functions to Functions and Returning Functions From Functions
As previously mentioned, functions are first-class citizens in JavaScript. And since a function is a value, and a function can be passed any sort of value, a function can be passed to a function. Functions that take and/or return other functions are sometimes called "higher-order functions.
In the following code, we are passing an anonymous function to the foo
function which we then immediately return from the foo
function. It is this anonymous function that the variable bar
points to, since foo
accepts and then returns the anonymous function.
Sample: sample95.html
<!DOCTYPE html><html lang="en"><body><script> // Functions can be sent to, and sent back from, functions. var foo = function (f) { return f; } var bar = foo(function () { console.log('Hi'); }); bar(); // Logs 'Hi'. </script></body></html>
So when bar
is invoked, it invokes the anonymous function that was passed to the foo()
function, which is then passed back from the foo()
function and referenced from the bar
variable. All this is to showcase the fact that functions can be passed around just like any other value.
Invoking Function Statements Before They Are Defined (aka Function Hoisting)
A function statement can be invoked during execution before its actual definition. This is a bit odd, but you should be aware of it so you can leverage it, or at least know what’s going on when you encounter it. In the following sample, I invoke the sayYo()
and sum()
function statements before they are defined.
Sample: sample96.html
<!DOCTYPE html><html lang="en"><body><script> // Example 1 var speak = function () { sayYo(); // sayYo() has not been defined yet, but it can still be invoked, logs 'yo'. function sayYo() { console.log('Yo'); } } (); // Invoke // Example 2 console.log(sum(2, 2)); // Invoke sum(), which is not defined yet, but can still be invoked. function sum(x, y) { return x + y; } </script></body></html>
This happens because before the code runs, function statements are interpreted and added to the execution stack/context. Make sure you are aware of this as you use function statements.
Functions defined as function expressions are not hoisted. Only function statements are hoisted.
A Function Can Call Itself (aka Recursion)
It’s perfectly legitimate for a function to call itself. In fact, this is often used in well-known coding patterns. In the code that follows, we kick off the countDownFrom
function, which then calls itself via the function name countDownFrom
. Essentially, this creates a loop that counts down from 5 to 0.
Sample: sample97.html
<!DOCTYPE html><html lang="en"><body><script> var countDownFrom = function countDownFrom(num) { console.log(num); num--; // Change the parameter value. if (num < 0) { return false; } // If num < 0 return function with no recursion. // Could have also done arguments.callee(num) if it was an anonymous function. countDownFrom(num); }; countDownFrom(5); // Kick off the function, which logs separately 5, 4, 3, 2, 1, 0. </script></body></html>
You should be aware that it’s natural for a function to invoke itself (aka recursion) or to do so repetitively.
Conclusion
Functions are one of the most used aspects of JavaScript, hopefully you now have a better understanding of how to use them.
Comments