Skip to content
Lev Brie edited this page Jan 19, 2016 · 12 revisions

◄ Back (The Basics)     Next (Objects) ►

Functions (Lecture 2: Part 1)

Now that we've reviewed the basic features of the JavaScript language, and saw some pretty complicated functions in class, let's take a step back and focus on creating some very simple, useful functions that we can keep as reference points as functions get more complicated.

Functions can be created either with a function declaration or with a function expression.

Function declarations

This is a function declaration:

function add(a, b) {
  var sum = a + b;
  console.log('The sum is ' + sum);
  return sum;
}

Function expressions

This is a function expression:

var add = function(a, b) {
  var sum = a + b;
  console.log('The sum is ' + sum);
  return sum;
};

The function here is anonymous - it doesn't have a name associated with it. The variable add will now reference a function which does the same computation and returns the same result as the add function we declared above. We could, if we wanted to, also name this function:

var add = function namedAddFunction(a, b) {
  var sum = a + b;
  console.log('The sum is ' + sum);
  return sum;
};

Since functions can be passed around in the same way that other values can be passed around in JavaScript, we can just assign functions to variables as we see fit.

A Calculator() function constructor

Let's build a calculator from the ground up, starting with a few of the functions we might need:

function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
function divide(a, b) { return a / b; }
function multiply(a, b) { return a * b; }

Now what if we wanted to store these functions in some kind of calculator object? In many languages, this is where we would want to create a Calculator class, but not in JavaScript. In JavaScript there are several ways to do this. One way is to create a Calculator function with properties to functions used to carry out the desired computations. Here we use this to refer to the Calculator object that has been created and new Calculator() to create a new calculator object.

// when we declare a function by beginning with the function keyword,
// we don't use a semicolon at the end of the function definition
// at all other times we do
function Calculator() {
  this.lastResult = null; // notice the ;
  this.add = function(a, b) { return a + b; }; // notice the ;
  this.subtract = function(a, b) { return a - b; };
  this.divide = function(a, b) { return a / b; };
  this.multiply = function(a, b) { return a * b; };
}

var calc = new Calculator(); // create a new calculator object

calc.add(3,2));       // this will equal 5
calc.subtract(3,2));  // this will equal 1
calc.divide(3,2));    // this will equal 1.5
calc.multiply(3,2));  // this will equal 6

In JavaScript, when we use the new keyword with a function to create a new object, this object's prototype is set to that function's prototype, so another way to accomplish the same thing would be to add our calculating "methods" to the Calculator function's prototype:

function Calculator() {}              // here our function definition is empty

// we use the calculator prototype to add the desired functionality to Calculator
Calculator.prototype.add = function(a, b) { return a + b; };
Calculator.prototype.subtract = function(a, b) { return a - b; };
Calculator.prototype.divide = function(a, b) { return a / b; };
Calculator.prototype.multiply = function(a, b) { return a * b; };

// and everything will now work exactly the same way as before
var calc = new Calculator();

calc.add(3,2));       // this will equal 5
calc.subtract(3,2));  // this will equal 1
calc.divide(3,2));    // this will equal 1.5
calc.multiply(3,2));  // this will equal 6

Ok, great, well then what's the difference? One difference is that a function's prototype has access to that function's this property, but it doesn't have access to variables set inside the function itself. Let me show you what I mean. Consider the case where we want our Calculator function to store the last result calculated. How can we do this?

function Calculator() {
  this.lastResult = null;
  // notice that whenever we use more than one line we keep the
  // opening braces on the right and start indented on the next line
  this.add = function(a, b) {
    var result = a + b;
    this.lastResult = result;
    return result;
  }; // notice the ;
  this.subtract = function(a, b) {
    this.lastResult = a - b; // we could also just set last result
                             // immediately and return it
    return this.lastResult;
  };
  this.divide = function(a, b) {
    // we could even set this.lastResult in our return but this is
    // much less clear
    return this.lastResult = a / b;
  };
  // and making it one line makes it even less clear!
  this.multiply = function(a, b) { return this.lastResult = a * b; };
}  // no ; here

var calc = new Calculator();

calc.lastResult;     // null
calc.add(3,2));      // 5
calc.lastResult;     // 5
calc.subtract(3,2)); // 1
calc.lastResult;     // 1
calc.divide(3,2);    // 1.5
calc.lastResult;     // 1.5
calc.multiply(3,2);  // 6
calc.lastResult;     // 6

