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'
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 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.
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!
};
}