Skip to content

Instantly share code, notes, and snippets.

@BrianLitwin
Last active February 16, 2019 21:19
Show Gist options
  • Save BrianLitwin/4934b7b6154ca7f3863b277c0ee47e27 to your computer and use it in GitHub Desktop.
Save BrianLitwin/4934b7b6154ca7f3863b277c0ee47e27 to your computer and use it in GitHub Desktop.
  • Defining functions
    • Function declarations
    • Function expressions
  • Calling functions
  • Function scope
  • Scope and the function stack
    • Recursion
    • Nested functions and closures
    • Preservation of variables
    • Multiply-nested functions
    • Name conflicts
  • Closures
  • Using the arguments object
  • Function parameters
    • Default parameters
    • Rest parameters
  • Arrow functions
    • Shorter functions
    • No separate this
  • Predefined functions

Defining functions

Primitive parameters (such as a number) are passed to functions by value; the value is passed to the function, but if the function changes the value of the parameter, this change is not reflected globally or in the calling function.

Objcets are passed by reference: If you pass an object (i.e. a non-primitive value, such as Array or a user-defined object) as a parameter and the function changes the object's properties, that change is visible outside the function.

Function expressions

Functions can be created with a function expression. Function expressions can be anonymous or named:

// anonymous 
var square = function(number) { return number * number; };
var x = square(4); // x gets the value 16

// named 
var factorial = function fac(n) { return n < 2 ? 1 : n * fac(n - 1); };
console.log(factorial(3));

Function expressions are convenient when passing a function as an argument to another function. The following example shows a map function that should receive a function as first argument and an array as second argument.

function map(f, a) {
  var result = [],i; // Create a new Array
  for (i = 0; i != a.length; i++)
    result[i] = f(a[i]);
  return result;
}

In the following code our function receives a function defined by a function expression and executes it for every element of the array received as a second argument.

function map(f, a) {
  var result = []; // Create a new Array
  var i; // Declare variable
  for (i = 0; i != a.length; i++)
    result[i] = f(a[i]);
  return result;
}
var f = function(x) {
   return x * x * x; 
}
var numbers = [0, 1, 2, 5, 10];
var cube = map(f,numbers);
console.log(cube);

A method is a function that is a property of an object.

Calling Functions

Functions must be in scope when they are called, but the function declaration can be hoisted (appear below the call in the code), as in this example:

console.log(square(5));
/* ... */
function square(n) { return n * n; }

The scope of a function is the function in which it is declared, or the entire program if it is declared at the top level. function hoisting only works with function declaration and not with function expression.

console.log(square); // square is hoisted with an initial value undefined.
console.log(square(5)); // TypeError: square is not a function
var square = function(n) { 
  return n * n; 
}

Functions are objects and function objects have methods.

Function scope

Variables defined inside a function cannot be accessed from anywhere outside the function, because the variable is defined only in the scope of the function. However, a function can access all variables and functions defined inside the scope in which it is defined. In other words, a function defined in the global scope can access all variables defined in the global scope. A function defined inside another function can also access all variables defined in its parent function and any other variable to which the parent function has access.

Scope and the function stack

A function can refer to and call itself. There are three ways for a function to refer to itself:

  1. the function's name
  2. arguments.callee
  3. an in-scope variable that refers to the function

For example, consider the following: var foo = function bar(). Within the function body, the following are all equivalent:

  1. bar()
  2. arguments.callee()
  3. foo()

A function that calls itself is called a recursive function. In some ways, recursion is analogous to a loop. Both execute the same code multiple times, and both require a condition (to avoid an infinite loop, or rather, infinite recursion in this case). For example, the following loop:

var x = 0;
while (x < 10) { // "x < 10" is the loop condition
   // do stuff
   x++;
}

can be converted into a recursive function and a call to that function:

function loop(x) {
  if (x >= 10) // "x >= 10" is the exit condition (equivalent to "!(x < 10)")
    return;
  // do stuff
  loop(x + 1); // the recursive call
}
loop(0);

It is possible to convert any recursive algorithm to a non-recursive one, but often the logic is much more complex and doing so requires the use of a stack. In fact, recursion itself uses a stack: the function stack.

The stack-like behavior can be seen in the following example:

function foo(i) {
  if (i < 0)
    return;
  console.log('begin: ' + i);
  foo(i - 1);
  console.log('end: ' + i);
}
foo(3);

// Output:

// begin: 3
// begin: 2
// begin: 1
// begin: 0
// end: 0
// end: 1
// end: 2
// end: 3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment