Master

JavaScript

Functions

Topics

  1. First Class Functions
  2. Higher Order Functions
  3. Callbacks
  4. Call Stack
  5. Closure

First Class Functions

JavaScript has First Class Functions, meaning that functions can be treated just like other data types

function takeResult(anotherFunc) {
    var result = anotherFunc();
    return `The result is ${result}.`.
}

// assign an anonymous function to a variable
var giveHamster = function() {
    return 'hamster';
}

takeResult(giveHamster); // The result is hamster.

Higher Order Functions

  • A HOF is a function that does at least one of the following (or both):
    • Accepts a function as a parameter
    • Returns a function as a result
  • Higher Order Functions are more of a mathematical concept rather than specific to programming languages.

Basic Example

/**
 * Unnecessarily complicated higher order
 *  greet function that takes a name and returns a function
 *
 * @param {string} name - person to greet
 * @returns {function} - the function to call that greets
 */
function greet(name) {
  return function inner() {
    return `hello ${name}`;
  };
}

var pointsToInnerFunc = greet('Whiskey');

pointsToInnerFunc(); // hello Whiskey

Practical Example

function myForEach(arr, cb) {
  for (let i = 0; i < arr.length; i++) {
    cb(arr[i], i);
  }
}

This HOF takes a callback!

myForEach(['matt', 'elie', 'joel'], function(item, idx) {
  console.log(`${item} is at index ${idx}`);
});

/*

  matt is at index 0
  elie is at index 1
  joel is at index 2

 */

Callback Pattern

Lots of built-in functions in JavaScript are Higher Order Functions ( HOFs) that are designed to accept callbacks.

The callback pattern usually looks like this:

function greeting(name) {
  alert('Hello ' + name);
}

function processUserInput(callback) {
  var name = prompt('Please enter your name.');
  callback(name);
}

processUserInput(greeting);

Callbacks In-Line

Usually callbacks are defined in-line with anonymous functions.

function eachItem(obj, cb) {
  for (let key in obj) {
    cb(key, obj[key]);
  }
}

let obj = { name: 'Whiskey', otherName: 'Hazel' };
eachItem(obj, function(key, val) {
  console.log('The key is ' + key + '. the value is ' + val + '.');
});

// The key is name. The value is Whiskey.
// The key is otherName. The value is Hazel.

Must-Know Functions that Take Callbacks

We'll discuss the following functions that all take callbacks:

1. setTimeOut

2. Array forEach

3. Array map

4. Array filter

5. Array reduce

6. Array sort

1. setTimeOut

setTimeout(function() {
  alert('yoooo');
}, 1000);
  • The first argument is a callback function
  • The second argument is the number of milliseconds to wait before executing the callback

2. Array forEach

[1,2,3].forEach(function(element, index) {
  console.log(element, index);
});

/*
    1 0
    2 1
    3 2
 */
  • There is a new callback function for every array element.
  • The functions take two arguments, the current array element and the current index.
  • The functions return undefined

3. Array map

var oldArr = [1,2,3];
var newArr = oldArr.map(function(element, index) {
  return element * index;
});

console.log(newArr);

/*
    [0, 2, 6]
*/
  • The map method returns an entirely new array
  • There is a new callback function for every array element.
  • The functions take two arguments: the current array element and the current index.
  • The return value of each callback is the new element value in the new array.

4. Array filter

var oldArr = [1,2,3,4,5,6,7,8,9,10]
var newArr = oldArr.filter(function(element, index) {
  return element % 2 === 0;
});

console.log(newArr);

/*
    [2, 4, 6, 8, 10]
*/
  • There is a new callback function for every array element.
  • The callbacks take two arguments: the current array element and the current index.
  • The filter method returns an entirely new array
  • The return value of each callback is true / false whether the element should be included in the new array

5. Array reduce

var oldArr = [3, 2, 1]
var countDown = oldArr.reduce(function(previous, current, index) {
  return previous + ' ' + current;
}, "ready");

console.log(countDown);

/*
    ready 3 2 1;
*/
  • There is a new callback function for every array element.
  • The callbacks take three arguments: the previous callback's return value, the current element, and the current index.
  • The reduce method can transform an array into any other type
  • The second argument is the optional initial value

6. Array sort

var myArr = [10, 3, 2, 7, 9, 11, 1];
myArr.sort(function(a, b) {
  return a - b;
})

console.log(myArr);
/*
    1, 2, 3, 7, 9, 10, 11
*/
  • Array sort modifies the current array in-place
  • The callback is a comparator function that gets passed to the underlying sorting algorithm
  • For any two numbers a and b, return 0, 1, or -1.
    • 0 means they are equal
    • greater than 0 means a is greater than b
    • less than 0  means b is greater than a

Call Stack & Execution Context

  • When JavaScript executes a function, it creates a call object that gets pushed on top the call stack
  • The call stack is a stack data structure. You put the most recently-executed function on top of the stack

myFunc

()

{ myFunc }

function myFunc(callback) {
  return callback();
}

function myCallBack() {
  console.log('foo');
}

myFunc(myCallBack);

Call Stack

  • When another function gets called within the first one (such as a callback), its call object gets placed on top of the current function in the call stack

myCallback

()

{ myFunc }

{ myCallback }

function myFunc(callback) {
  return callback();
}

function myCallBack() {
  console.log('foo');
}

myFunc(myCallBack);

Call Stack

As functions return, their call objects get popped off the top of the call stack, and the next item down resumes execution until the stack is empty

{ myFunc }

{ myCallback }

{ myFunc }

return
return

Execution Context

  • Each call object carries a reference to its scope as well as its parents' scopes via its execution context.

{ myFunc }

{ myCallback }

  • In our first example, myFunc's execution context references the global object, and myCallback's execution context references myFunc
function myFunc(callback) {
  return callback();
}

function myCallBack() {
  console.log('foo');
}

myFunc(myCallBack);

Closure

Closure is when an inner function "remembers" variables in the outer function's scope...even after the outer function has executed.

Closure

We say variables are "closed over."

function closureCounter() {

  var count = 0;

  return function innerFunction() {
    return ++count;
  };

}

The inner function must reference the count variable of its parent for closure to occur!

Closure



/**
 * A Higher Order Function w/closure
 */
function closureCounter() {
  
    var count = 0;
  
    return function innerFunction() {
      return ++count;
    };
  
  }
  
  var myCounter = closureCounter();

{ closureCounter }

Call Stack

Memory Heap

Closure



/**
 * A Higher Order Function w/closure
 */
function closureCounter() {
  
    var count = 0;
  
    return function innerFunction() {
      return ++count;
    };
  
  }
  
  var myCounter = closureCounter(); 

{ count }

Call Stack

Memory Heap

Closure



/**
 * A Higher Order Function w/closure
 */
function closureCounter() {
  
    var count = 0;
  
    return function innerFunction() {
      return ++count;
    };
  
  }
  
  var myCounter = closureCounter(); 
  myCounter(); // 1

{ count }

{ innerFunction }

Call Stack

Memory Heap

Closure



/**
 * A Higher Order Function w/closure
 */
function closureCounter() {
  
    var count = 0;
  
    return function innerFunction() {
      return ++count;
    };
  
  }
  
  var myCounter = closureCounter();
  myCounter(); // 1
  myCounter(); // 2

{ count }

{ innerFunction }

Call Stack

Memory Heap

Closure



/**
 * A Higher Order Function w/closure
 */
function closureCounter() {
  
    var count = 0;
  
    return function innerFunction() {
      return ++count;
    };
  
  }
  
  var myCounter = closureCounter();
  myCounter(); // 1
  myCounter(); // 2

  var mySecondCounter = closureCounter();

{ count }

Call Stack

Memory Heap

{ closureCounter }

Closure



/**
 * A Higher Order Function w/closure
 */
function closureCounter() {
  
    var count = 0;
  
    return function innerFunction() {
      return ++count;
    };
  
  }
  
  var myCounter = closureCounter();
  myCounter(); // 1
  myCounter(); // 2

  var mySecondCounter = closureCounter();
  mySecondCounter(); // 1

{ count }

Call Stack

Memory Heap

{ count }

{ innerFunction }

Closure



/**
 * A Higher Order Function w/closure
 */
function closureCounter() {
  
    var count = 0;
  
    return function innerFunction() {
      return ++count;
    };
  
  }
  
  var myCounter = closureCounter();
  myCounter(); // 1
  myCounter(); // 2

  var mySecondCounter = closureCounter();
  mySecondCounter(); // 1

  myCounter(); // 3

{ count }

Call Stack

Memory Heap

{ count }

{ innerFunction }

Applied Closure:

Module Pattern

One of the two main patterns in JavaScript programming is the module pattern, which groups programs into functions called modules.

var myModule = (function(){
  var private = { name: 'nobody' };
  return {
    getName: function() {
      return private.name;
    },
    setName: function(newName) {
      private.name = newName;
      return newName;
   }
  };
})();

myModule.getName(); // "nobody"
myModule.setName('Elie');
myModule.getName(); // "Elie"

Module Pattern

Encapsulation - the ability to restrict access to private stuff

 

Flexible namespace, for example jQuery is a module aliased as $.

What benefits does this give us?

Closure Recipe

  1. Start with an outer (parent) function
  2. Add variables scoped inside the outer function
  3. Return an inner (child) function that references variables in the parent function
  4. Assign an external variable to the result of calling the outer function. This variable will point to an inner function with closed over variables.
function outer() {  // step 1
  var count = 0; // step 2
  return function inner() {  // step 3
    return ++count;  // step 4
  }
}

var myCounter = outer(); // step 5

Master JavaScript Functions

By Michael Hueter

Master JavaScript Functions

Advanced talk about higher order functions, callbacks, and closure.

  • 1,257