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.

Live Code

<!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.

Live Code

<!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 use eval() to parse the string containing the function’s logic. Many consider eval() 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 the new keyword has the same effect as using only the constructor to create function objects [e.g., new Function('x','return x') versus function(('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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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>

Note

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() or call()

In the code below, we examine each of these invocation patterns.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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.

Live Code

<!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>

Note

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. Below, we encapsulate the goo function inside of the bar function, which is inside of the foo function.

Live Code

<!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.

Note

Remember, the value of this for nested functions will be the head object (e.g., window object in a web browser) in JavaScript 1.5, ECMAScript 3 Edition.

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.

Live Code

<!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.

Live Code

<!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.

Note

Functions, defined as “function expressions” are not hoisted—only “function statements” are hoisted.

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.

Live Code

<!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.