Skip to main content

I was reading up on functions in JavaScript (from the book Professional JavaScript for Web Developers, 3rd edition) and felt the need to capture a few things that stood out to me. Below I will explain:

apply and call

Both of these functions allow a function to be executed with a custom this value.

In a function, the this keyword is equal to the execution context of the function, which is the object that calls the function. This means that when a function is run in the global scope this equals window—below is an example of this.

var color = 'blue';

function sayColor() { alert(this.color); }

sayColor(); // 'blue';

If the same function lives within an object, then the function is run in the scope of that object meaning that this will refer to the object.

var obj = {
  color: 'red',
  sayColor: function() {
    alert(this.color);
  }
};

obj.sayColor(); // 'red'

apply and call allow us to specify our own value for this, meaning we can get a lot of control over how our functions perform. The only difference between the two comes down to how they accept arguments.

apply accepts two arguments; the first being the execution context, i.e. this, and the second being an array of arguments passed to the function.

var color = 'blue';
var obj = { color: 'red' };

function sayColor(message) { alert(message + this.color); }

sayColor('The color is: '); // 'The color is: blue'
sayColor.apply(obj, ['The color on "obj" is: ']); // 'The color on "obj" is: red'

call accepts an endless number of arguments; the first being the execution context and the arguments following being passed to the function.

var color = 'blue';
var object = { color: 'red' };

function sayColor(message) { alert(message + this.color); }

sayColor('The color is: '); // 'The color is: blue'
sayColor.call(object, 'The color on object is: '); // 'The color is: red'

Returning functions from functions

It is easy to forget or take for granted that JavaScript functions can be treated like variables. Because of this, it's possible to not only return variables from functions, but to return functions as well.

A good example of this is from Professional JavaScript for Web Developers relating to the sort method on Arrays which is demonstrated below.

function createComparisionFunction(property) {

  return function(obj1, obj2) {

    var value1 = obj1[property];
    var value2 = obj2[property];

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

  }

}

var data = [{
  name: 'A',
  number: 1
},{
  name: 'B',
  number: 2
}];

alert(data[0].name); // 'A'
data.sort(createComparisionFunction('number'));
alert(data[0].name); // 'B'

"Safe" recursion with function expressions

A common way to achieve recursion in JavaScript is demonstrated below:

function alertArray(item) {

  for (var i = 0, length = item.length; i < length; i++) {

    if (Array.isArray(item[i])) {
      return alertArray(item[i]);
    } else {
      alert(item[i]);
    }

  }

}

alertArray([1, 2, 3, [4, 5]]); // '1', '2', '3', '4', '5'

This all seems well and good, but it is assumed that the alertArray function will always be available. The below example demonstrates why this can be a problem:

function alertArray(item) {

  for (var i = 0, length = item.length; i < length; i++) {

    if (Array.isArray(item[i])) {
      return alertArray(item[i]);
    } else {
      alert(item[i]);
    }

  }

}

var shortcut = alertArray; // Remember, functions can be stored as variables
alertArray = null;

shortcut([1, 2, 3, [4, 5]]); // '1', '2', '3', and then an undefined error

To get around this, named function expressions can be used within an Immediately-Invoked Function Expression (IIFE). A function expression can be described as a function stored within a variable (i.e. an expression).

var alertArray = (function f(item) {

  for (var i = 0, length = item.length; i < length; i++) {

    if (Array.isArray(item[i])) {
      return f(item[i]);
    } else {
      alert(item[i]);
    }

  }

});

var shortcut = alertArray; // Remember, functions can be stored as variables
alertArray = null;

shortcut([1, 2, 3, [4, 5]]); // '1', '2', '3', '4', '5'

It should be noted that function expressions are treated like variables and, as such, cannot be accessed globally due to the order of execution. That means that alertArray cannot be called before it is defined.

alertArray([1, 2, 3, [4, 5]]); // "Uncaught TypeError: alertArray is not a function..."

var alertArray = (function f(item) {

  for (var i = 0, length = item.length; i < length; i++) {

    if (Array.isArray(item[i])) {
      return f(item[i]);
    } else {
      alert(item[i]);
    }

  }

});