We could also set the lastResult variable using this, as above, and then access it in our function's prototype:

function Calculator() {
  this.lastResult = null;
}

Calculator.prototype.add = function(a, b) {
  var result = a + b;
  this.lastResult = result;
  return result;
};
Calculator.prototype.subtract = function(a, b) {
  this.lastResult = a - b;
  return this.lastResult;
};
Calculator.prototype.divide = function(a, b) {
  this.lastResult = a / b;
  return this.lastResult;
};
Calculator.prototype.multiply = function(a, b) {
  this.lastResult = a * b;
  return this.lastResult
};

var calc = new Calculator();

calc.lastResult;     // null
calc.add(3,2));      // 5
calc.lastResult;     // 5
calc.subtract(3,2)); // 1
calc.lastResult;     // 1
calc.divide(3,2);    // 1.5
calc.lastResult;     // 1.5
calc.multiply(3,2);  // 6
calc.lastResult;     // 6

This works exactly the same way as before.

Another way to achieve the same effect is to store the lastResult in a variable set inside the Calculator function and then access that variable from outside the function using a getter function:

function Calculator() {
  var lastResult = null; // this variable is not a property of the
                         // object returned from new Calculator()
                         // so we cannot access it from outside the
                         // function

  // we need to use an accessor function like this to get it
  this.getLastResult = function () { return lastResult; };
  this.add = function(a, b) {
    var result = a + b;
    lastResult = result; // we no longer use this to set lastResult,
    return result;
  }; // notice the ;
  this.subtract = function(a, b) {
    lastResult = a - b; // we could also just set last result
                             // immediately and return it
    return lastResult;
  };
  this.divide = function(a, b) {
    // we could even set lastResult in our return but this is
    // much less clear
    return lastResult = a / b;
  };
  // and making it one line makes it even less clear!
  this.multiply = function(a, b) { return lastResult = a * b; };
}  // no ; here

var calc = new Calculator();

calc.getLastResult();  // null
calc.add(3,2);         // 5
calc.getLastResult();  // 5
calc.subtract(3,2);    // 1
calc.getLastResult();  // 1
calc.divide(3,2);      // 1.5
calc.getLastResult();  // 1.5
calc.multiply(3,2);    // 6
calc.getLastResult();  // 6

In other words, here we no longer have a calc.lastResult property available to us, since the lastResult is not set on this but is instead a simple variable inside of the function's scope. The only way to access it is to provide a function that is set on the function's this property and use that function, getLastResult(), to get the value of the lastResult variable.

Doing it this way has the advantage of "hiding" that value from the user so that they can't change the value of lastResult (either accidentally or intentionally) except by using one of the calculator's functions, they can only ask for its value. This won't work, however, if we try to use the prototype property to set up the functionality we need:

function Calculator() {
  var lastResult = null; // this variable is not accessible outside
                         // this function
}

// NONE OF THESE FUNCTIONS WILL WORK, SINCE THEY ONLY HAVE ACCESS
// TO PROPERTIES OF THE CALCULATOR FUNCTION (ACCESSED BY this)
// AND NOT TO VARIABLES STORED WITHIN THE FUNCTION ITSELF
Calculator.prototype.getLastResult = function() {
  return lastResult; // this causes an error at compile time
};
Calculator.prototype.add = function(a, b) {
  var result = a + b;
  lastResult = result;
  return result;
};

// we can no longer create a new Calculator objects without causing a
// reference error (lastResult is not defined)
// var calc = new Calculator();

Basic Function Scope

Most programming languages have lexical scope. JavaScript is no exception. But most languages also have block scope. JavaScript does have some mechanisms for block scope, including the keyword let (which declares a variable with block scope), but in general, JavaScript has function scope.

This means that variables declared inside a function are not visible outside of that function:

function scopedFunction() {
  var scopedVariable = 1;
  console.log(scopedVariable); // prints 1
}

console.log(scopedVariable);   // undefined!

but variables declared inside a block are visible outside of that block:

function scopeFunctionWithConditional() {
  console.log(scopedVarInConditional); // undefined
  var isTrue = true;
  if (isTrue) {
    var scopedVarInConditional = 'I am available outside this block';
  }
  console.log(scopedVarInConditional); // 'I am available outside this block'
}

