var functionName = function(){} vs function functionName(){}
我最近开始维护别人的JavaScript代码。 我正在修复错误,增加function,并试图整理代码并使其更加一致。
以前的开发人员使用两种声明函数的方法,如果有没有原因,我不能解决。
两种方式是:
var functionOne = function() { // Some code };
function functionTwo() { // Some code }
使用这两种不同方法的原因是什么?每种方法的优缺点是什么? 有一种方法可以用另一种方法完成吗?
所不同的是, functionOne
是一个函数expression式,所以只有在到达该行时才被定义,而functionTwo
是一个函数声明,并且在其周围函数或脚本执行(由于提升 )时被定义。
例如,一个函数expression式:
// TypeError: undefined is not a function functionOne(); var functionOne = function() { console.log("Hello!"); };
首先,我想纠正Greg: function abc(){}
也是作用域 – 名称abc
是在遇到此定义的作用域中定义的。 例:
function xyz(){ function abc(){}; // abc is defined here... } // ...but not here
其次,可以将两种风格结合起来:
var xyz = function abc(){};
xyz
将像往常一样定义, abc
在所有浏览器中都是未定义的,但是Internet Explorer中 – 不要依赖它被定义。 但它将在其内部定义:
var xyz = function abc(){ // xyz is visible here // abc is visible here } // xyz is visible here // abc is undefined here
如果你想在所有浏览器上使用别名函数,请使用这种声明:
function abc(){}; var xyz = abc;
在这种情况下, xyz
和abc
都是同一个对象的别名:
console.log(xyz === abc); // prints "true"
使用组合样式的一个令人信服的理由是函数对象的“名称”属性( 不受Internet Explorer支持 )。 基本上,当你定义一个function
function abc(){}; console.log(abc.name); // prints "abc"
它的名字被自动分配。 但是,当你定义它
var abc = function(){}; console.log(abc.name); // prints ""
它的名字是空的 – 我们创build了一个匿名函数并将其分配给了一些variables。
使用组合样式的另一个很好的理由是使用一个简短的内部名称来引用它自己,同时为外部用户提供一个长的非冲突名称:
// 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); // ... }
在上面的例子中,我们可以用一个外部的名字来做同样的事情,但是它太笨拙了(而且比较慢)。
(另一种引用自身的方法是使用arguments.callee
,它仍然比较长,在严格模式下不受支持。)
在内心深处,JavaScript对待这两个语句的方式不同。 这是一个函数声明:
function abc(){}
这里的abc
是在当前范围内的任何地方定义的:
// We can call it here abc(); // Works // Yet, it is defined down there. function abc(){} // We can call it again abc(); // Works
另外,它通过一个return
声明来升起:
// We can call it here abc(); // Works return; function abc(){}
这是一个函数expression式:
var xyz = function(){};
这里的xyz
是从赋值的angular度定义的:
// We can't call it here xyz(); // UNDEFINED!!! // Now it is defined xyz = function(){} // We can call it here xyz(); // works
函数声明与函数expression式是Greg为什么会有区别的真正原因。
有趣的事实:
var xyz = function abc(){}; console.log(xyz.name); // Prints "abc"
就个人而言,我更喜欢“函数expression式”声明,因为这样我可以控制可见性。 当我定义的function
var abc = function(){};
我知道我在本地定义了这个function。 当我定义的function
abc = function(){};
我知道我在全球范围内定义了它,因为我没有在范围链中的任何地方定义abc
。 即使在eval()
使用,这种风格的定义也是有弹性的。 而定义
function abc(){};
取决于上下文,可能会让你猜它是在哪里实际定义的,特别是在eval()
的情况下 – 答案是:它取决于浏览器。
下面是创build函数的标准表格的简要说明:( 原本是为另一个问题编写的,但在被移入标准问题之后进行了修改)。
条款:
- ES5 : ECMAScript第五版 ,2009
- ES2015 : ECMAScript 2015 (也称为“ES6”)
快速列表:
-
函数声明
-
“匿名”
function
expression式(尽pipe有这个词,有时会得到名字) -
命名的
function
expression式 -
访问器函数初始化程序(ES5 +)
-
箭头函数expression式(ES2015 +) (这些在本质上也是匿名的,但有时会得到名字)
-
对象初始化程序中的方法声明(ES2015 +)
-
类中的构造函数和方法声明(ES2015 +)
函数声明
第一种forms是一个函数声明 ,如下所示:
function x() { console.log('x'); }
函数声明是一个声明 ; 这不是一个声明或expression。 因此,你不要跟着它;
(虽然这样做是无害的)。
函数声明在执行进入其出现的上下文时被处理, 然后执行任何逐步的代码。 它创build的函数被赋予一个合适的名字(上面例子中的x
),这个名字被放在声明出现的范围内。
因为它是在相同的上下文中的任何一步一步的代码之前处理的,你可以做这样的事情:
x(); // Works even though it's above the declaration function x() { console.log('x'); }
直到ES2015,规范并没有涵盖JavaScript引擎应该做什么,如果你把一个函数声明放在像try
, if
, switch
, while
等这样的控制结构中,像这样:
if (someCondition) { function foo() { // <===== HERE THERE } // <===== BE DRAGONS }
而且由于它们是在逐步执行的代码运行之前处理的,所以知道在控制结构中要做什么是很困难的。
尽pipe在ES2015之前这样做并没有被指定 ,但是它是一个允许的扩展来支持块中的函数声明。 不幸的是(不可避免地),不同的引擎做了不同的事情。
从ES2015开始,规范说明了要做什么。 事实上,它有三个独立的事情要做:
- 如果处于松散模式而不是 Web浏览器,JavaScript引擎应该做一件事情
- 如果在networking浏览器中处于松散模式,JavaScript引擎应该做其他事情
- 如果在严格模式下(浏览器与否),JavaScript引擎应该做的另一件事情
松散模式的规则是棘手的,但在严格模式下,块中的函数声明很容易:它们是块本地的(它们具有块范围 ,这在ES2015中也是新的),并且它们被boost到顶部的块。 所以:
"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)
“匿名” function
expression式
第二种常见forms称为匿名函数expression式 :
var y = function () { console.log('y'); };
像所有的expression式一样,它是在代码的逐步执行中达到的。
在ES5中,这个创build的函数没有名字(匿名)。 在ES2015中,如果可能的话,通过从上下文推断该函数,函数被分配一个名称。 在上面的例子中,名字是y
。 当函数是属性初始值设定项的值时,会做类似的事情。 (有关发生这种情况和规则的详细信息,请在ES2015规范中searchSetFunctionName – 它会遍布整个地方。
命名的function
expression式
第三种forms是一个命名函数expression式 (“NFE”):
var z = function w() { console.log('zw') };
这个创build的函数有一个合适的名字(在这种情况下是w
)。 像所有的expression式一样,这是在代码的逐步执行中达到的。 该函数的名称不会添加到expression式所在的作用域中; 该名称在该函数本身的范围内:
var z = function w() { console.log(typeof w); // "function" }; console.log(typeof w); // "undefined"
请注意,NFE经常是JavaScript实现的bug的来源。 IE8和更早版本,例如, 完全不正确地处理NFE,在两个不同的时间创build两个不同的function。 Safari的早期版本也有问题。 好消息是,当前版本的浏览器(IE9以上,当前的Safari)不再有这些问题了。 (但截至撰写本文时,遗憾的是,IE8仍然被广泛使用,因此使用带有networking代码的NFE通常仍然存在问题。)
访问器函数初始化程序(ES5 +)
有时候function可能在很大程度上被忽视; 访问器函数就是这种情况。 这是一个例子:
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"
请注意,当我使用该function时,我没有使用()
! 这是因为它是一个属性的访问函数 。 我们以正常的方式得到并设置属性,但在幕后,函数被调用。
您还可以使用Object.defineProperty
, Object.defineProperties
和Object.create
的较less已知的第二个参数创build存取函数。
箭头函数expression式(ES2015 +)
ES2015为我们带来了箭头function 。 这里有一个例子:
var a = [1, 2, 3]; var b = a.map(n => n * 2); console.log(b.join(", ")); // 2, 4, 6
看到隐藏在map()
调用中的n => n * 2
东西? 这是一个function。
有关箭头function的一些事情:
-
他们
this
是词汇绑定 ,不被确定时,他们被称为。 这意味着他们内部的this
与他们创造的地方是一样的。 -
正如你会注意到上述,你不使用关键字
function
; 相反,你使用=>
。
上面的n => n * 2
例子就是其中的一种forms。 如果你有多个参数来传递函数,你可以使用parens:
var a = [1, 2, 3]; var b = a.map((n, i) => n * i); console.log(b.join(", ")); // 0, 2, 6
(请记住, Array#map
会将条目作为第一个参数,将索引作为第二个参数。
如果您不仅仅使用单个expression式,而是正常使用{}
:
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));
对象初始化程序中的方法声明(ES2015 +)
ES2015允许声明一个引用函数的属性的简短forms; 它看起来像这样:
var o = { foo() { } };
ES5和更早版本中的等价物将是:
var o = { foo: function foo() { } };
类中的构造函数和方法声明(ES2015 +)
ES2015为我们带来了class
语法,包括声明的构造函数和方法:
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName() { return this.firstName + " " + this.lastName; } }
上面有两个函数声明:一个是构造函数,它的名字是Person
,另一个是getFullName
,它是一个赋给Person.prototype
的函数。
说到全局上下文,最后的var
语句和FunctionDeclaration
都会在全局对象上创build一个不可删除的属性,但是两者的值都可以被覆盖 。
这两种方式之间的细微差别是,当variables实例化过程运行时(在实际的代码执行之前),所有用var
声明的标识符将被初始化为undefined
,并且函数声明使用的标识符自那时起将是可用的,例:
alert(typeof foo); // 'function', it's already available alert(typeof bar); // 'undefined' function foo () {} var bar = function () {}; alert(typeof bar); // 'function'
bar
FunctionExpression
expression式的分配发生在运行时。
由FunctionDeclaration
创build的全局属性可以像variables值一样被覆盖而没有任何问题,例如:
function test () {} test = null;
你的两个例子之间的另一个明显的区别是第一个函数没有名字,但是第二个函数具有它,这在debugging(即检查调用堆栈)时非常有用。
关于你编辑的第一个例子( foo = function() { alert('hello!'); };
),这是一个未声明的任务,我会极力鼓励你总是使用var
关键字。
使用赋值,如果没有var
语句,如果在作用域链中找不到引用的标识符,它将成为全局对象的可删除属性。
另外,未声明的赋值在严格模式下的ECMAScript 5上引发ReferenceError
。
必须阅读:
- 命名函数expression式被揭秘
注意 :这个答案已经从另一个问题合并了,其中OP的主要疑问和误解是用FunctionDeclaration
声明的标识符不能被覆盖,而不是这种情况。
您发布的两个代码片段几乎可以用于所有目的,行为方式相同。
但是,行为的差异在于,对于第一个变体( var functionOne = function() {}
),只能在代码中的该点之后调用该函数。
使用第二个变体( function functionTwo()
),该函数可用于声明函数上方的代码。
这是因为在第一个变体中,函数在运行时被分配给variablesfoo
。 第二,函数在parsing时被分配给该标识符foo
。
更多的技术信息
JavaScript有三种定义函数的方法。
- 您的第一个片段显示了一个函数expression式 。 这涉及到使用“函数”操作符创build一个函数 – 该运算符的结果可以存储在任何variables或对象属性中。 函数expression式是非常强大的。 函数expression式通常被称为“匿名函数”,因为它不必具有名称,
- 你的第二个例子是一个函数声明 。 这使用“函数”语句来创build一个函数。 该函数在parsing时可用,并可在该范围内的任何地方调用。 您以后仍然可以将其存储在variables或对象属性中。
- 定义一个函数的第三种方法是“Function()”构造函数 ,它不会在原始文章中显示。 不build议使用这个,因为它与
eval()
有相同的方式,这有其问题。
更好的解释格雷格的答案
functionTwo(); function functionTwo() { }
为什么没有错误? 我们总是被教导,expression式是从上到下执行的(??)
因为:
函数声明和variables声明总是被JavaScript解释器无形地移动(
hoisted
)到其包含范围的顶部。 显然,函数参数和语言定义的名字已经存在。 本樱桃
这意味着这样的代码:
functionOne(); --------------- var functionOne; | is actually | functionOne(); var functionOne = function(){ | interpreted |--> }; | like | functionOne = function(){ --------------- };
请注意,声明的分配部分没有被挂起。 只有名字被悬挂。
但是在函数声明的情况下,整个函数体也将被挂起 :
functionTwo(); --------------- function functionTwo() { | is actually | }; function functionTwo() { | interpreted |--> } | like | functionTwo(); ---------------
其他评论者已经涵盖了上述两个变体的语义差异。 我想要注意一个风格的区别:只有“赋值”变体可以设置另一个对象的属性。
我经常用这样的模式构buildJavaScript模块:
(function(){ var exports = {}; function privateUtil() { ... } exports.publicUtil = function() { ... }; return exports; })();
有了这个模式,你的公用函数将全部使用赋值,而你的私有函数使用声明。
(还要注意,在声明之后,赋值应该需要分号,而声明则禁止它。)
第一个方法到第二个方法的时间的例子是,当你需要避免重写一个函数的以前的定义。
同
if (condition){ function myfunction(){ // Some code } }
, myfunction
这个定义会覆盖以前的任何定义,因为它将在分析时完成。
而
if (condition){ var myfunction = function (){ // Some code } }
只有在condition
满足的condition
才能正确定义myfunction
。
一个重要的原因是添加一个且只有一个variables作为您的命名空间的“根”…
var MyNamespace = {} MyNamespace.foo= function() { }
要么
var MyNamespace = { foo: function() { }, ... }
有很多命名空间的技巧。 随着大量的JavaScript模块可用,这变得越来越重要。
另请参阅如何在JavaScript中声明名称空间?
提升 是JavaScript解释器将所有variables和函数声明移动到当前作用域顶部的操作。
但是,只有实际的申报是悬挂的。 把任务留在原地。
- 在页面内声明的variables的/函数是全局的,可以访问该页面的任何地方。
- variables的/函数内部声明的函数具有局部范围。 意味着它们在函数体(scope)中是可用/访问的,它们在函数体外部是不可用的。
variables
JavaScript被称为松散types的语言。 这意味着Javascriptvariables可以保存任何数据types的值。 Javascript会根据运行时提供的值/字面量来自动修改variablestypes。
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'; }
- 在页面内声明的函数被悬挂在具有全局访问权限的页面的顶部。
- 在函数块内部声明的函数被提升到块的顶部。
-
函数的默认返回值是' undefined ', variables声明的默认值也是'undefined'
Scope with respect to function-block global. Scope with respect to page undefined | not available.
函数声明
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
函数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' };
分配给variables的函数示例:
(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解释为
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();
你可以使用jsperf Test Runner
来检查不同浏览器的函数声明,expression式testing
ES5构造函数类 :使用Function.prototype.bind创build的函数对象
JavaScript把函数当作第一类对象,所以作为一个对象,你可以给函数赋予属性。
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引入了Arrow函数 :箭头函数expression式的语法较短,最适合用于非方法函数,不能用作构造函数。
ArrowFunction : ArrowParameters => ConciseBody
。const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; }; console.log( fn(2) ); // Even console.log( fn(3) ); // Odd
A function declaration and a function expression assigned to a variable behave the same once the binding is established.
There is a difference however at how and when the function object is actually associated with its variable. This difference is due to the mechanism called variable hoisting in JavaScript.
Basically, all function declarations and variable declarations are hoisted to the top of the function in which the declaration occurs (this is why we say that JavaScript has function scope ).
-
When a function declaration is hoisted, the function body "follows" so when the function body is evaluated, the variable will immediately be bound to a function object.
-
When a variable declaration is hoisted, the initialization does not follow, but is "left behind". The variable is initialized to
undefined
at the start of the function body, and will be assigned a value at its original location in the code. (Actually, it will be assigned a value at every location where a declaration of a variable with the same name occurs.)
The order of hoisting is also important: function declarations take precedence over variable declarations with the same name, and the last function declaration takes precedence over previous function declarations with the same name.
Some examples…
var foo = 1; function bar() { if (!foo) { var foo = 10 } return foo; } bar() // 10
Variable foo
is hoisted to the top of the function, initialized to undefined
, so that !foo
is true
, so foo
is assigned 10
. The foo
outside of bar
's scope plays no role and is untouched.
function f() { return a; function a() {return 1}; var a = 4; function a() {return 2}} f()() // 2 function f() { return a; var a = 4; function a() {return 1}; function a() {return 2}} f()() // 2
Function declarations take precedence over variable declarations, and the last function declaration "sticks".
function f() { var a = 4; function a() {return 1}; function a() {return 2}; return a; } f() // 4
In this example a
is initialized with the function object resulting from evaluating the second function declaration, and then is assigned 4
.
var a = 1; function b() { a = 10; return; function a() {}} b(); a // 1
Here the function declaration is hoisted first, declaring and initializing variable a
. Next, this variable is assigned 10
. In other words: the assignment does not assign to outer variable a
.
The first example is a function declaration:
function abc(){}
The second example is a function expression:
var abc = function() {};
The main difference is how they are hoisted (lifted and declared). In the first example, the whole function declaration is hoisted. In the second example only the var 'abc' is hoisted, its value (the function) will be undefined, and the function itself remains at the position that it is declared.
简单地说:
//this will work abc(param); function abc(){} //this would fail abc(param); var abc = function() {}
To study more about this topic I strongly recommend you this link
In terms of code maintenance cost, named functions are more preferable:
- Independent from the place where they are declared (but still limited by scope).
- More resistant to mistakes like conditional initialization (you are still able to override if wanted to).
- The code becomes more readable by allocating local functions separately of scope functionality. Usually in the scope the functionality goes first, followed by declarations of local functions.
- In a debugger you will clearly see the function name on the call stack instead of an "anonymous/evaluated" function.
I suspect more PROS for named functions are follow. And what is listed as an advantage of named functions is a disadvantage for anonymous ones.
Historically, anonymous functions appeared from the inability of JavaScript as a language to list members with named functions:
{ member:function() { /* How do I make "this.member" a named function? */ } }
I use the variable approach in my code for a very specific reason, the theory of which has been covered in an abstract way above, but an example might help some people like me, with limited JavaScript expertise.
I have code that I need to run with 160 independently-designed brandings. Most of the code is in shared files, but branding-specific stuff is in a separate file, one for each branding.
Some brandings require specific functions, and some do not. Sometimes I have to add new functions to do new branding-specific things. I am happy to change the shared coded, but I don't want to have to change all 160 sets of branding files.
By using the variable syntax, I can declare the variable (a function pointer essentially) in the shared code and either assign a trivial stub function, or set to null.
The one or two brandings that need a specific implementation of the function can then define their version of the function and assign this to the variable if they want, and the rest do nothing. I can test for a null function before I execute it in the shared code.
From people's comments above, I gather it may be possible to redefine a static function too, but I think the variable solution is nice and clear.
Greg's Answer is good enough, but I still would like to add something to it that I learned just now watching Douglas Crockford's videos.
Function expression:
var foo = function foo() {};
Function statement:
function foo() {};
The function statement is just a shorthand for var
statement with a function
value.
所以
function foo() {};
expands to
var foo = function foo() {};
Which expands further to:
var foo = undefined; foo = function foo() {};
And they are both hoisted to the top of the code.
In computer science terms, we talk about anonymous functions and named functions. I think the most important difference is that an anonymous function is not bound to an name, hence the name anonymous function. In JavaScript it is a first class object dynamically declared at runtime.
For more information on anonymous functions and lambda calculus, Wikipedia is a good start ( http://en.wikipedia.org/wiki/Anonymous_function ).
@EugeneLazutkin gives an example where he names an assigned function to be able to use shortcut()
as an internal reference to itself. John Resig gives another example – copying a recursive function assigned to another object in his Learning Advanced Javascript tutorial. While assigning functions to properties isn't strictly the question here, I recommend actively trying the tutorial out – run the code by clicking the button in the upper right corner, and double click the code to edit to your liking.
Examples from the tutorial: recursive calls in yell()
:
Tests fail when the original ninja object is removed. (page 13)
var ninja = { yell: function(n){ return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; } }; assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); var samurai = { yell: ninja.yell }; var ninja = null; try { samurai.yell(4); } catch(e){ assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); }
If you name the function that will be called recursively, the tests will pass. (page 14)
var ninja = { yell: function yell(n){ return n > 0 ? yell(n-1) + "a" : "hiy"; } }; assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); var samurai = { yell: ninja.yell }; var ninja = {}; assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );
If you would use those functions to create objects, you would get:
var objectOne = new functionOne(); console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function var objectTwo = new functionTwo(); console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function
I'm listing out the differences below:
-
A function declaration can be placed anywhere in the code. Even if it is invoked before the definition appears in code, it gets executed as function declaration is committed to memory or in a way it is hoisted up, before any other code in the page starts execution.
Take a look at the function below:
function outerFunction() { function foo() { return 1; } return foo(); function foo() { return 2; } } alert(outerFunction()); // Displays 2
This is because, during execution, it looks like:-
function foo() { // The first function declaration is moved to top return 1; } function foo() { // The second function declaration is moved to top return 2; } function outerFunction() { return foo(); } alert(outerFunction()); //So executing from top to bottom, //the last foo() returns 2 which gets displayed
A function expression, if not defined before calling it, will result in an error. Also, here the function definition itself is not moved to the top or committed to memory like in the function declarations. But the variable to which we assign the function gets hoisted up and undefined gets assigned to it.
Same function using function expressions:
function outerFunction() { var foo = function() { return 1; } return foo(); var foo = function() { return 2; } } alert(outerFunction()); // Displays 1
This is because during execution, it looks like:
function outerFunction() { var foo = undefined; var foo = undefined; foo = function() { return 1; }; return foo (); foo = function() { // This function expression is not reachable return 2; }; } alert(outerFunction()); // Displays 1
-
It is not safe to write function declarations in non-function blocks like if because they won't be accessible.
if (test) { function x() { doSomething(); } }
-
Named function expression like the one below, may not work in Internet Explorer browsers prior to version 9.
var today = function today() {return new Date()}
Another difference that is not mentioned in the other answers is that if you use the anonymous function
var functionOne = function() { // Some code };
and use that as a constructor as in
var one = new functionOne();
then one.constructor.name
will not be defined. Function.name
is non-standard but is supported by Firefox, Chrome, other Webkit-derived browsers and IE 9+.
同
function functionTwo() { // Some code } two = new functionTwo();
it is possible to retrieve the name of the constructor as a string with two.constructor.name
.
The first one (function doSomething(x)) should be part of an object notation.
The second one ( var doSomething = function(x){ alert(x);}
) is simply creating an anonymous function and assigning it to a variable, doSomething
. So doSomething() will call the function.
You may want to know what a function declaration and function expression is.
A function declaration defines a named function variable without requiring variable assignment. Function declarations occur as standalone constructs and cannot be nested within non-function blocks.
function foo() { return 3; }
ECMA 5 (13.0) defines the syntax as
function Identifier ( FormalParameterList opt ) { FunctionBody }
In above condition the function name is visible within its scope and the scope of its parent (otherwise it would be unreachable).
And in a function expression
A function expression defines a function as a part of a larger expression syntax (typically a variable assignment ). Functions defined via functions expressions can be named or anonymous. Function expressions should not start with “function”.
// Anonymous function expression var a = function() { return 3; } // Named function expression var a = function foo() { return 3; } // Self-invoking function expression (function foo() { alert("hello!"); })();
ECMA 5 (13.0) defines the syntax as
function Identifier opt ( FormalParameterList opt ) { FunctionBody }
In light of the "named functions show up in stack traces" argument, modern JavaScript engines are actually quite capable of representing anonymous functions.
As of this writing, V8, SpiderMonkey, Chakra and Nitro always refer to named functions by their names. They almost always refer to an anonymous function by its identifier if it has one.
SpiderMonkey can figure out the name of an anonymous function returned from another function. The rest can't.
If you really, really wanted your iterator and success callbacks to show up in the trace, you could name those too…
[].forEach(function iterator() {});
But for the most part it's not worth stressing over.
Harness ( Fiddle )
'use strict'; var a = function () { throw new Error(); }, b = function b() { throw new Error(); }, c = function d() { throw new Error(); }, e = { f: a, g: b, h: c, i: function () { throw new Error(); }, j: function j() { throw new Error(); }, k: function l() { throw new Error(); } }, m = (function () { return function () { throw new Error(); }; }()), n = (function () { return function n() { throw new Error(); }; }()), o = (function () { return function p() { throw new Error(); }; }()); console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) { return values.concat(e[key]); }, [])).concat([m, n, o]).reduce(function (logs, func) { try { func(); } catch (error) { return logs.concat('func.name: ' + func.name + '\n' + 'Trace:\n' + error.stack); // Need to manually log the error object in Nitro. } }, []).join('\n\n'));
V8
func.name: Trace: Error at a (http://localhost:8000/test.js:4:11) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: b Trace: Error at b (http://localhost:8000/test.js:7:15) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: d Trace: Error at d (http://localhost:8000/test.js:10:15) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: Trace: Error at a (http://localhost:8000/test.js:4:11) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: b Trace: Error at b (http://localhost:8000/test.js:7:15) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: d Trace: Error at d (http://localhost:8000/test.js:10:15) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: Trace: Error at ei (http://localhost:8000/test.js:17:19) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: j Trace: Error at j (http://localhost:8000/test.js:20:19) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: l Trace: Error at l (http://localhost:8000/test.js:23:19) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: Trace: Error at http://localhost:8000/test.js:28:19 at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: n Trace: Error at n (http://localhost:8000/test.js:33:19) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 func.name: p Trace: Error at p (http://localhost:8000/test.js:38:19) at http://localhost:8000/test.js:47:9 at Array.reduce (native) at http://localhost:8000/test.js:44:27 test.js:42
SpiderMonkey
func.name: Trace: a@http://localhost:8000/test.js:4:5 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: b Trace: b@http://localhost:8000/test.js:7:9 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: d Trace: d@http://localhost:8000/test.js:10:9 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: Trace: a@http://localhost:8000/test.js:4:5 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: b Trace: b@http://localhost:8000/test.js:7:9 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: d Trace: d@http://localhost:8000/test.js:10:9 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: Trace: ei@http://localhost:8000/test.js:17:13 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: j Trace: j@http://localhost:8000/test.js:20:13 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: l Trace: l@http://localhost:8000/test.js:23:13 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: Trace: m</<@http://localhost:8000/test.js:28:13 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: n Trace: n@http://localhost:8000/test.js:33:13 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1 func.name: p Trace: p@http://localhost:8000/test.js:38:13 @http://localhost:8000/test.js:47:9 @http://localhost:8000/test.js:54:1
脉轮
func.name: undefined Trace: Error at a (http://localhost:8000/test.js:4:5) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at b (http://localhost:8000/test.js:7:9) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at d (http://localhost:8000/test.js:10:9) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at a (http://localhost:8000/test.js:4:5) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at b (http://localhost:8000/test.js:7:9) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at d (http://localhost:8000/test.js:10:9) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at ei (http://localhost:8000/test.js:17:13) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at j (http://localhost:8000/test.js:20:13) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at l (http://localhost:8000/test.js:23:13) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at Anonymous function (http://localhost:8000/test.js:28:13) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at n (http://localhost:8000/test.js:33:13) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1) func.name: undefined Trace: Error at p (http://localhost:8000/test.js:38:13) at Anonymous function (http://localhost:8000/test.js:47:9) at Global code (http://localhost:8000/test.js:42:1)
硝基
func.name: Trace: a@http://localhost:8000/test.js:4:22 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: b Trace: b@http://localhost:8000/test.js:7:26 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: d Trace: d@http://localhost:8000/test.js:10:26 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: Trace: a@http://localhost:8000/test.js:4:22 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: b Trace: b@http://localhost:8000/test.js:7:26 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: d Trace: d@http://localhost:8000/test.js:10:26 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: Trace: i@http://localhost:8000/test.js:17:30 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: j Trace: j@http://localhost:8000/test.js:20:30 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: l Trace: l@http://localhost:8000/test.js:23:30 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: Trace: http://localhost:8000/test.js:28:30 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: n Trace: n@http://localhost:8000/test.js:33:30 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33 func.name: p Trace: p@http://localhost:8000/test.js:38:30 http://localhost:8000/test.js:47:13 reduce@[native code] global code@http://localhost:8000/test.js:44:33
In JavaScript there are two ways to create functions:
-
Function declaration:
function fn(){ console.log("Hello"); } fn();
This is very basic, self-explanatory, used in many languages and standard across C family of languages. We declared a function defined it and executed it by calling it.
What you should be knowing is that functions are actually objects in JavaScript; internally we have created an object for above function and given it a name called fn or the reference to the object is stored in fn. Functions are objects in JavaScript; an instance of function is actually an object instance.
-
Function expression:
var fn=function(){ console.log("Hello"); } fn();
JavaScript has first-class functions, that is, create a function and assign it to a variable just like you create a string or number and assign it to a variable. Here, the fn variable is assigned to a function. The reason for this concept is functions are objects in JavaScript; fn is pointing to the object instance of the above function. We have initialized a function and assigned it to a variable. It's not executing the function and assigning the result.
Reference: JavaScript function declaration syntax: var fn = function() {} vs function fn() {}
Both are different ways of defining a function. The difference is how the browser interprets and loads them into an execution context.
The first case is of function expressions which loads only when the interpreter reaches that line of code. So if you do it like the following, you will get an error that the functionOne is not a function .
functionOne(); var functionOne = function() { // Some code };
The reason is that on the first line no value is assigned to functionOne, and hence it is undefined. We are trying to call it as a function, and hence we are getting an error.
On the second line we are assigning the reference of an anonymous function to functionOne.
The second case is of function declarations that loads before any code is executed. So if you do like the following you won't get any error as the declaration loads before code execution.
functionOne(); function functionOne() { // Some code }
They are pretty similar with some small differences, first one is a variable which assigned to an anonymous function (Function Declaration) and second one is the normal way to create a function in JavaScript(Anonymous function Declaration), both has usage, cons and pros:
1. Function Expression
var functionOne = function() { // Some code };
A Function Expression defines a function as a part of a larger expression syntax (typically a variable assignment ). Functions defined via Functions Expressions can be named or anonymous. Function Expressions must not start with “function” (hence the parentheses around the self invoking example below).
Assign a variable to a function, means no Hoisting, as we know functions in JavaScript can Hoist, means they can be called before they get declared, while variables need to be declared before getting access to them, so means in this case we can not access the function before where it's declared, also it could be a way that you write your functions, for the functions which return another function, this kind of declaration could make sense, also in ECMA6 & above you can assign this to an arrow function which can be used to call anonymous functions, also this way of declaring is a better way to create Constructor functions in JavaScript.
2. Function Declaration
function functionTwo() { // Some code }
A Function Declaration defines a named function variable without requiring variable assignment. Function Declarations occur as standalone constructs and cannot be nested within non-function blocks. It's helpful to think of them as siblings of Variable Declarations. Just as Variable Declarations must start with “var”, Function Declarations must begin with “function”.
This is the normal way of calling a function in JavaScript, this function can be called before you even declare it as in JavaScript all functions get Hoisted, but if you have 'use strict' this won't Hoist as expected, it's a good way to call all normal functions which are not big in lines and neither are a constructor function.
Also, if you need more info about how hoisting works in JavaScript, visit the link below:
About performance:
New versions of V8
introduced several under-the-hood optimizations and so did SpiderMonkey
.
There is almost no difference now between expression and declaration.
Function expression appears to be faster now.
Chrome 62.0.3202
FireFox 55
Chrome Canary 63.0.3225
Anonymous
function expressions appear to have better performance againstNamed
function expression.
Firefox Chrome Canary Chrome
This is just two possible ways of declaring functions, and in the second way, you can use the function before declaration.
new Function()
can be used to pass the function's body in a string. And hence this can be used to create dynamic functions. Also passing the script without executing the script.
var func = new Function("x", "y", "return x*y;"); function secondFunction(){ var result; result = func(10,20); console.log ( result ); } secondFunction()