Reader small image

You're reading from  Professional JavaScript for Web Developers - Fourth Edition

Product typeBook
Published inNov 2019
Reading LevelBeginner
PublisherWiley
ISBN-139781119366447
Edition4th Edition
Languages
Right arrow
Author (1)
Matt Frisbie
Matt Frisbie
author image
Matt Frisbie

Matt Frisbie has worked in web development for over a decade. During that time, he's been a startup co-founder, an engineer at a Big Four tech company, and the first engineer at a Y Combinator startup that would eventually become a billion-dollar company. As a Google software engineer, Matt worked on both the AdSense and Accelerated Mobile Pages (AMP) platforms; his code contributions run on most of the planet's web browsing devices. Prior to this, Matt was the first engineer at DoorDash, where he helped lay the foundation for a company that has become the leader in online food delivery. Matt has written two books and recorded two video series for O'Reilly and Packt, speaks at frontend meetups and web casts, and is a level 1 sommelier. He majored in Computer Engineering at the University of Illinois Urbana-Champaign. Matt's Twitter handle is @mattfriz.
Read more about Matt Frisbie

Right arrow

10
Functions

WHAT'S IN THIS CHAPTER?

  • Function expressions, function declarations, and arrow functions
  • Default parameters and spread operators
  • Recursion with functions
  • Private variables using closures

WROX.COM DOWNLOADS FOR THIS CHAPTER

Please note that all the code examples for this chapter are available as a part of this chapter's code download on the book's website at www.wrox.com/go/projavascript4e on the Download Code tab.

Some of the most interesting parts of ECMAScript are its functions, primarily because functions actually are objects. Each function is an instance of the Function type that has properties and methods just like any other reference type. Because functions are objects, function names are simply pointers to function objects and are not necessarily tied to the function itself. Functions are typically defined using function-declaration syntax, as in this example:

function sum (num1, num2) {
 return num1 + num2;
}

In this code, a variable sum is...

ARROW FUNCTIONS

New in ECMAScript 6 is the capability to define a function expression using the fat-arrow syntax. For the most part, arrow functions instantiate function objects that behave in the same manner as their formal function expression counterparts. Anywhere a function expression can be used, an arrow function can also be used:

let arrowSum = (a, b) => {
 return a + b;
};

let functionExpressionSum = function(a, b) {
 return a + b;
};

console.log(arrowSum(5, 8)); // 13 
console.log(functionExpressionSum(5, 8)); // 13 

Arrow functions are exceptionally useful in inline situations where they offer a more succinct syntax:

let ints = [1, 2, 3];

console.log(ints.map(function(i) { return i + 1; }));  // [2, 3, 4]
console.log(ints.map((i) => { return i + 1 }));        // [2, 3, 4]

Arrow functions do not require the parentheses if you only want to use a single parameter. If you want to have zero parameters, or more than one parameter, parentheses are required:

// Both are...

FUNCTION NAMES

Because function names are simply pointers to functions, they act like any other variable containing a pointer to an object. This means it's possible to have multiple names for a single function, as in this example:

function sum(num1, num2) {
 return num1 + num2;
}
 
console.log(sum(10, 10));        // 20
          
let anotherSum = sum;    
console.log(anotherSum(10, 10)); // 20
          
sum = null;    
console.log(anotherSum(10, 10)); // 20

This code defines a function named sum() that adds two numbers together. A variable, anotherSum, is declared and set equal to sum. Note that using the function name without parentheses accesses the function pointer instead of executing the function. At this point, both anotherSum and sum point to the same function, meaning that anotherSum() can be called and a result returned. When sum is set to null, it severs its relationship with the function, although anotherSum() can still be called without any problems.

All function...

UNDERSTANDING ARGUMENTS

Function arguments in ECMAScript don't behave in the same way as function arguments in most other languages. An ECMAScript function doesn't care how many arguments are passed in, nor does it care about the data types of those arguments. Just because you define a function to accept two arguments doesn't mean you can pass in only two arguments. You could pass in one or three or none, and the interpreter won't complain.

