Skip to content

Instantly share code, notes, and snippets.

@BrianWill
Last active September 21, 2016 20:05
Show Gist options
  • Save BrianWill/ba9addaf887970e4f9c38cde905eae05 to your computer and use it in GitHub Desktop.
Save BrianWill/ba9addaf887970e4f9c38cde905eae05 to your computer and use it in GitHub Desktop.
Javascript functions

Named functions vs. anonymous functions

A function statement creates a new function and assigns that function to a variable of the given name:

// creates a new function and assigns it to the variable foo
function foo() {
    // do stuff here
}

Just like [] is an expression returning a new array, and just like {} is an expression returning a new object, an anonymous function is an expression returning a new function:

// this is an expression, not a statement
function () { /* do something */ }

If we assign an anonymous function to a variable, it is just the same as if we used a function statement but for one difference:

foo();  // error! foo is not defined yet
var foo = function() {
    // do something
};  // semi-colon needed here!
foo();  // ok: foo is defined
foo();  // ok: foo is defined even though the function statment comes later
function foo() {
    // do something
} // no semi-colon needed here!
foo();  // OK: foo is defined

The special thing about function statements is that they are 'hoisted': no matter where you put a function statement in a scope, Javascript sees it as if it were placed at the top of the scope. This is convenient in certain cases, for example:

function foo() {
    bar(); 
}

foo(); // ok: bar is already defined as a function 

function bar() {
    // do stuff
}

We have a problem if we try the same thing with anonymous functions:

var foo = function () {
    bar(); 
}

foo(); // bad! will throw an exception because bar is still undefined

var bar = function () {
    // do stuff
}

In general, we use named functions when we just want to assign a function to a variable of the current scope. The anonymous form is often more convenient and readable in other cases, for example:

// create a new object whose foo property is a new function
var obj = {
    foo: function () { /* do something */ }
};

Confusingly, we can actually give an anonymous function a name when we create it. This simply sets the .name property of the function (which can be helpful for when we debug or get a stack trace):

var foo = function bar() { /* do something */ };
console.log(foo.name);  // 'bar'

Nested functions, scoping, and closure:

Every function has its own scope, a set of local variables that exist for each call to the function. The local variables of a function are its parameters and any variables declared inside the function with var:

// this function has four local variables: a, b, c, d
function foo(a, b) {
    var c = a + b;
    d = 11;
    return c * d; 
    var d;  // it doesn't matter where we put the variable declarations--they can even go after a return!
}

Be clear that each call to a function has its own set of local variables independent from every other call to the function, e.g. c in one call to foo is a separate variable from the c of any other call to foo.

When we create a function inside another function, the inside function can see the local variables of the outer function call in which it was created:

// in the outer function, we can use a, b, c, d, or bar, but not x or y 
function foo(a, b) {
    var c = a + b;
    d = 11;
    // this function statement defines a local variable bar inside the outer function
    function bar() {
        // this function has its own locals x and y, but we can also use a, b, c, d, and bar here 
        var x = 2;
        var y = 8;
        return x + c;
    }
    return bar() * d; 
    var d;  // it doesn't matter where we put the variable declarations--they can even go after a return!
}

Normally, local variables of a function call disappear when the call returns, but thanks to a Javascript feature called 'closure', a nested function retains any variables it uses from enclosing functions. For example:

function foo() {
    var a = 2;
    function bar() {
        a = a + 3;
        return a;
    }
    return bar;
}

// assign to x the function returned by foo
// this function retains the variable 'a' from its enclosing function
var x = foo();
x();  // returns 5
x();  // returns 8
x();  // returns 11

// a second call to foo returns the same nested function, but the function object assigned to y is 
// a different 'closure': it has its own separate variable 'a' from the enclosing function
var y = foo();
y();  // returns 5
y();  // returns 8
y();  // returns 11

If a nested function creates its own variable of the same name as a variable of the enclosing function, the nested function cannot access the variable of that name from the enclosing function:

function foo() {
    var ack;
    function bar() {
        var ack;  // because bar has its own ack, bar cannot refer to the variable ack of foo
        // do something    
    }
    // do something
}

The special parameter this

The reserved word this is a parameter that gets its argument in a special way. When a function is normally called, the global scope object is passed to the this parameter:

function foo() {
    console.log(this);  // print out the value passed to the special parameter 'this'
}
foo();   // prints out the global scope object 

There is no good reason for this behavior! It is totally a design mistake in Javascript!

So what is this good for? Well, when a function is a property of an object and we invoke the function using the dot operator, the object itself is passed to the this parameter of the function:

var obj = {};
obj.foo = function () {
    console.log(this);
}
obj.foo();  // prints out the object assigned to obj

Combined with the prototype look-up behavior of objects, the dot operator syntax and this parameter enable an Object-Oriented style of programming in Javascript, and so Javascript functions which use this are often called methods.

Arrow functions

An arrow function is basically an anonymous function written with a more compact syntax:

// normal anonymous functions
function (a, b) { /* do stuff */ }
function (a) { /* do stuff */ }
function () { /* do stuff */ }

// arrow function equivalents
(a, b) => { /* do stuff */ }
(a) => { /* do stuff */ }
() => { /* do stuff */ }

However, there is one other important difference: arrow functions have no this parameter.

function foo() {
    return function () {
        return this;  // 'this' of the anonymous function itself, not of foo  
    };
}
function foo() {
    return () => {  
        return this;  // 'this' of foo! The arrow function has no 'this' of its own!
    };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment