Chapter 4. Function()
Conceptual Overview of Using Function() Objects
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.
Below, we create two versions of an addNumbers
function object—one 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.
<!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. It’s also possible to send multiple parameters
as a comma-separated string.
Below, I contrast the usage of the Function()
constructor with the more common
patterns of instantiating a function object.
<!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>
Notes
Directly leveraging the
Function()
constructor is not recommended or typically ever done because JavaScript will useeval()
to parse the string containing the function’s logic. Many considereval()
to be unnecessary overhead. If it’s in use, a flaw in the design of the code is highly possible.Using the
Function()
constructor without thenew
keyword has the same effect as using only the constructor to create function objects [e.g.,new Function('x','return x')
versusfunction(('x','return x')
].No closure is created (see Chapter 7) when invoking the
Function()
constructor directly.
Function() Properties and Methods
The function object has the following properties (not including inherited properties and methods):
Properties (e.g., Function.prototype;
):
prototype
Function Object Instance Properties and Methods
Function object instances have the following properties and methods:
Instance Properties (e.g.,
var myFunction = function(x, y, z) {};
myFunction.length;
):
arguments
constructor
length
Instance Methods (e.g., var myFunction = function(x, y, z) {};
myFunction.toString();
):
apply()
call()
toString()
Functions Always Return a Value
While it’s possible to create a function simply to execute code
statements, it’s also very common for a function to return a value. Below,
we are returning a string from the sayHi
function.
<!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, then undefined
is returned. Below, we call the
yelp
function, which logs the string
'yelp'
to the console without
explicitly returning a value.
<!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 takeaway 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.
<!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. Below, as we invoke addFunction()
, since we have predefined it to
take two parameters, two added values become available within its
scope.
<!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>
Notes
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 of
undefined
. Of course, by leaving out values for the parameters, the function might not work properly.If you pass unexpected parameters to a function (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 Available To All Functions
Inside the scope/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 code below, 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.
<!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 (i.e.,
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 code below and make sure you understand what this
is returning.
<!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 (e.g.,
arguments.callee
)—a self-reference. In
the code below, we use this property to gain a reference to the calling
function.
<!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 (e.g., 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.
<!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.
<!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>
Note
The arguments.length
property
beginning with JavaScript 1.4 is deprecated, and the number of arguments
sent to a function can be accessed from the length
property of the function object. So,
moving forward, you can get the length value by leveraging the callee
property to first gain reference to the
function being invoked (i.e., arguments.callee.length
).
Redefining Function Parameters
A function’s parameters can be redefined inside the function either
directly, or by using the arguments
array. Take a look at the code below.
<!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. Below, we are canceling the add
function if the parameters are undefined or not a number.
<!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 takeaway 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. Below, I demonstrate each variation.
<!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>
Invoking a Function [Function, Method, Constructor, or call() and apply()]
Functions are invoked using four different scenarios or patterns:
In the code below, we examine each of these invocation patterns.
<!DOCTYPE html><html lang="en"><body><script> // function pattern var myFunction = function(){return 'foo'}; console.log(myFunction()); // log 'foo' // method pattern var myObject = {myFunction: function(){return 'bar';}} console.log(myObject.myFunction()); // log '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 Cody constructor console.log(cody); // logs 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.
<!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 anonymous function } // pass an anonymous function as parameter sayHi(function(){console.log('hi');}); // log '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.
Below, we create a sayWord()
function
expression and then immediately invoke the function. This is considered to
be a self-invoking function.
<!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
It’s possible to create an anonymous function statement that is self-invoked. This is called a self-invoking anonymous function. Below, we create several anonymous functions that are immediately invoked.
<!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>
Functions Can Be Nested
Functions can be nested inside of other functions indefinitely.
Below, we encapsulate the goo
function
inside of the bar
function, which is
inside of the foo
function.
<!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 takeaway here is that functions can be nested and that there is no limit to how deep the nesting can go.
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.”
Below, 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.
<!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 (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.
Below, I invoke the sayYo()
and
sum()
function statements before they
are defined.
<!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.
A Function Can Call Itself (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 below, 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.
<!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 not uncommon for a function to invoke itself (a.k.a. recursion) or to do so repetitively.
Get JavaScript Enlightenment 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.