This indifference happens because arguments in ECMAScript are represented as an array internally. The array is always passed to the function, but the function doesn't care what (if anything) is in the array. If the array arrives with zero items, that's fine; if it arrives with more, that's okay, too. In fact, when a function is defined using the function keyword (meaning a non-arrow function), there actually is an arguments object that can be accessed while inside a function to retrieve the values of each argument...

NO OVERLOADING

ECMAScript functions cannot be overloaded in the traditional sense. In other languages, such as Java, it is possible to write two definitions of a function as long as their signatures (the type and number of arguments accepted) are different. As just discussed, functions in ECMAScript don't have signatures because the arguments are represented as an array containing zero or more values. Without function signatures, true overloading is not possible.

If two functions are defined to have the same name in ECMAScript, it is the last function that becomes the owner of that name. Consider the following example:

function addSomeNumber(num) {
 return num + 100;
}
          
function addSomeNumber(num) {
 return num + 200;
}
          
let result = addSomeNumber(100); // 300

Here, the function addSomeNumber() is defined twice. The first version of the function adds 100 to the argument, and the second adds 200. When the last line is called, it returns 300 because the second...

DEFAULT PARAMETER VALUES

In ECMAScript 5.1 and before, a common strategy for implementing default parameter values was to determine if a parameter was not provided to the function invocation by checking if it was undefined, and assigning a value to the parameter if that was the case:

function makeKing(name) {
 name = (typeof name !== 'undefined') ? name : 'Henry';
 return `King ${name} VIII`;
}

console.log(makeKing());        // 'King Henry VIII'
console.log(makeKing('Louis')); // 'King Louis VIII'

This is no longer required in ECMAScript 6, as it supports explicitly defining values for parameters if they are not provided when the function is invoked. The equivalent of the previous function with ES6 default parameters is done using the = operator directly inside the function signature:

function makeKing(name = 'Henry') {
 return `King ${name} VIII`;
}

console.log(makeKing('Louis')); // 'King Louis VIII&apos...

SPREAD ARGUMENTS AND REST PARAMETERS

ECMAScript 6 introduces the spread operator, which allows for a very elegant way of managing and grouping collections. One of its most useful applications is in the domain of function signatures where it shines especially brightly in the domain of weak typing and variable length arguments. The spread operator is useful both when invoking a function, as well as when defining a function's parameters.

Spread Arguments

Instead of passing an array as a single function argument, it is often useful to be able to break apart an array of values and individually pass each value as a separate argument.

Suppose you have the following function defined, which sums all the values passed as arguments:

let values = [1, 2, 3, 4];

function getSum() {
 let sum = 0;
 for (let i = 0; i < arguments.length; ++i) {
  sum += arguments[i];
 }
 return sum;
}

This function expects each of its arguments to be an individual number that will be iterated through to find...

FUNCTION DECLARATIONS VERSUS FUNCTION EXPRESSIONS

Throughout this section, the function declaration and function expression are referred to as being almost equivalent. This hedging is due to one major difference in the way that a JavaScript engine loads data into the execution context. Function declarations are read and available in an execution context before any code is executed, whereas function expressions aren't complete until the execution reaches that line of code. Consider the following:

// OK
console.log(sum(10, 10));
function sum(num1, num2) {
 return num1 + num2;
}

This code runs perfectly because function declarations are read and added to the execution context before the code begins running through a process called function declaration hoisting. As the code is being evaluated, the JavaScript engine does a first pass for function declarations and pulls them to the top of the source tree. So even though the function declaration appears after its usage in the code, the...

FUNCTIONS AS VALUES

Because function names in ECMAScript are nothing more than variables, functions can be used anywhere variables can be used. This means it's possible not only to pass a function into another function as an argument, but also to return a function as the result of another function. Consider the following function:

function callSomeFunction(someFunction, someArgument) {
 return someFunction(someArgument);
}

This function accepts two arguments. The first argument should be a function, and the second argument should be a value to pass to that function. Any function can then be passed in as follows:

function add10(num) {
 return num + 10;
}
          
let result1 = callSomeFunction(add10, 10);
console.log(result1); // 20
          
function getGreeting(name) {
 return "Hello, " + name;
}
          
