var functionName = function() vs function functionName()
Posted
技术标签:
【中文标题】var functionName = function() vs function functionName()【英文标题】:var functionName = function() vs function functionName() var functionName = function() vs function functionName() 【发布时间】:2010-09-25 02:39:49 【问题描述】:我最近开始维护别人的 javascript 代码。我正在修复错误、添加功能并尝试整理代码并使其更加一致。
之前的开发者使用了两种声明函数的方式,我不知道这背后是否有原因。
两种方式分别是:
var functionOne = function()
// Some code
;
function functionTwo()
// Some code
使用这两种不同方法的原因是什么,各自的优缺点是什么?有什么可以用一种方法做而另一种方法做不到的事情吗?
【问题讨论】:
【参考方案1】:不同之处在于functionOne
是一个函数表达式,因此仅在到达该行时才定义,而functionTwo
是一个函数声明,并在其周围的函数或脚本执行后立即定义(由于@987654321 @)。
例如一个函数表达式:
// TypeError: functionOne is not a function
functionOne();
var functionOne = function()
console.log("Hello!");
;
还有,一个函数声明:
// Outputs: "Hello!"
functionTwo();
function functionTwo()
console.log("Hello!");
从历史上看,块内定义的函数声明在浏览器之间的处理方式不一致。严格模式(在 ES5 中引入)通过将函数声明的范围限定为它们的封闭块来解决这个问题。
'use strict';
// note this block!
function functionThree()
console.log("Hello!");
functionThree(); // ReferenceError
【讨论】:
函数定义在代码进入周围块时执行,而不是在进入封闭函数时执行。我不知道事情是否总是这样,但如果一个块使用let
或const
来定义一个被其中的函数封闭的变量,并且一致地应用该规则可能会更好而不是只在不可避免的时候才应用它。
“由于提升”这句话可能会给人一种错误的印象,即只有指定的函数会被提升。事实上,var functionOne
和 function functionTwo
都在某种程度上被提升了 - 只是 functionOne 设置为未定义(你可以称之为半提升,变量总是只提升到那个程度)而函数 functionTwo 完全提升它的定义和声明。调用未定义的东西当然会抛出 typeError。
感谢 Greg 和 @Ben_Aston 解释差异。但是,你们中的任何一个人是否也可以按照用户的要求说明“优点”和“缺点”?【参考方案2】:
首先我要更正 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 abc();
console.log(abc.name); // prints "abc"
它的名字是自动分配的。但是当你像这样定义它时
var abc = function();
console.log(abc.name); // prints ""
它的名字是空的——我们创建了一个匿名函数并将它分配给某个变量。
使用组合样式的另一个好理由是使用简短的内部名称来引用自身,同时为外部用户提供不冲突的长名称:
// 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()
这是一个函数表达式:
var xyz = function();
xyz
这里是从赋值的角度定义的:
// We can't call it here
xyz(); // UNDEFINED!!!
// Now it is defined
xyz = function()
// We can call it here
xyz(); // works
函数声明与函数表达式是 Greg 展示差异的真正原因。
有趣的事实:
var xyz = function abc();
console.log(xyz.name); // Prints "abc"
就我个人而言,我更喜欢“函数表达式”声明,因为这样我可以控制可见性。当我定义函数时
var abc = function();
我知道我在本地定义了函数。当我定义函数时
abc = function();
我知道我在全局范围内定义了它,前提是我没有在范围链中的任何地方定义abc
。即使在eval()
中使用,这种定义风格也是有弹性的。而定义
function abc();
取决于上下文,可能会让您猜测它的实际定义位置,尤其是在 eval()
的情况下——答案是:这取决于浏览器。
【讨论】:
var abc = function();控制台.log(abc.name); // “abc” // 从 2021 年开始 显然,JS 运行时变得更智能了。然而把它包起来: var abc = (() => function())();控制台.log(abc.name); // 什么都没有 @EugeneLazutkin 您正在执行该函数并尝试读取结果的名称。除掉 '();'部分和你的例子将是正确的;) @ikirachen 我不明白你指的是什么。您能否为您的建议提供一些背景信息,以便我可以找到您在说什么? @EugeneLazutkin 您正在定义一个函数并立即调用(调用)它,也称为 IIFE(立即调用函数表达式),这是一种实现词法作用域的方法(IIFE 内部的任何内容都不会可以在它之外访问)。所以abc
的值不是函数本身,而是函数的返回值。 abc.name 为空是有意义的,因为 abc 返回一个未命名的函数。 @ikirachen 提到删除 ()
因为那是调用该函数的原因。没有它,它只是用多余的括号括起来。【参考方案3】:
以下是创建函数的标准表单的概要:(最初是为另一个问题编写的,但在被移入规范问题后进行了调整。)
条款:
ES5:ECMAScript 5th edition,2009 ES2015:ECMAScript 2015(也称为“ES6”)快速列表:
函数声明
“匿名”function
表达式(尽管有这个术语,但有时会创建带有名称的函数)
命名为function
表达式
访问函数初始化器 (ES5+)
箭头函数表达式 (ES2015+) (与匿名函数表达式一样,不涉及显式名称,但可以创建具有名称的函数)
对象初始化器中的方法声明 (ES2015+)
class
(ES2015+) 中的构造函数和方法声明
函数声明
第一种形式是函数声明,如下所示:
function x()
console.log('x');
函数声明是一个声明;它不是一个陈述或表达。因此,您不要使用;
来关注它(尽管这样做是无害的)。
函数声明在执行进入它出现的上下文时被处理,在任何分步代码被执行之前。它创建的函数有一个正确的名称(上例中的x
),并且该名称被放置在声明出现的范围内。
因为它是在同一上下文中的任何分步代码之前处理的,您可以执行以下操作:
x(); // Works even though it's above the declaration
function x()
console.log('x');
在 ES2015 之前,该规范没有涵盖如果将函数声明放入控制结构(如 try
、if
、switch
、while
等)中,JavaScript 引擎应该做什么,就像这样:
if (someCondition)
function foo() // <===== HERE THERE
// <===== BE DRAGONS
而且由于它们是在运行分步代码之前处理的,因此很难知道当它们处于控制结构中时要做什么。
虽然直到 ES2015 才指定这样做,但它是一个允许的扩展,以支持块中的函数声明。不幸的是(并且不可避免地),不同的引擎做了不同的事情。
从 ES2015 开始,规范说明了要做什么。事实上,它提供了三件不同的事情要做:
-
如果在网络浏览器上不处于松散模式,JavaScript 引擎应该做一件事
如果在网络浏览器上处于松散模式,JavaScript 引擎应该执行其他操作
如果在 strict 模式下(浏览器与否),JavaScript 引擎应该做另一件事
松散模式的规则很棘手,但在 strict 模式下,块中的函数声明很简单:它们是块的本地(它们具有 块范围 ,这在 ES2015 中也是新的),并且它们被提升到块的顶部。所以:
"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
表达式
第二种常用形式称为匿名函数表达式:
var y = function ()
console.log('y');
;
与所有表达式一样,它会在代码的逐步执行过程中到达时进行评估。
在 ES5 中,此创建的函数没有名称(它是匿名的)。在 ES2015 中,如果可能,函数会通过从上下文中推断来分配一个名称。在上面的示例中,名称将是 y
。当函数是属性初始值设定项的值时,会执行类似的操作。 (有关何时发生这种情况和规则的详细信息,请在 the specification 中搜索 SetFunctionName
——它到处出现。)
命名为function
表达式
第三种形式是命名函数表达式(“NFE”):
var z = function w()
console.log('zw')
;
此创建的函数有一个专有名称(在本例中为w
)。与所有表达式一样,在逐步执行代码时会评估 this。函数名不加到表达式出现的作用域;名称 在函数本身的范围内:
var z = function w()
console.log(typeof w); // "function"
;
console.log(typeof w); // "undefined"
请注意,NFE 经常成为 JavaScript 实现的错误来源。例如,IE8 和更早版本处理 NFE completely incorrectly,在两个不同的时间创建两个不同的函数。 Safari 的早期版本也存在问题。好消息是当前版本的浏览器(IE9 及更高版本,当前 Safari)不再存在这些问题。 (但遗憾的是,在撰写本文时,IE8 仍在广泛使用,因此将 NFE 与 Web 代码一起使用通常仍然存在问题。)
访问函数初始化器 (ES5+)
有时功能可能会在很大程度上被忽视; 访问器函数就是这种情况。这是一个例子:
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"
注意,我使用函数的时候,没有使用()
!那是因为它是一个属性的访问器函数。我们以正常方式获取和设置属性,但在后台调用函数。
您还可以使用Object.defineProperty
、Object.defineProperties
和Object.create
的鲜为人知的第二个参数创建访问器函数。
箭头函数表达式(ES2015+)
ES2015 为我们带来了箭头函数。这是一个例子:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
看到n => n * 2
隐藏在map()
调用中的东西了吗?这是一个函数。
关于箭头函数的几点说明:
他们没有自己的this
。相反,它们关闭定义它们的上下文的this
。 (它们还关闭了arguments
和super
。)这意味着它们中的this
与创建它们的this
相同,并且不能更改。
正如您在上面看到的那样,您不使用关键字function
;相反,您使用=>
。
上面的n => n * 2
示例就是其中的一种形式。如果您有多个参数来传递函数,则使用括号:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(请记住,Array#map
将条目作为第一个参数传递,索引作为第二个参数传递。)
在这两种情况下,函数体只是一个表达式;函数的返回值将自动成为该表达式的结果(您不使用显式的return
)。
如果您做的不仅仅是单个表达式,请照常使用 和显式
return
(如果您需要返回值):
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 允许以更短的形式声明引用称为方法定义的函数的属性;它看起来像这样:
var o =
foo()
;
在 ES5 及更早版本中几乎等效的是:
var o =
foo: function foo()
;
区别(除了冗长)是方法可以使用super
,但函数不能。因此,例如,如果您有一个使用方法语法定义(例如)valueOf
的对象,它可以使用 super.valueOf()
来获取 Object.prototype.valueOf
将返回的值(在可能用它做其他事情之前),而 ES5版本必须改为Object.prototype.valueOf.call(this)
。
这也意味着该方法具有对其定义的对象的引用,因此如果该对象是临时对象(例如,您将其作为源对象之一传递给 Object.assign
),方法语法 可能意味着对象被保留在内存中,否则它可能会被垃圾回收(如果 JavaScript 引擎没有检测到这种情况并在没有任何方法使用 super
的情况下处理它)。
class
(ES2015+) 中的构造函数和方法声明
ES2015 为我们带来了class
语法,包括声明的构造函数和方法:
class Person
constructor(firstName, lastName)
this.firstName = firstName;
this.lastName = lastName;
getFullName()
return this.firstName + " " + this.lastName;
上面有两种函数声明:一种用于构造函数,其名称为Person
,另一种用于getFullName
,它是分配给Person.prototype
的函数。
【讨论】:
【参考方案4】:说到全局上下文,var
语句和最后的 FunctionDeclaration
都会在全局对象上创建一个 不可删除 属性,但两者的值 可以被覆盖。
这两种方式的细微差别在于,当Variable Instantiation进程运行时(在实际代码执行之前),所有用var
声明的标识符都将用undefined
初始化,而FunctionDeclaration
使用的标识符将从那一刻起可用,例如:
alert(typeof foo); // 'function', it's already available
alert(typeof bar); // 'undefined'
function foo ()
var bar = function () ;
alert(typeof bar); // 'function'
bar
FunctionExpression
的分配一直持续到运行时。
FunctionDeclaration
创建的全局属性可以像变量值一样被覆盖而没有任何问题,例如:
function test ()
test = null;
您的两个示例之间的另一个明显区别是第一个函数没有名称,但第二个函数有名称,这在调试(即检查调用堆栈)时非常有用。
关于您编辑的第一个示例 (foo = function() alert('hello!'); ;
),这是一个未声明的分配,我强烈建议您始终使用 var
关键字。
有了赋值,没有var
语句,如果在作用域链中找不到被引用的标识符,它将成为全局对象的一个可删除属性。
此外,未声明的赋值会在 ECMAScript 5 上的 Strict Mode 下抛出 ReferenceError
。
必读:
Named function expressions demystified注意:此答案已合并自 another question,其中 OP 的主要疑问和误解是使用 FunctionDeclaration
声明的标识符不能被覆盖情况。
【讨论】:
【参考方案5】:您在此处发布的两个代码 sn-ps 几乎在所有方面都表现相同。
但是,行为的不同在于,对于第一个变体 (var functionOne = function()
),该函数只能在代码中的该点之后调用。
使用第二个变体 (function functionTwo()
),函数可用于在声明函数的位置之上运行的代码。
这是因为在第一个变体中,函数在运行时被分配给变量foo
。第二,函数在解析时被分配给标识符foo
。
更多技术信息
JavaScript 有三种定义函数的方式。
-
您的第一个 sn-p 显示了一个函数表达式。这涉及使用 "function" 运算符 创建一个函数 - 该运算符的结果可以存储在任何变量或对象属性中。这样,函数表达式就很强大了。函数表达式通常被称为“匿名函数”,因为它不必有名字,
您的第二个示例是函数声明。这使用 "function" 语句 来创建一个函数。该函数在解析时可用,并且可以在该范围内的任何位置调用。以后您仍然可以将其存储在变量或对象属性中。
定义函数的第三种方法是“Function()”构造函数,您的原始帖子中没有显示。不建议使用它,因为它与
eval()
的工作方式相同,但存在问题。
【讨论】:
【参考方案6】:对Greg's answer的更好解释
functionTwo();
function functionTwo()
为什么没有错误?我们总是被告知表达式是从上到下执行的(??)
因为:
函数声明和变量声明总是被 JavaScript 解释器以不可见的方式移动 (
hoisted
) 到其包含范围的顶部。显然,函数参数和语言定义的名称已经存在。 ben cherry
这意味着这样的代码:
functionOne(); --------------- var functionOne;
| is actually | functionOne();
var functionOne = function() | interpreted |-->
; | like | functionOne = function()
--------------- ;
请注意,声明的赋值部分没有被提升。只吊起名字。
但是在函数声明的情况下,整个函数体也会被提升:
functionTwo(); --------------- function functionTwo()
| is actually | ;
function functionTwo() | interpreted |-->
| like | functionTwo();
---------------
【讨论】:
【参考方案7】:其他评论者已经涵盖了上述两种变体的语义差异。我想指出一个风格差异:只有“赋值”变体可以设置另一个对象的属性。
我经常用这样的模式构建 JavaScript 模块:
(function()
var exports = ;
function privateUtil()
...
exports.publicUtil = function()
...
;
return exports;
)();
使用这种模式,您的公共函数将全部使用赋值,而您的私有函数使用声明。
(还要注意,赋值语句后需要一个分号,而声明禁止它。)
【讨论】:
【参考方案8】:当您需要避免覆盖函数的先前定义时,说明何时首选第一种方法而不是第二种方法。
有
if (condition)
function myfunction()
// Some code
,myfunction
的这个定义将覆盖之前的任何定义,因为它将在解析时完成。
虽然
if (condition)
var myfunction = function ()
// Some code
只有在满足condition
时才能正确定义myfunction
。
【讨论】:
【参考方案9】:一个重要的原因是添加一个且只有一个变量作为命名空间的“根”...
var MyNamespace =
MyNamespace.foo= function()
或
var MyNamespace =
foo: function()
,
...
命名空间有很多技术。随着大量可用的 JavaScript 模块的出现,这一点变得更加重要。
另见How do I declare a namespace in JavaScript?
【讨论】:
【参考方案10】:Hoisting 是 JavaScript 解释器将所有变量和函数声明移动到当前作用域顶部的操作。
然而,只有实际的声明被提升。将作业留在原处。
在页面内声明的变量/函数是全局的,可以在该页面的任何地方访问。 在函数内声明的变量/函数具有本地范围。表示它们在函数体(范围)内可用/可访问,它们在函数体之外不可用。Variable
Javascript 被称为松散类型语言。这意味着 Javascript 变量可以保存任何 Data-Type 的值。 Javascript 会根据运行时提供的值/文字自动处理更改变量类型。
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 Identifier_opt ( FormalParameterList_opt )
FunctionBody | sequence of statements
« return; Default undefined
« return 'some data';
在页面内声明的函数被提升到具有全局访问权限的页面顶部。
在函数块内声明的函数被提升到块的顶部。
函数的默认返回值是'undefined',Variable声明默认值也是'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
函数表达式
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 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
在不同浏览器上检查函数声明、表达式测试
ES5 Constructor Function Classes:使用 Function.prototype.bind 创建的函数对象
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引入箭头函数:箭头函数表达式语法较短,最适合非方法函数,不能用作构造函数。
ArrowFunction : ArrowParameters => ConciseBody
.const fn = (item) => return item & 1 ? 'Odd' : 'Even'; ; console.log( fn(2) ); // Even console.log( fn(3) ); // Odd
【讨论】:
【参考方案11】:我添加我自己的答案只是因为其他人已经彻底覆盖了吊装部分。
我一直想知道哪种方式更好,感谢http://jsperf.com 现在我知道了:)
函数声明更快,这才是网络开发中真正重要的,对吧? ;)
【讨论】:
见answer about performance below,不同的结果【参考方案12】:???????????????????????????????????????????????????????????????????????????????????????????????????????
函数的可用性(范围)
因为function add()
的作用域是最近的块,所以以下方法有效:
try
console.log("Success: ", add(1, 1));
catch(e)
console.log("ERROR: " + e);
function add(a, b)
return a + b;
以下不起作用,因为在将函数值分配给变量add
之前调用了该变量。
try
console.log("Success: ", add(1, 1));
catch(e)
console.log("ERROR: " + e);
var add=function(a, b)
return a + b;
上面的代码在功能上与下面的代码相同。请注意,显式分配add = undefined
是多余的,因为简单地执行var add;
与var add=undefined
完全相同。
var add = undefined;
try
console.log("Success: ", add(1, 1));
catch(e)
console.log("ERROR: " + e);
add = function(a, b)
return a + b;
以下不起作用,因为var add=
开始一个表达式并导致以下function add()
成为一个表达式而不是一个块。命名函数只对它们自己和它们周围的块可见。由于function add()
在这里是一个表达式,它没有包围块,所以它只对自己可见。
try
console.log("Success: ", add(1, 1));
catch(e)
console.log("ERROR: " + e);
var add=function add(a, b)
return a + b;
-
(函数).name
以这种方式声明的函数function thefuncname()
的名称是thefuncname。
function foobar(a, b)
console.log(foobar.name);
var a = function foobar();
console.log(a.name);
否则,如果函数被声明为function()
,function.name 是用于存储函数的第一个变量。
var a = function();
var b = (function() return function() );
console.log(a.name);
console.log(b.name);
如果没有为函数设置变量,则函数名称为空字符串 (""
)。
console.log((function()).name === "");
最后,虽然分配给函数的变量最初设置名称,但设置给函数的后续变量不会更改名称。
var a = function();
var b = a;
var c = b;
console.log(a.name);
console.log(b.name);
console.log(c.name);
-
性能
在 Google 的 V8 和 Firefox 的 Spidermonkey 中,JIT 编译可能存在几微秒的差异,但最终结果是完全相同的。为了证明这一点,让我们通过比较两个空白代码 sn-ps 的速度来检验 JSPerf 在微基准上的效率。 JSPerf tests are found here。而且,jsben.ch tests are found here。如您所见,当不存在时,存在明显的差异。如果你真的像我一样是性能狂,那么尝试减少作用域中的变量和函数的数量,尤其是消除多态性(例如使用同一个变量存储两种不同的类型)可能更值得。
-
变量的可变性
当您使用var
关键字声明变量时,您可以像这样重新为变量分配不同的值。
(function()
"use strict";
var foobar = function(); // initial value
try
foobar = "Hello World!"; // new value
console.log("[no error]");
catch(error)
console.log("ERROR: " + error.message);
console.log(foobar, window.foobar);
)();
但是,当我们使用 const 语句时,变量引用变得不可变。这意味着我们不能为变量分配新值。但是请注意,这不会使变量的内容不可变:如果您执行const arr = []
,那么您仍然可以执行arr[10] = "example"
。只有执行arr = "new value"
或arr = []
之类的操作才会引发如下所示的错误。
(function()
"use strict";
const foobar = function(); // initial value
try
foobar = "Hello World!"; // new value
console.log("[no error]");
catch(error)
console.log("ERROR: " + error.message);
console.log(foobar, window.foobar);
)();
有趣的是,如果我们将变量声明为function funcName()
,那么变量的不变性与使用var
声明它是一样的。
(function()
"use strict";
function foobar(); // initial value
try
foobar = "Hello World!"; // new value
console.log("[no error]");
catch(error)
console.log("ERROR: " + error.message);
console.log(foobar, window.foobar);
)();
???? ?? ??? "??????? ?????"
“最近块”是最近的“函数”(包括异步函数、生成器函数和异步生成器函数)。然而,有趣的是,function functionName()
的行为类似于var functionName = function()
在非封闭块中对所述封闭之外的项目。观察。
var add=function()
try
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined")
add(1, 1); // just to prove it
console.log("Not a block");
else if(add===undefined) // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b)return a+b');
catch(e)
console.log("Is a block");
var add=function(a, b)return a + b
正常function add()
try
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined")
add(1, 1); // just to prove it
console.log("Not a block");
else if(add===undefined) // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b)return a+b')
catch(e)
console.log("Is a block");
function add(a, b)
return a + b;
功能
try
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined")
add(1, 1); // just to prove it
console.log("Not a block");
else if(add===undefined) // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b)return a+b')
catch(e)
console.log("Is a block");
(function ()
function add(a, b)
return a + b;
)();
声明(如if
、else
、for
、while
、try
/catch
/finally
、switch
、if
、if
/@9876 )
try
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined")
add(1, 1); // just to prove it
console.log("Not a block");
else if(add===undefined) // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b)return a+b')
catch(e)
console.log("Is a block");
function add(a, b)
return a + b;
var add=function()
的箭头函数
try
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined")
add(1, 1); // just to prove it
console.log("Not a block");
else if(add===undefined) // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b)return a+b')
catch(e)
console.log("Is a block");
(() =>
var add=function(a, b)
return a + b;
)();
带有function add()
的箭头函数
try
// typeof will simply return "undefined" if the variable does not exist
if (typeof add !== "undefined")
add(1, 1); // just to prove it
console.log("Not a block");
else if(add===undefined) // this throws an exception if add doesn't exist
console.log('Behaves like var add=function(a,b)return a+b')
catch(e)
console.log("Is a block");
(() =>
function add(a, b)
return a + b;
)();
【讨论】:
【参考方案13】:一旦建立绑定,分配给变量的函数声明和函数表达式的行为相同。
然而,在如何和何时函数对象实际上与它的变量相关联是有区别的。这种差异是由于 JavaScript 中称为变量提升的机制造成的。
基本上,所有的函数声明和变量声明都被提升到声明发生的函数的顶部(这就是我们说JavaScript有函数范围的原因) .
当函数声明被提升时,函数体“跟随” 所以当函数体被评估时,变量将立即 绑定到函数对象。
当变量声明被提升时,初始化不会不
跟随,但被“抛在后面”。变量初始化为
undefined
在函数体的开头,将被赋值
代码中原始位置的值。 (实际上,它会在 每个 发生同名变量声明的位置分配一个值。)
提升的顺序也很重要:函数声明优先于同名的变量声明,最后一个函数声明优先于之前的同名函数声明。
一些例子...
var foo = 1;
function bar()
if (!foo)
var foo = 10
return foo;
bar() // 10
变量foo
被提升到函数顶部,初始化为undefined
,这样!foo
就是true
,所以foo
被赋值为10
。在bar
的范围之外的foo
没有任何作用,也没有受到影响。
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 f()
var a = 4;
function a() return 1;
function a() return 2;
return a;
f() // 4
在此示例中,a
使用评估第二个函数声明产生的函数对象进行初始化,然后分配 4
。
var a = 1;
function b()
a = 10;
return;
function a()
b();
a // 1
这里首先提升函数声明,声明并初始化变量a
。接下来,为这个变量分配10
。换句话说:赋值不会分配给外部变量a
。
【讨论】:
【参考方案14】:第一个例子是函数声明:
function abc()
第二个例子是函数表达式:
var abc = function() ;
主要区别在于它们的吊装方式(吊装和申报)。在第一个示例中,提升了整个函数声明。在第二个例子中,只有 var 'abc' 被提升,它的值(函数)将是未定义的,并且函数本身保持在它被声明的位置。
简单地说:
//this will work
abc(param);
function abc()
//this would fail
abc(param);
var abc = function()
要了解有关此主题的更多信息,我强烈建议您这样做 link
【讨论】:
【参考方案15】:在代码维护成本方面,命名函数更可取:
独立于它们的声明位置(但仍受范围限制)。 更能抵抗条件初始化等错误(如果需要,您仍然可以覆盖)。 通过将本地函数与作用域功能分开分配,代码变得更具可读性。通常在范围内,功能首先出现,然后是局部函数的声明。 在调试器中,您会清楚地看到调用堆栈上的函数名称,而不是“匿名/评估”函数。我怀疑命名函数的更多优点如下。命名函数的优点是匿名函数的缺点。
从历史上看,匿名函数的出现是由于 JavaScript 作为一种语言无法列出具有命名函数的成员:
member:function() /* How do I make "this.member" a named function? */
【讨论】:
【参考方案16】:在计算机科学术语中,我们谈论匿名函数和命名函数。我认为最重要的区别是匿名函数不绑定名称,因此名称为匿名函数。在 JavaScript 中,它是在运行时动态声明的第一类对象。
有关匿名函数和 lambda 演算的更多信息,***是一个好的开始:Anonymous Functions。
【讨论】:
【参考方案17】:Greg's Answer 已经足够好了,但我还是想补充一些我刚刚在观看Douglas Crockford's 视频时学到的东西。
函数表达式:
var foo = function foo() ;
功能说明:
function foo() ;
函数语句只是带有function
值的var
语句的简写。
所以
function foo() ;
扩展到
var foo = function foo() ;
进一步扩展为:
var foo = undefined;
foo = function foo() ;
它们都被提升到代码的顶部。
【讨论】:
【参考方案18】:我在我的代码中使用变量方法是出于一个非常具体的原因,上面已经以抽象的方式介绍了该方法的理论,但是一个示例可能会对像我这样的 JavaScript 专业知识有限的人有所帮助。
我的代码需要运行 160 个独立设计的品牌。大部分代码都在共享文件中,但品牌特定的东西在一个单独的文件中,每个品牌都有一个。
有些品牌需要特定的功能,有些则不需要。有时我必须添加新功能来做新的品牌特定的事情。我很高兴更改共享编码,但我不想更改所有 160 组品牌文件。
通过使用变量语法,我可以在共享代码中声明变量(本质上是一个函数指针),然后分配一个普通的存根函数,或者设置为 null。
然后,需要特定函数实现的一两个品牌可以定义他们的函数版本,并在需要时将其分配给变量,其余的则什么都不做。我可以在共享代码中执行空函数之前对其进行测试。
从上面人们的 cmets 中,我认为重新定义静态函数也可能是可能的,但我认为变量解决方案很好且清晰。
【讨论】:
【参考方案19】:@EugeneLazutkin 给出了一个例子,他将names an assigned function to be able to use shortcut()
作为对自身的内部引用。 John Resig 在他的 Learning Advanced Javascript 教程中给出了另一个示例 - 复制分配给另一个对象的递归函数。虽然在这里将函数分配给属性并不是严格意义上的问题,但我建议积极尝试本教程 - 通过单击右上角的按钮运行代码,然后双击代码以根据自己的喜好进行编辑。
教程中的示例:yell()
中的递归调用:
Tests fail when the original ninja object is removed.(第 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.(第 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." );
【讨论】:
【参考方案20】:其他答案中没有提到的另一个区别是,如果您使用匿名函数
var functionOne = function()
// Some code
;
并将其用作构造函数
var one = new functionOne();
那么one.constructor.name
将不会被定义。 Function.name
是非标准的,但受 Firefox、Chrome、其他 Webkit 衍生浏览器和 IE 9+ 支持。
与
function functionTwo()
// Some code
two = new functionTwo();
可以使用two.constructor.name
将构造函数的名称作为字符串检索。
【讨论】:
【参考方案21】:我在下面列出了不同之处:
函数声明可以放在代码中的任何位置。即使在定义出现在代码中之前调用它,它也会在函数声明被提交到内存或以某种方式被提升时执行,然后页面中的任何其他代码开始执行。
看看下面的函数:
function outerFunction()
function foo()
return 1;
return foo();
function foo()
return 2;
alert(outerFunction()); // Displays 2
这是因为,在执行过程中,它看起来像:-
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
一个函数表达式,如果在调用它之前没有定义,将会导致错误。此外,这里的函数定义本身并没有像函数声明中那样移动到顶部或提交到内存中。但是我们分配函数的变量被提升并且 undefined 被分配给它。
使用函数表达式的相同函数:
function outerFunction()
var foo = function()
return 1;
return foo();
var foo = function()
return 2;
alert(outerFunction()); // Displays 1
这是因为在执行过程中,它看起来像:
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
在像 if 这样的非函数块中编写函数声明是不安全的,因为它们不可访问。
if (test)
function x() doSomething();
如下所示的命名函数表达式,可能无法在版本 9 之前的 Internet Explorer 浏览器中使用。
var today = function today() return new Date()
【讨论】:
【参考方案22】:第一个(函数 doSomething(x))应该是对象符号的一部分。
第二个(var doSomething = function(x) alert(x);
)只是创建一个匿名函数并将其分配给一个变量doSomething
。所以 doSomething() 会调用这个函数。
您可能想知道函数声明和函数表达式是什么。
函数声明定义了一个命名函数变量,而不需要变量赋值。函数声明作为独立构造出现,不能嵌套在非函数块中。
function foo()
return 3;
ECMA 5 (13.0) 将语法定义为 函数标识符(FormalParameterListopt) FunctionBody
在上述情况下,函数名在其范围内和其父级范围内可见(否则将无法访问)。
在函数表达式中
函数表达式将函数定义为更大的表达式语法(通常是变量赋值)的一部分。通过函数表达式定义的函数可以命名或匿名。函数表达式不应以“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) 将语法定义为 函数标识符opt (FormalParameterListopt) FunctionBody
【讨论】:
【参考方案23】:如果你使用这些函数来创建对象,你会得到:
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
【讨论】:
【参考方案24】:关于性能:
V8
的新版本引入了一些底层优化,SpiderMonkey
也是如此。
现在表达式和声明之间几乎没有区别。 函数表达式appears to be faster now。
Chrome 62.0.3202
FireFox 55
Chrome Canary 63.0.3225
Anonymous
函数表达式appear to have better performance 反对Named
函数表达式。
火狐 Chrome 金丝雀 Chrome
【讨论】:
结果差异太小,不能视为差异。如果您将运行测试 100 次,您将获得 100 个结果。 @RonnySherer,你熟悉 jsperf 吗?运行超过 1000 万次后进行测试! 每次测量都有干扰。计算机它不在同一状态,这不是计算机上运行的唯一进程。当差异如此之小,这意味着你不能依赖它,它几乎是一样的。尝试一个接一个地运行 sane 测试 10 次,您会发现数字不同。非常接近,但不一样。 @RonnySherer js perf 创建了一个虚拟环境,特别是为了解决具有这些微小差异的进程。它没有在我的电脑上运行。它只运行那个。当事情如此之小,也许有人不应该在乎。但我从来没有正确地计算过它并报告它。如果有人想在具有数十亿次迭代的循环中使用它,那么他应该选择性能最佳的函数。 虚拟环境位于服务器上,它可能会做一些其他事情。我做了一些测试。结果永远不会完全相同。【参考方案25】:鉴于“命名函数显示在堆栈跟踪中”的说法,现代 JavaScript 引擎实际上非常有能力表示匿名函数。
在撰写本文时,V8、SpiderMonkey、Chakra 和 Nitro 始终通过名称来引用命名函数。他们几乎总是通过它的标识符来引用一个匿名函数(如果它有一个)。
SpiderMonkey 可以找出从另一个函数返回的匿名函数的名称。其他的不行。
如果您真的非常希望您的迭代器和成功回调显示在跟踪中,您也可以命名它们...
[].forEach(function iterator() );
但在大多数情况下,这不值得强调。
线束 (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 e.i (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
蜘蛛猴
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:
e.i@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 e.i (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
【讨论】:
【参考方案26】:两者都是定义函数的不同方式。不同之处在于浏览器如何解释它们并将它们加载到执行上下文中。
第一种情况是函数表达式,仅在解释器到达该行代码时才加载。所以如果你像下面这样做,你会得到functionOne不是函数的错误。
functionOne();
var functionOne = function()
// Some code
;
原因是第一行没有给functionOne赋值,因此它是未定义的。我们试图将它作为一个函数来调用,因此我们得到了一个错误。
在第二行,我们将匿名函数的引用分配给 functionOne。
第二种情况是在执行任何代码之前加载的函数声明。因此,如果您执行以下操作,则在代码执行之前加载声明时不会出现任何错误。
functionOne();
function functionOne()
// Some code
【讨论】:
【参考方案27】:在 JavaScript 中有两种创建函数的方法:
函数声明:
function fn()
console.log("Hello");
fn();
这是非常基本的,不言自明的,用于许多语言和跨 C 系列语言的标准。我们声明了一个函数来定义它并通过调用它来执行它。
您应该知道的是,函数实际上是 JavaScript 中的对象;在内部,我们为上述函数创建了一个对象,并给它一个名为 fn 的名称,或者对该对象的引用存储在 fn 中。函数是 JavaScript 中的对象;函数的实例实际上是一个对象实例。
函数表达式:
var fn=function()
console.log("Hello");
fn();
JavaScript 具有一流的函数,即创建一个函数并将其分配给一个变量,就像您创建一个字符串或数字并将其分配给一个变量一样。这里, fn 变量被分配给一个函数。这个概念的原因是函数是 JavaScript 中的对象; fn 指向上述函数的对象实例。我们已经初始化了一个函数并将其分配给一个变量。它没有执行函数并分配结果。
参考:JavaScript function declaration syntax: var fn = function() vs function fn()
【讨论】:
【参考方案28】:它们非常相似,但有一些小的区别,第一个是分配给匿名函数的变量(函数声明),第二个是在 JavaScript 中创建函数的常规方法(匿名函数声明),两者都有用法,优缺点:
1.函数表达式
var functionOne = function()
// Some code
;
函数表达式将函数定义为更大的函数的一部分 表达式语法(通常是变量赋值)。职能 通过函数定义表达式可以命名或匿名。功能 表达式不能以“function”开头(因此括号 围绕下面的自调用示例)。
给函数赋值,意思是不能Hoisting,因为我们知道JavaScript中的函数可以Hoist,意思是可以在声明之前调用它们,而变量需要在访问它们之前声明,所以在这种情况下意味着我们无法在声明函数之前访问该函数,也可能是您编写函数的一种方式,对于返回另一个函数的函数,这种声明可能有意义,同样在 ECMA6 及更高版本中,您可以将其分配给箭头函数,可用于调用匿名函数,这种声明方式也是在 JavaScript 中创建构造函数的更好方法。
2。函数声明
function functionTwo()
// Some code
函数声明定义了一个命名函数变量,没有 需要变量赋值。函数声明发生为 独立构造,不能嵌套在非功能块中。 将它们视为变量声明的兄弟姐妹会很有帮助。 正如变量声明必须以“var”开头一样,函数 声明必须以“function”开头。
这是在 JavaScript 中调用函数的正常方式,甚至可以在你声明它之前调用这个函数,因为在 JavaScript 中所有函数都会被提升,但如果你有 'use strict' 这不会像预期的那样提升,它是调用所有行数不大的普通函数的好方法,也不是构造函数。
另外,如果您需要更多关于提升在 JavaScript 中的工作原理的信息,请访问以下链接:
https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
【讨论】:
【参考方案29】:这只是两种可能的函数声明方式,第二种方式,你可以在声明之前使用函数。
【讨论】:
【参考方案30】:new Function()
可用于在字符串中传递函数的主体。因此这可以用来创建动态函数。也在不执行脚本的情况下传递脚本。
var func = new Function("x", "y", "return x*y;");
function secondFunction()
var result;
result = func(10,20);
console.log ( result );
secondFunction()
【讨论】:
虽然这是好的和真实的,但仅此一项与被问到的问题到底有什么关系?以上是关于var functionName = function() vs function functionName()的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript中的var functionName = function() vs function functionName()
js中 var functionName = function() {} 和 function functionName() {} 两种函数声明的区别