admin管理员组文章数量:1125409
I've recently started maintaining someone else's JavaScript code. I'm fixing bugs, adding features and also trying to tidy up the code and make it more consistent.
The previous developer used two ways of declaring functions and I can't work out if there is a reason behind it or not.
The two ways are:
var functionOne = function() {
// Some code
};
And,
function functionTwo() {
// Some code
}
What are the reasons for using these two different methods and what are the pros and cons of each? Is there anything that can be done with one method that can't be done with the other?
I've recently started maintaining someone else's JavaScript code. I'm fixing bugs, adding features and also trying to tidy up the code and make it more consistent.
The previous developer used two ways of declaring functions and I can't work out if there is a reason behind it or not.
The two ways are:
var functionOne = function() {
// Some code
};
And,
function functionTwo() {
// Some code
}
What are the reasons for using these two different methods and what are the pros and cons of each? Is there anything that can be done with one method that can't be done with the other?
Share Improve this question edited Feb 14, 2023 at 7:44 Richard Garside asked Dec 3, 2008 at 11:31 Richard GarsideRichard Garside 89.1k12 gold badges87 silver badges97 bronze badges 042 Answers
Reset to default 1 2 Next 5607The difference is that functionOne
is a function expression and so only defined when that line is reached, whereas functionTwo
is a function declaration and is defined as soon as its surrounding function or script is executed (due to hoisting).
For example, a function expression:
// TypeError: functionOne is not a function
functionOne();
var functionOne = function() {
console.log("Hello!");
};
And, a function declaration:
// Outputs: "Hello!"
functionTwo();
function functionTwo() {
console.log("Hello!");
}
Historically, function declarations defined within blocks were handled inconsistently between browsers. Strict mode (introduced in ES5) resolved this by scoping function declarations to their enclosing block.
'use strict';
{ // note this block!
function functionThree() {
console.log("Hello!");
}
}
functionThree(); // ReferenceError
First I want to correct Greg: function abc(){}
is scoped too — the name abc
is defined in the scope where this definition is encountered. Example:
function xyz(){
function abc(){};
// abc is defined here...
}
// ...but not here
Secondly, it is possible to combine both styles:
var xyz = function abc(){};
xyz
is going to be defined as usual, abc
is undefined in all browsers but Internet Explorer — do not rely on it being defined. But it will be defined inside its body:
var xyz = function abc(){
// xyz is visible here
// abc is visible here
}
// xyz is visible here
// abc is undefined here
If you want to alias functions on all browsers, use this kind of declaration:
function abc(){};
var xyz = abc;
In this case, both xyz
and abc
are aliases of the same object:
console.log(xyz === abc); // prints "true"
One compelling reason to use the combined style is the "name" attribute of function objects (not supported by Internet Explorer). Basically when you define a function like
function abc(){};
console.log(abc.name); // prints "abc"
its name is automatically assigned. But when you define it like
var abc = function(){};
console.log(abc.name); // prints ""
its name is empty — we created an anonymous function and assigned it to some variable.
Another good reason to use the combined style is to use a short internal name to refer to itself, while providing a long non-conflicting name for external users:
// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
// Let it call itself recursively:
shortcut(n - 1);
// ...
// Let it pass itself as a callback:
someFunction(shortcut);
// ...
}
In the example above we can do the same with an external name, but it'll be too unwieldy (and slower).
(Another way to refer to itself is to use arguments.callee
, which is still relatively long, and not supported in the strict mode.)
Deep down, JavaScript treats both statements differently. This is a function declaration:
function abc(){}
abc
here is defined everywhere in the current scope:
// We can call it here
abc(); // Works
// Yet, it is defined down there.
function abc(){}
// We can call it again
abc(); // Works
Also, it hoisted through a return
statement:
// We can call it here
abc(); // Works
return;
function abc(){}
This is a function expression:
var xyz = function(){};
xyz
here is defined from the point of assignment:
// We can't call it here
xyz(); // UNDEFINED!!!
// Now it is defined
xyz = function(){}
// We can call it here
xyz(); // works
Function declaration vs. function expression is the real reason why there is a difference demonstrated by Greg.
Fun fact:
var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"
Personally, I prefer the "function expression" declaration because this way I can control the visibility. When I define the function like
var abc = function(){};
I know that I defined the function locally. When I define the function like
abc = function(){};
I know that I defined it globally providing that I didn't define abc
anywhere in the chain of scopes. This style of definition is resilient even when used inside eval()
. While the definition
function abc(){};
depends on the context and may leave you guessing where it is actually defined, especially in the case of eval()
— the answer is: It depends on the browser.
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:
- ES5: ECMAScript 5th edition, 2009
- ES2015: ECMAScript 2015 (also known as "ES6")
The quick list:
Function Declaration
"Anonymous"
function
Expression (which despite the term, sometimes create functions with names)Named
function
ExpressionAccessor Function Initializer (ES5+)
Arrow Function Expression (ES2015+) (which, like anonymous function expressions, don't involve an explicit name, and yet can create functions with names)
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');
}
Until ES2015, the spec didn't cover what a JavaScript engine should do if you put a function declaration inside a control structure like try
, if
, switch
, while
, etc., like this:
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
And since they're processed before step-by-step code is run, it's tricky to know what to do when they're in a control structure.
Although doing this wasn't specified until ES2015, it was an allowable extension to support function declarations in blocks. Unfortunately (and inevitably), different engines did different things.
As of ES2015, the specification says what to do. In fact, it gives three separate things to do:
- If in loose mode not on a web browser, the JavaScript engine is supposed to do one thing
- If in loose mode on a web browser, the JavaScript engine is supposed to do something else
- If in strict mode (browser or not), the JavaScript engine is supposed to do yet another thing
The rules for the loose modes are tricky, but in strict mode, function declarations in blocks are easy: They're local to the block (they have block scope, which is also new in ES2015), and they're hoisted to the top of the block. So:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
"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 the 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:
They don't have their own
this
. Instead, they close over thethis
of the context where they're defined. (They also close overarguments
and, where relevant,super
.) This means that thethis
within them is the same as thethis
where they're created, and cannot be changed.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.)
In both cases, the body of the function is just an expression; the function's return value will automatically be the result of that expression (you don't use an explicit return
).
If you're doing more than just a single expression, use {}
and an explicit return
(if you need to return a value), 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));
The version without { ... }
is called an arrow function with an expression body or concise body. (Also: A concise arrow function.) The one with { ... }
defining the body is an arrow function with a function body. (Also: A verbose arrow function.)
Method Declaration in Object Initializer (ES2015+)
ES2015 allows a shorter form of declaring a property that references a function called a method definition; it looks like this:
var o = {
foo() {
}
};
the almost-equivalent in ES5 and earlier would be:
var o = {
foo: function foo() {
}
};
the difference (other than verbosity) is that a method can use super
, but a function cannot. So for instance, if you had an object that defined (say) valueOf
using method syntax, it could use super.valueOf()
to get the value Object.prototype.valueOf
would have returned (before presumably doing something else with it), whereas the ES5 version would have to do Object.prototype.valueOf.call(this)
instead.
That also means that the method has a reference to the object it was defined on, so if that object is temporary (for instance, you're passing it into Object.assign
as one of the source objects), method syntax could mean that the object is retained in memory when otherwise it could have been garbage collected (if the JavaScript engine doesn't detect that situation and handle it if none of the methods uses super
).
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
.
Speaking about the global context, both, the var
statement and a FunctionDeclaration
at the end will create a non-deleteable property on the global object, but the value of both can be overwritten.
The subtle difference between the two ways is that when the Variable Instantiation process runs (before the actual code execution) all identifiers declared with var
will be initialized with undefined
, and the ones used by the FunctionDeclaration
's will be available since that moment, for example:
alert(typeof foo); // 'function', it's already available
alert(typeof bar); // 'undefined'
function foo () {}
var bar = function () {};
alert(typeof bar); // 'function'
The assignment of the bar
FunctionExpression
takes place until runtime.
A global property created by a FunctionDeclaration
can be overwritten without any problems just like a variable value, e.g.:
function test () {}
test = null;
Another obvious difference between your two examples is that the first function doesn't have a name, but the second has it, which can be really useful when debugging (i.e. inspecting a call stack).
About your edited first example (foo = function() { alert('hello!'); };
), it is an undeclared assignment, I would highly encourage you to always use the var
keyword.
With an assignment, without the var
statement, if the referenced identifier is not found in the scope chain, it will become a deleteable property of the global object.
Also, undeclared assignments throw a ReferenceError
on ECMAScript 5 under Strict Mode.
A must read:
- Named function expressions demystified
Note: This answer has been merged from another question, in which the major doubt and misconception from the OP was that identifiers declared with a FunctionDeclaration
, couldn't be overwritten which is not the case.
The two code snippets you've posted there will, for almost all purposes, behave the same way.
However, the difference in behaviour is that with the first variant (var functionOne = function() {}
), that function can only be called after that point in the code.
With the second variant (function functionTwo()
), the function is available to code that runs above where the function is declared.
This is because with the first variant, the function is assigned to the variable foo
at run time. In the second, the function is assigned to that identifier, foo
, at parse time.
More technical information
JavaScript has three ways of defining functions.
- Your first snippet shows a function expression. This involves using the "function" operator to create a function - the result of that operator can be stored in any variable or object property. The function expression is powerful that way. The function expression is often called an "anonymous function", because it does not have to have a name,
- Your second example is a function declaration. This uses the "function" statement to create a function. The function is made available at parse time and can be called anywhere in that scope. You can still store it in a variable or object property later.
- The third way of defining a function is the "Function()" constructor, which is not shown in your original post. It's not recommended to use this as it works the same way as
eval()
, which has its problems.
A better explanation to Greg's answer
functionTwo();
function functionTwo() {
}
Why no error? We were always taught that expressions are executed from top to bottom(??)
Because:
Function declarations and variable declarations are always moved (
hoisted
) invisibly to the top of their containing scope by the JavaScript interpreter. Function parameters and language-defined names are, obviously, already there. ben cherry
This means that code like this:
functionOne(); --------------- var functionOne;
| is actually | functionOne();
var functionOne = function(){ | interpreted |-->
}; | like | functionOne = function(){
--------------- };
Notice that the assignment portion of the declarations were not hoisted. Only the name is hoisted.
But in the case with function declarations, the entire function body will be hoisted as well:
functionTwo(); --------------- function functionTwo() {
| is actually | };
function functionTwo() { | interpreted |-->
} | like | functionTwo();
---------------
Other commenters have already covered the semantic difference of the two variants above. I wanted to note a stylistic difference: Only the "assignment" variation can set a property of another object.
I often build JavaScript modules with a pattern like this:
(function(){
var exports = {};
function privateUtil() {
...
}
exports.publicUtil = function() {
...
};
return exports;
})();
With this pattern, your public functions will all use assignment, while your private functions use declaration.
(Note also that assignment should require a semicolon after the statement, while declaration prohibits it.)
An illustration of when to prefer the first method to the second one is when you need to avoid overriding a function's previous definitions.
With
if (condition){
function myfunction(){
// Some code
}
}
, this definition of myfunction
will override any previous definition, since it will be done at parse-time.
While
if (condition){
var myfunction = function (){
// Some code
}
}
does the correct job of defining myfunction
only when condition
is met.
An important reason is to add one and only one variable as the "Root" of your namespace...
var MyNamespace = {}
MyNamespace.foo= function() {
}
or
var MyNamespace = {
foo: function() {
},
...
}
There are many techniques for namespacing. It's become more important with the plethora of JavaScript modules available.
Also see How do I declare a namespace in JavaScript?
Hoisting is the JavaScript interpreter’s action of moving all variable and function declarations to the top of the current scope.
However, only the actual declarations are hoisted. by leaving assignments where they are.
- variable's/Function's declared inside the page are global can access anywhere in that page.
- variable's/Functions declared inside the function are having local scope. means they are available/accessed inside the function body (scope), they are not available outside the function body.
Variable
Javascript is called loosely typed language. Which means Javascript variables can hold value of any Data-Type. Javascript automatically takes care of changing the variable-type based on the value/literal provided during runtime.
global_Page = 10; var global_Page; « undefined
« Integer literal, Number Type. ------------------- global_Page = 10; « Number
global_Page = 'Yash'; | Interpreted | global_Page = 'Yash'; « String
« String literal, String Type. « AS « global_Page = true; « Boolean
var global_Page = true; | | global_Page = function (){ « function
« Boolean Type ------------------- var local_functionblock; « undefined
global_Page = function (){ local_functionblock = 777;« Number
var local_functionblock = 777; };
// Assigning function as a data.
};
Function
function Identifier_opt ( FormalParameterList_opt ) {
FunctionBody | sequence of statements
« return; Default undefined
« return 'some data';
}
- functions declared inside the page are hoisted to top of the page having global access.
- functions declared inside the function-block are hoisted to top of the block.
Default return value of function is 'undefined', Variable declaration default value also 'undefined'
Scope with respect to function-block global. Scope with respect to page undefined | not available.
Function Declaration
function globalAccess() { function globalAccess() {
} ------------------- }
globalAccess(); | | function globalAccess() { « Re-Defined / overridden.
localAccess(); « Hoisted As « function localAccess() {
function globalAccess() { | | }
localAccess(); ------------------- localAccess(); « function accessed with in globalAccess() only.
function localAccess() { }
} globalAccess();
} localAccess(); « ReferenceError as the function is not defined
Function Expression
10; « literal
(10); « Expression (10).toString() -> '10'
var a;
a = 10; « Expression var a.toString() -> '10'
(function invoke() { « Expression Function
console.log('Self Invoking'); (function () {
}); }) () -> 'Self Invoking'
var f;
f = function (){ « Expression var Function
console.log('var Function'); f () -> 'var Function'
};
Function assigned to variable Example:
(function selfExecuting(){
console.log('IIFE - Immediately-Invoked Function Expression');
}());
var anonymous = function (){
console.log('anonymous function Expression');
};
var namedExpression = function for_InternalUSE(fact){
if(fact === 1){
return 1;
}
var localExpression = function(){
console.log('Local to the parent Function Scope');
};
globalExpression = function(){
console.log('creates a new global variable, then assigned this function.');
};
//return; //undefined.
return fact * for_InternalUSE( fact - 1);
};
namedExpression();
globalExpression();
javascript interpreted as
var anonymous;
var namedExpression;
var globalExpression;
anonymous = function (){
console.log('anonymous function Expression');
};
namedExpression = function for_InternalUSE(fact){
var localExpression;
if(fact === 1){
return 1;
}
localExpression = function(){
console.log('Local to the parent Function Scope');
};
globalExpression = function(){
console.log('creates a new global variable, then assigned this function.');
};
return fact * for_InternalUSE( fact - 1); // DEFAULT UNDEFINED.
};
namedExpression(10);
globalExpression();
You can check function declaration, expression test over different browser's using jsperf Test Runner
ES5 Constructor Function Classes: Function objects created using Function.prototype.bind
JavaScript treats functions as first-class objects, so being an object, you can assign properties to a function.
function Shape(id) { // Function Declaration
this.id = id;
};
// Adding a prototyped method to a function.
Shape.prototype.getID = function () {
return this.id;
};
Shape.prototype.setID = function ( id ) {
this.id = id;
};
var expFn = Shape; // Function Expression
var funObj = new Shape( ); // Function Object
funObj.hasOwnProperty('prototype'); // false
funObj.setID( 10 );
console.log( funObj.getID() ); // 10
ES6 introduced Arrow function: An arrow function expression has a shorter syntax, they are best suited for non-method functions, and they cannot be used as constructors.
ArrowFunction : ArrowParameters => ConciseBody
.const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; }; console.log( fn(2) ); // Even console.log( fn(3) ); // Odd
本文标签: javascriptvar functionNamefunction()vs function functionName() Stack Overflow
版权声明:本文标题:javascript - var functionName = function() {} vs function functionName() {} - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736659566a1946348.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论