let result2 = callSomeFunction(getGreeting, "Nicholas");
console.log(result2);  // "Hello, Nicholas"

The callSomeFunction() function is generic...

FUNCTION INTERNALS

In ECMAScript 5, two special objects exist inside a function: arguments and this. In ECMAScript 6, the new.target property was introduced.

arguments

The arguments object, as discussed previously, is an array-like object that contains all of the arguments that were passed into the function. It is only available when a function is declared using the function keyword (as opposed to arrow function declaration). Though its primary use is to represent function arguments, the arguments object also has a property named callee, which is a pointer to the function that owns the arguments object. Consider the following classic factorial function:

function factorial(num) {
 if (num <= 1) {
  return 1;
 } else {
  return num * factorial(num - 1);
 }
}

Factorial functions are typically defined to be recursive, as in this example, which works fine when the name of the function is set and won't be changed. However, the proper execution of this function is tightly coupled...

FUNCTION PROPERTIES AND METHODS

Functions are objects in ECMAScript and, as mentioned previously, therefore have properties and methods. Each function has two properties: length and prototype. The length property indicates the number of named arguments that the function expects, as in this example:

function sayName(name) {
 console.log(name);
}   
          
function sum(num1, num2) {
 return num1 + num2;
}
          
function sayHi() {
 console.log("hi");
}
          
console.log(sayName.length); // 1
console.log(sum.length);     // 2
console.log(sayHi.length);   // 0

This code defines three functions, each with a different number of named arguments. The sayName() function specifies one argument, so its length property is set to 1. Similarly, the sum() function specifies two arguments, so its length property is 2, and sayHi() has no named arguments, so its length is 0.

The prototype property is perhaps the most interesting part of the ECMAScript core. The prototype is the...

FUNCTION EXPRESSIONS

One of the more powerful, and often confusing, parts of JavaScript is function expressions. There are two ways to define a function: by function declaration and by function expression. The first, function declaration, has the following form:

function functionName(arg0, arg1, arg2) {
 // function body
}

One of the key characteristics of function declarations is function declaration hoisting, whereby function declarations are read before the code executes. That means a function declaration may appear after code that calls it and still work:

sayHi();
function sayHi() {
 console.log("Hi!");
}

This example doesn't throw an error because the function declaration is read first before the code begins to execute.

The second way to create a function is by using a function expression. Function expressions have several forms. The most common is as follows:

let functionName = function(arg0, arg1, arg2) {
 // function body
};

This pattern of function expression...

RECURSION

A recursive function typically is formed when a function calls itself by name, as in the following example:

function factorial(num) {
 if (num <= 1) {
  return 1;
 } else {
  return num * factorial(num - 1);
 }
}

This is the classic recursive factorial function. Although this works initially, it's possible to prevent it from functioning by running the following code immediately after it:

let anotherFactorial = factorial;
factorial = null;
console.log(anotherFactorial(4)); // error!

Here, the factorial() function is stored in a variable called anotherFactorial. The factorial variable is then set to null, so only one reference to the original function remains. When anotherFactorial() is called, it will cause an error because it will try to execute factorial(), which is no longer a function. Using arguments.callee can alleviate this problem.

Recall that arguments.callee is a pointer to the function being executed and, as such, can be used to call the function recursively...

TAIL CALL OPTIMIZATION

The ECMAScript 6 specification also introduced a memory management optimization that allows the JavaScript engine to reuse stack frames when certain conditions are met. Specifically, this optimization pertains to “tail calls”, where the return value of an outer function is the returned value of an inner function, as follows:

function outerFunction() {
 return innerFunction(); // tail call
}

Prior to the ES6 optimization, executing this example would have the following effect in memory:

  1. Execution reaches outerFunction body, first stack frame is pushed onto stack.
  2. Body of outerFunction executes, return statement is reached. To evaluate the return statement, innerFunction must be evaluated.
  3. Execution reaches innerFunction body, second stack frame is pushed onto stack.
  4. Body of innerFunction executes, and its returned value is evaluated.
  5. Return value is passed back to outerFunction, which in turn can return that value.
  6. Stack frames are popped off the...