Passing by value and passing by reference

Primitive data types are passed by value:

var a = 0;

function incr(num) {
  num++;
  console.log(num);
}

incr(a);          // prints 1
console.log(a);   // prints 0

Objects are passed by reference:

var a = [0, 1];

function addTo(num, val) {
  num += val;
  console.log(num);
}

function addToFirstElement(arr, val) {
  arr[0] += val;
  console.log(arr[0]);
}

addTo(a[0], 5);               // 5
console.log(a[0]);            // 0
addToFirstElement(arr, 5);    // 5
console.log(a[0]);            // 5

Shadowing

Inner variables can shadow outer variables:

var a = 0;

function incr() {
  var a = 0;
  a++;
  console.log(a);
}

incr();          // prints 1
console.log(a);  // prints 0

Hoisting

In JavaScript, the engine first runs through the scope and finds any variables that are declared (along with any LHS variables that need to be resolved) and adds them to the scope. It then goes through and sets their values in order:

var a = 0;

function addToA(val) {
  a += val;
  console.log(a);
  var a = 0;
}

addToA(5);          // NaN!!!
console.log(a);     // prints 0

Although there is a variable a in the outser scope here, inside the function the a in a += 5; refers to the var a declared below it inside of that function. However, at the time it was referenced in a += val; it did not yet have a value assigned and thus returned NaN. If we added a log statement below it we would get NaN and then 0:

var a = 0;

function addToA(val) {
  a += val;
  console.log(a);
  var a = 0;
  console.log(a);
}

addToA(5);          // NaN!!!
                    // 0
console.log(a);     // prints 0

To the engine, this is exactly equivalent to:

var a = 0;

function addToA(val) {
  var a;           // a is declared but undefined
  a += val;
  console.log(a);
  a = 0;           // now a has been set to 0
  console.log(a);
}

addToA(5);          // NaN!!!
                    // 0
console.log(a);     // prints 0

This is called hoisting because it is as if the variable declaration is being "hoisted" to the top of the function scope.

Nested Functions

Functions can be nested inside of other functions. We have seen this in the constructor function, but we can nest functions anywhere we please. In fact, this can be a good way to keep our functions clean and focused when we want to carry out a complex computation and the steps of the computation are only needed inside of that function:

function findMyCourse(myCourseName, courseList) {
  var indexOfCourse = -1;           // not found
  courseList.forEach(checkForCourse);
  return indexOfCourse;

  function checkForCourse(course, index, array) {
    if (course.name === myCourseName) {
      console.log('Found course: ' + course.name);
      console.log('Section: ' + course.section);
      indexOfCourse = index;
    }
  }
}

var courses = [
  {name: 'plt', section: 4115},
  {name: 'os', section: 4118},
  {name: 'js', section: 3101},
  {name: 'shakespeare', section: undefined}
];
console.log(findMyCourse('js', courses)); // Found course: js
                                          // Section: 3101
                                          // 2

Arguments

All JavaScript functions, when called, contain an arguments object. This object is array-like in that it has a length and that its properties can be accessed by index, but it is not an array. If a function is called with too few arguments, the parameters that do not receive arguments will be set to undefined. If it receives too many, they will be ignored. This is great for situations where we want to take an arbitrary number of arguments, or want to create a function with optional arguments. As you can see in almost all of the built-in array methods, providing optional arguments is extremely common in JavaScript, especially when callbacks are involved.

The 4 Forms of Invocation

There are 4 ways to call a function. These "forms of invocation" are referred to as:

  1. Function Form
  2. Method Form
  3. Constructor Form
  4. Apply Form

Function Form:

findMyCourse('js', courses);

Given an object courseFinder:

var courseFinder = {
  courses: [
    {name: 'plt', section: 4115},
    {name: 'os', section: 4118},
    {name: 'js', section: 3101},
    {name: 'shakespeare', section: undefined}
  ],
  findMyCourse: function (myCourseName) {
    var indexOfCourse = -1;           // not found
    this.courses.forEach(checkForCourse);
    return indexOfCourse;

    function checkForCourse(course, index, array) {
      if (course.name === myCourseName) {
        console.log('Found course: ' + course.name);
        console.log('Section: ' + course.section);
        indexOfCourse = index;
      }
    }
  }
};

The method form is called on an object, and this is set to that object:

courseFinder.findMyCourse('js');

Constructor form we have seen before. In constructor form, this is set to the new object that's returned:

var calculator;

function Calculator() {
  this.totalCalculations = 0;
  this.runningTotal = 0;
}

Calculator.prototype.add = function(a, b) {
  this.totalCalculations++;
  var sum = a + b;
  this.runningTotal += sum;
  return sum;
};

calculator = new Calculator();
console.log(calculator.totalCalculations); // 0
console.log(calculator.runningTotal);      // 0
console.log(calculator.sum(1, 3));         // 4
console.log(calculator.totalCalculations); // 1
console.log(calculator.runningTotal);      // 4

In the apply form, a function is called using the apply or call method, which allows us to explicitly set the thisObject to be used:

var someObject = {
  courses: [
    {name: 'js', section: 'totallydifferentsectioncode'}
  ]
};
// prints:
// Found course: js
// Section: totallydifferentsectioncode
courseFinder.findMyCourse.call(someObject, 'js');

The difference between apply and call

The call method takes a this argument as the first parameter and then any number of additional argument. The apply method also takes a this argument as the first parameter, and then takes an array of values to be used as additional parameters. This is a convenience method for when we want to use the apply form and have been holding the arguments to be applied in an array, as is often the case:

// from MDN
var numbers = [5, 6, 2, 3, 7];

var max = Math.max.apply(null, numbers); /* This about equal to Math.max(numbers[0], ...)
                                            or Math.max(5, 6, ...) */
var min = Math.min.apply(null, numbers);

console.log(max);
console.log(min);

We can rewrite call using apply:

Function.prototype.call = function(fn) {
  var arr = Array.prototype.slice.apply(arguments);
  arr.splice(0,1);
  return this.apply(fn, arr);
}

Remember that we saw this crazy slice thing with call already:

var arr = Array.prototype.slice.call(arguments, 0);

And of course this will also work:

Function.prototype.call = function() {
  var args = [];
  for (var i = 1; i<arguments.length;  i++) { args.push(arguments[i]); }
  return this.apply(arguments[0], args);
}

Currying

Currying is a technique for transforming a function of n-ary arity to a function of lesser arity. Great. What does that mean. Arity just means the number of arguments a function accepts. A unary function takes a single argument, a binary function takes 2 arguments, a ternary function takes 3 arguments, and an n-ary function takes n arguments.

In JavaScript, we use currying to take a function and then return a function with 1 or more of the initial arguments now fixed, so that we can then call the new function on a smaller set of arguments:

function add(a) {
  return function(b) {
    return a + b;
  }
}

var sum = add(2)(5);
console.log(sum);
console.log(add(4)(7));

var addTwo = add(2),
    addFive = add(5);

console.log(addTwo(8));
console.log(addFive(7));

What should these print?

Functions that take functions as arguments

In JavaScript, we can pass functions as arguments to other functions and then simply call those passed-in functions as if they were any other function:

function callOnEach(arr, functionToCall) {
  var i;
  for (i=0; i<arr.length; i++) {
    functionToCall(arr[i]);
  }
}

var someArray = ['me', 'you', 'and', 'everyone', 'we', 'know'];
var commentMeOut = function (myName) {
  console.log('/** ' + myName + ' **/');
};
commentMeOut('JavaScript');
callOnEach(someArray, commentMeOut);

// PRINTS:
/** JavaScript **/
/** me **/
/** you **/
/** and **/
/** everyone **/
/** we **/
/** know **/

More on Scope

Lexical scope (JavaScript is lexically scoped. So is Java. So is C. So is Ruby...) works from the inside out. When functions are nested inside of each other, the scopes of outer functions are visible to inner functions, but the scope of inner functions is not visible to outer functions:

// this outerFunction has an innerFunction but it can't see any of
// innerFunciton's variables
function outerFunction(outerFunctionParam) {
  var outerFunctionVariable = 'I\'m visible here and in my inner functions!';
  console.log(outerFunctionParam);
  console.log(outerFunctionVariable);
  console.log('but I can\'t see any of the functions inside me!');

  function innerFunction(innerParam) {
    var innerVariable = 'I\'m only visible to functions inside me!';
    console.log(innerParam);
    console.log(innerVariable);
    // innerFunction can see outerFunctionparam and outerFunctionVariable
    console.log(outerFunctionParam);
    console.log(outerFunctionVariable);

    function innerInnerFunction(innermostParam) {
      var innermostVariable = 'I\'m only visible to myself!';
      console.log('But I can see everybody outside of me!');
      console.log(innermostParam);
      console.log(innermostVariable);
      console.log(innerParam);
      console.log(innerVariable);
      // innerFunction can see outerFunctionparam and outerFunctionVariable
      console.log(outerFunctionParam);
      console.log(outerFunctionVariable);
    }
  }
}

outerFunction('I am an outer function');

Ok, so what will this print out? As it is, this function is a bit useless. It only prints: I am an outer function I'm visible here and in my inner functions! but I can't see any of the functions inside me!

So how can we make this useful? It turns out there are a lot of ways to make this useful, and understanding and mastering them is one of the keys to being able to create clean, useful, and powerful programs in JavaScript.

Let's start by at least making sure this is working as we expect:

// this outerFunction has an innerFunction but it can't see any of
// innerFunciton's variables
function outerFunction(outerFunctionParam) {
  var outerFunctionVariable = 'I\'m visible here and in my inner functions!';
  console.log(outerFunctionParam);
  console.log(outerFunctionVariable);
  console.log('but I can\'t see any of the functions inside me!');

  console.log('when i try to print inner params or variables I instead ' +
              'print undefined');
  console.log(typeof innerParam);
  console.log(typeof innerVariable);

  innerFunction('I am an inner param, my function can see the outerFunction');

  function innerFunction(innerParam) {
    var innerVariable = 'I\'m only visible to functions inside me!';
    console.log(innerParam);
    console.log(innerVariable);
    // innerFunction can see outerFunctionparam and outerFunctionVariable
    console.log(outerFunctionParam);
    console.log(outerFunctionVariable);

    // call inner function to see it has access to everything
    innerInnerFunction('I am the innermost param');

    function innerInnerFunction(innermostParam) {
      var innermostVariable = 'I\'m only visible to myself!';
      console.log('But I can see everybody outside of me!');
      console.log(innermostParam);
      console.log(innermostVariable);
      console.log(innerParam);
      console.log(innerVariable);
      // innerFunction can see outerFunctionparam and outerFunctionVariable
      console.log(outerFunctionParam);
      console.log(outerFunctionVariable);
    }
  }
}

outerFunction('I am an outer function');

Ok, so we can call these functions from the scope in which they are defined and we can see that they can access outer and not inner variables. But what else can we do with them? For starters, we can return them:

function outerFunction(outerFunctionParam) {
  var outerFunctionVariable = 'I\'m visible here and in my inner functions!';
  console.log(outerFunctionParam);
  console.log(outerFunctionVariable);
  console.log('but I can\'t see any of the functions inside me!');

  console.log('when i try to print inner params or variables I instead ' +
              'print undefined');
  console.log(typeof innerParam);
  console.log(typeof innerVariable);

  // let's not call this inner function just yet
  // innerFunction('I am an inner param, my function can see the outerFunction');

  return innerFunction;

  function innerFunction(innerParam) {
    var innerVariable = 'I\'m only visible to functions inside me!';
    console.log(innerParam);
    console.log(innerVariable);
    // innerFunction can see outerFunctionparam and outerFunctionVariable
    console.log(outerFunctionParam);
    console.log(outerFunctionVariable);

    // we won't call this innerInnerFunction yet either
    // innerInnerFunction('I am the innermost param');

    return innerInnerFunction;

    function innerInnerFunction(innermostParam) {
      var innermostVariable = 'I\'m only visible to myself!';
      console.log('But I can see everybody outside of me!');
      console.log(innermostParam);
      console.log(innermostVariable);
      console.log(innerParam);
      console.log(innerVariable);
      // innerFunction can see outerFunctionparam and outerFunctionVariable
      console.log(outerFunctionParam);
      console.log(outerFunctionVariable);
    }
  }

  return innerFunction;
}

function giveMeYourInnerFunction() {
  return outerFunction('I am an outer function giving up my inner function');
}

// will print what outer function prints and also return inner
var innerFunctionNowOnTheOutside = giveMeYourInnerFunction();
console.log('\n\n');

// will print what inner function prints and also return innerInner
var innerInnerNowOuter = innerFunctionNowOnTheOutside('inner on outer');
console.log('\n\n');

// and now we can call innerInner from the outside too!
innerInnerNowOuter('innerInner on outer');
console.log('\n\n');

// we can actually do this by invoking the inner funcs on the outer funcs:

outerFunction('outer')('inner')('innerInner');

And this is closure!

Closure

Closure often causes a lot of head-scratching, especially when you're trying to understand it in PLT. And trying to understand it from it's wikipedia entry often doesn't help much:

In programming languages, a closure (also lexical closure or function closure) is a function or reference to a function together with a referencing environment. A closure—unlike a plain function pointer—enables a function to access those non-local variables even when invoked outside its immediate lexical scope.

JavaScript makes closure much easier to understand. Closure is what happens when the ability to pass a function around meets the simple lexical scoping rules that we have already seen.

Lexical scoping means that the scope available to a function is determined by where that function is defined. As we saw above, inner functions have access to outer functions' variables because they are defined inside that function. Outer functions don't have access to inner functions because they are defined outside that function. Simple.

Now imagine what would happen if, when we passed a function to another function outside of its normal scope, we could no longer access the variables that function had access to from where it was originally defined.

funciton add(a) {
  return innerFunction;

  function innerFunction(b) {
    var sum = a + b;
    console.log(a);
    console.log(b);
    console.log(sum);
    return sum;
  }
}

var addFive = add(5);
var sum = addFive(10); // prints 5, 10, 15

This function would no longer work because the innerFunction, once returned, wouldn't have access to that outer-function, and passing around functions wouldn't make much sense. This is where closure comes in. Closure ensures that as we pass a function around, it retains its access to the variables that were in its scope where it was defined. That way, as we write our code, its very obvious what scope the functions we're writing have access to, and at the same time we can easily pass those functions to other parts of our program that want to make use of them.

Another couple quick examples of closure from the book:

var n;
function f() {
  var b = 'b';
  n = function () {
    return b;
  }
}

console.log(typeof n);  // undefined
console.log(f());
console.log(n());       // b

and:

function f(arg) {
  var n = function() {
    return arg;
  };
  arg++;
  return n;
}

var m = f(123);
console.log(m());    // 124

Anonymous Functions

An anonymous function is simply a function expression in which a function is defined but not given a name. Because we can pass functions around in JavaScript, we can also define functions directly in the place where we want to pass them:

var array = [1, 2, 3, 4, 5];

array.forEach(function (element) {
  console.log(element); // prints 1, 2, 3, 4, 5 on separate lines
});

Arrays have a built-in forEach function that takes a callback function as a parameter and calls that function on each element of the array. That function takes as parameters, the element, the index, and the array itself, so we could also print out the index and the array:

var array = [1, 2, 3, 4, 5];

array.forEach(function (element, index, arr) {
  console.log(element, index); // prints 1, 2, 3, 4, 5 on separate lines
  console.log(arr);
});

/* PRINTS:
[ 1, 2, 3, 4, 5 ]
2 1
[ 1, 2, 3, 4, 5 ]
3 2
[ 1, 2, 3, 4, 5 ]
4 3
[ 1, 2, 3, 4, 5 ]
5 4
[ 1, 2, 3, 4, 5 ]
*/

Self-invoking functions

Anonymous functions can be used to call a function right after defining it. You've seen this already. The code in every file you've seen so far has been surrounded by a self-invoking function, which takes the form:

(function () {
  // code to be executed immediately goes here
})();

so:

(function () {
  console.log('I just invoked myself');
})();

will log out 'I just invoked myself'.

This is also known as an immediately-invoked function expression or IIFE and protects the code inside from polluting the global environment. It also enables us to allow public access to variables and methods we wish to make public while retaining privacy for those we do not. It can, and sometimes does (this used to be more common practice than it is today), take arguments.

(function (name) {
  var myName = name;
  console.log('I am invoking myself and I am passing myself a name');
  console.log(myName);
})('Lev');

We'll see how important a role the IIFE plays soon when we discuss the module pattern.

Clone this wiki locally