All do approximately the same thing, with a few subtle differences:
There is a distinction between the function name and the variable the function is assigned to. The function name cannot be changed, while the variable the function is assigned to can be reassigned. The function name can be used only within the function's body. Attempting to use it outside the function's body results in an error (or get another value, if the same name is declared elsewhere). For example:
const y = function x() {};
console.log(x);
The function name also appears when the function is serialized via its toString()
method.
On the other hand, the variable the function is assigned to is limited only by its scope, which is guaranteed to include the scope in which the function is declared.
As the const y = function x() {}
example shows, the function name can be different from the variable the function is assigned to. They have no relation to each other. A function declaration also creates a variable with the same name as the function name. Thus, unlike those defined by function expressions, functions defined by function declarations can be accessed by their name in the scope they were defined in, as well as in their own body.
A function defined by new Function
will dynamically have its source assembled, which is observable when you serialize it. For example, console.log(new Function().toString())
gives:
function anonymous(
) {
}
This is the actual source used to compile the function. However, although the Function()
constructor will create the function with name anonymous
, this name is not added to the scope of the body. The body only ever has access to global variables. For example, the following would result in an error:
new Function("alert(anonymous);")();
Unlike functions defined by function expressions or by the Function
constructor, a function defined by a function declaration can be used before the function declaration itself. For example:
foo();
function foo() {
console.log('FOO!');
}
A function defined by a function expression or by a function declaration inherits the current scope. That is, the function forms a closure. On the other hand, a function defined by a Function
constructor does not inherit any scope other than the global scope (which all functions inherit).
var p = 5;
function myFunc() {
const p = 9;
function decl() {
console.log(p);
}
const expr = function () {
console.log(p);
};
const cons = new Function('\tconsole.log(p);');
decl();
expr();
cons();
}
myFunc();
Functions defined by function expressions and function declarations are parsed only once, while those defined by the Function
constructor are not. That is, the function body string passed to the Function
constructor must be parsed each and every time the constructor is called. Although a function expression creates a closure every time, the function body is not reparsed, so function expressions are still faster than new Function(...)
. Therefore the Function
constructor should generally be avoided whenever possible.
It should be noted, however, that function expressions and function declarations nested within the function generated by parsing a Function()
constructor's string aren't parsed repeatedly. For example:
const foo = (new Function("const bar = 'FOO!'; return function() { alert(bar); };"))();
foo();
A function declaration is very easily (and often unintentionally) turned into a function expression. A function declaration ceases to be one when it either:
- becomes part of an expression
- is no longer a "source element" of a function or the script itself. A "source element" is a non-nested statement in the script or a function body:
let x = 0;
if (x === 0) {
x = 10;
function boo() {}
}
function foo() {
let y = 2;
function bar() {}
while (y < 10) {
function blah() {}
y++;
}
}