CLOSURES

The terms anonymous functions and closures are often incorrectly used interchangeably. Closures are functions that have access to variables from another function's scope. This is often accomplished by creating a function inside a function, as in the following highlighted lines from the previous createComparisonFunction() example:

function createComparisonFunction(propertyName) {         
 return function(object1, object2) {
  let value1 = object1[propertyName];
  let value2 = object2[propertyName];

  if (value1 < value2) {
   return -1;
  } else if (value1> value2) {
   return 1;
  } else {
   return 0;
  }
 };
}

The highlighted lines in this example are part of the inner function (an anonymous function) that is accessing a variable (propertyName) from the outer function. Even after the inner function has been returned and is being used elsewhere, it has access to that variable. This occurs because the inner function's scope chain includes the scope of createComparisonFunction...

IMMEDIATELY INVOKED FUNCTION EXPRESSIONS

An anonymous function that is called immediately is most often called an immediately invoked function expression (IIFE). It resembles a function declaration, but because it is enclosed in parentheses it is interpreted as a function expression. This function is then called via the second set of parentheses at the end. The basic syntax is as follows:

(function() {
 // block code here
})();

The use of an IIFE to simulate block scope uses values defined inside a function expression that is executed immediately, thereby offering a block scope–like behavior using function scoped variables. (The utility of the IIFE was much greater in previous versions of ECMAScript 6 where block scoped variables were not supported.) Consider the following example:

// IIFE
(function () {
 for (var i = 0; i < count; i++) {
  console.log(i);
 }
})();
  
console.log(i); // Throws an error

The preceding code will error when the console.log() outside the IIFE...

PRIVATE VARIABLES

Strictly speaking, JavaScript has no concept of private members; all object properties are public. There is, however, a concept of private variables. Any variable defined inside a function or block is considered private because it is inaccessible outside that function. This includes function arguments, local variables, and functions defined inside other functions. Consider the following:

function add(num1, num2) {
 let sum = num1 + num2;
 return sum;
} 

In this function, there are three private variables: num1, num2, and sum. These variables are accessible inside the function but can't be accessed outside it. If a closure were to be created inside this function, it would have access to these variables through its scope chain. Using this knowledge, you can create public methods that have access to private variables.

A privileged method is a public method that has access to private variables and/or private functions. There are two ways to create privileged methods...

SUMMARY

Functions are useful and versatile tools in JavaScript programming. ECMAScript 6 introduces powerful syntax allowing you to wield them even more effectively.

  • Function expressions are different from function declarations. Function declarations require names, while function expressions do not. A function expression without a name is also called an anonymous function.
  • New in ES6, arrow functions are similar to function expressions, but with some important differences.
  • Arguments and parameters in JavaScript functions are highly flexible. The arguments object, along with the new spread operator in ES6, allow for totally dynamic definition and invocation.
  • Internally, functions expose a handful of objects and references that provide you information on how the function was invoked, where it was invoked, and what was originally passed to it.
  • Engines will optimize functions with tail calls to preserve stack space.
  • Behind the scenes, the closure's scope chain contains a variable object...
lock icon
The rest of the chapter is locked
You have been reading a chapter from
Professional JavaScript for Web Developers - Fourth Edition
Published in: Nov 2019Publisher: WileyISBN-13: 9781119366447
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Matt Frisbie

Matt Frisbie has worked in web development for over a decade. During that time, he's been a startup co-founder, an engineer at a Big Four tech company, and the first engineer at a Y Combinator startup that would eventually become a billion-dollar company. As a Google software engineer, Matt worked on both the AdSense and Accelerated Mobile Pages (AMP) platforms; his code contributions run on most of the planet's web browsing devices. Prior to this, Matt was the first engineer at DoorDash, where he helped lay the foundation for a company that has become the leader in online food delivery. Matt has written two books and recorded two video series for O'Reilly and Packt, speaks at frontend meetups and web casts, and is a level 1 sommelier. He majored in Computer Engineering at the University of Illinois Urbana-Champaign. Matt's Twitter handle is @mattfriz.
Read more about Matt Frisbie