Here's the rundown on the standard forms that create functions: (Originally written for another question, but adapted after being moved into the canonical question.)
Terms:
The quick list:
Function Declaration
"Anonymous" function
Expression (which despite the term, sometimes get names)
Named function
Expression
Accessor Function Initializer (ES5+)
Arrow Function Expression (ES2015+)
Method Declaration in Object Initializer (ES2015+)
Constructor and Method Declarations in class
(ES2015+)
Function Declaration
The first form is a function declaration, which looks like this:
function x() {
console.log('x');
}
A function declaration is a declaration; it's not a statement or expression. As such, you don't follow it with a ;
(although doing so is harmless).
A function declaration is processed when execution enters the context in which it appears, before any step-by-step code is executed. The function it creates is given a proper name (x
in the example above), and that name is put in the scope in which the declaration appears.
Because it's processed before any step-by-step code in the same context, you can do things like this:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
Also because it's not part of the step-by-step execution of the code, you can't put it inside a control structure like try
, if
, switch
, while
, etc.
if (someCondition) {
function foo() { // <===== INVALID AND WILL FAIL ON
} // MANY ENGINES
}
Some engines will handle the above even though it's invalid, by rewriting it as a function expression on-the-fly. There's talk of adding a function statement to the next spec (ECMAScript6) to codify that. But with current engines, it will not work reliably; don't do it.
"Anonymous" function
Expression
The second common form is called an anonymous function expression:
var y = function () {
console.log('y');
};
Like all expressions, it's evaluated when it's reached in the step-by-step execution of the code.
In ES5, the function this creates has no name (it's anonymous). In ES2015, the function is assigned a name if possible by inferring it from context. In the example above, the name would be y
. Something similar is done when the function is the value of a property initializer. (For details on when this happens and the rules, search for SetFunctionName
in the ES2015 specification — it appears all over the place.)
Named function
Expression
The third form is a named function expression ("NFE"):
var z = function w() {
console.log('zw')
};
The function this creates has a proper name (w
in this case). Like all expressions, this is evaluated when it's reached in the step-by-step execution of the code. The name of the function is not added to the scope in which the expression appears; the name is in scope within the function itself:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
Note that NFEs have frequently been a source of bugs for JavaScript implementations. IE8 and earlier, for instance, handle NFEs completely incorrectly, creating two different functions at two different times. Early versions of Safari had issues as well. The good news is that current versions of browsers (IE9 and up, current Safari) don't have those issues any more. (But as of this writing, sadly, IE8 remains in widespread use, and so using NFEs with code for the web in general is still problematic.)
Accessor Function Initializer (ES5+)
Sometimes functions can sneak in largely unnoticed; that's the case with accessor functions. Here's an example:
var obj = {
value: 0,
get f() {
return this.value;
},
set f(v) {
this.value = v;
}
};
console.log(obj.f); // 0
console.log(typeof obj.f); // "number"
Note that when I used the function, I didn't use ()
! That's because it's an accessor function for a property. We get and set the property in the normal way, but behind the scenes, the function is called.
You can also create accessor functions with Object.defineProperty
, Object.defineProperties
, and the lesser-known second argument to Object.create
.
Arrow Function Expression (ES2015+)
ES2015 brings us the arrow function. Here's one example:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
See that n => n * 2
thing hiding in the map()
call? That's a function.
A couple of things about arrow functions:
Their this
is lexically bound, not determined when they're called. This means that the this
within them is the same as the this
where they're created.
As you'll have noticed with the above, you don't use the keyword function
; instead, you use =>
.
The n => n * 2
example above is one form of them. If you have multiple arguments to pass the function, you use parens:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(Remember that Array#map
passes the entry as the first argument, and the index as the second.)
If you're doing more than just a single expression, use {}
as normal:
var a = [
{first: "Joe", last: "Bloggs"},
{first: "Albert", last: "Bloggs"},
{first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
var rv = a.last.localeCompare(b.last);
if (rv === 0) {
rv = a.first.localeCompare(b.first);
}
return rv;
});
console.log(JSON.stringify(a));
Method Declaration in Object Initializer (ES2015+)
ES2015 allows a shorter form of declaring a property that references a function; it looks like this:
var o = {
foo() {
}
};
the equivalent in ES5 and earlier would be:
var o = {
foo: function foo() {
}
};
Constructor and Method Declarations in class
(ES2015+)
ES2015 brings us class
syntax, including declared constructors and methods:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
There are two function declarations above: One for the constructor, which gets the name Person
, and one for getFullName
, which is a function assigned to Person.prototype
.
var a = 1; var b = 2;
becomesvar a; var b; a = 1; b = 2
. So when you declare functionOne, it gets declared but its value isn't set immediately. Whereas since functionTwo is just a declaration, it gets put at the top of the scope. #2 functionTwo lets you access the name property and that helps a lot when trying to debug something. – xavierm02 Aug 21 '12 at 16:20function f(){}
vsvar f = function(){};
. – xavierm02 Aug 21 '12 at 16:27