我最近开始维护其他人的JavaScript代码。我正在修复错误,添加功能,并试图整理代码并使其更加一致。
以前的开发人员使用两种声明函数的方法,如果背后有原因,我就无法解决。
这两种方式是:
var functionOne = function() {
// Some code
};
function functionTwo() {
// Some code
}
使用这两种不同方法的原因是什么?每种方法的优缺点是什么?有没有什么可以通过一种方法完成,而另一种方法无法完成?
答案 0 :(得分:4786)
不同之处在于functionOne
是函数表达式,因此仅在到达该行时定义,而functionTwo
是函数声明,并且在其执行周围函数或脚本时定义(由于hoisting)。
例如,函数表达式:
// TypeError: functionOne is not a function
functionOne();
var functionOne = function() {
console.log("Hello!");
};
并且,函数声明:
// Outputs: "Hello!"
functionTwo();
function functionTwo() {
console.log("Hello!");
}
这也意味着你不能使用函数声明来有条件地定义函数:
if (test) {
// Error or misbehavior
function functionThree() { doSomething(); }
}
以上实际上定义functionThree
而不考虑test
的值 - 除非use strict
生效,在这种情况下它只会引发错误。
答案 1 :(得分:1874)
首先,我想纠正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()
的情况下 - 答案是:它取决于浏览器。
答案 2 :(得分:138)
在谈到全局上下文时,最后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
语句,如果在作用域链中找不到引用的标识符,它将成为全局对象的可删除属性。
此外,未声明的作业会在Strict Mode下的ECMAScript 5上投放ReferenceError
。
注意:此答案已从another question合并,其中来自OP的主要疑问和误解是用FunctionDeclaration
声明的标识符,不能被覆盖的情况并非如此。
答案 3 :(得分:117)
您发布的两个代码段几乎可以用于所有目的。
但是,行为的差异在于第一个变体(var functionOne = function() {}
),该函数只能在代码中的该点之后调用。
使用第二个变体(function functionTwo()
),该函数可用于在声明函数的上方运行的代码。
这是因为对于第一个变体,该函数在运行时被赋值给变量foo
。在第二个中,该函数在分析时被分配给该标识符foo
。
更多技术信息
JavaScript有三种定义函数的方法。
eval()
相同,后者存在问题。答案 4 :(得分:98)
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();
---------------
答案 5 :(得分:88)
其他评论者已经涵盖了上述两种变体的语义差异。我想要注意一个风格差异:只有“赋值”变体可以设置另一个对象的属性。
我经常用这样的模式构建JavaScript模块:
(function(){
var exports = {};
function privateUtil() {
...
}
exports.publicUtil = function() {
...
};
return exports;
})();
使用此模式,您的公共函数将全部使用赋值,而您的私有函数使用声明。
(另请注意,在语句之后,赋值应该使用分号,而声明则禁止使用分号。)
答案 6 :(得分:75)
何时首选方法到第二种方法的说明是当你需要避免覆盖函数的先前定义时。
使用
if (condition){
function myfunction(){
// Some code
}
}
,myfunction
的这个定义将覆盖任何先前的定义,因为它将在分析时完成。
虽然
if (condition){
var myfunction = function (){
// Some code
}
}
只有在满足myfunction
时才能正确定义condition
。
答案 7 :(得分:60)
一个重要的原因是只添加一个变量作为命名空间的“根”......
var MyNamespace = {}
MyNamespace.foo= function() {
}
或
var MyNamespace = {
foo: function() {
},
...
}
命名空间有很多技巧。随着大量JavaScript模块的出现,它变得越来越重要。
答案 8 :(得分:53)
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';
}
功能的默认返回值是&#39; undefined&#39;,Variable声明默认值也是&#39; undefined&#39;
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
答案 9 :(得分:37)
答案 10 :(得分:32)
一旦建立了绑定,分配给变量的函数声明和函数表达式就会表现相同。
但是 和 时,函数对象实际上与其变量相关联。这种差异是由于JavaScript中称为变量提升的机制。
基本上,所有函数声明和变量声明都被提升到声明发生的 function 的顶部(这就是我们说JavaScript具有函数作用域的原因)
当函数声明被提升时,函数体“跟随” 所以当评估函数体时,变量将立即生效 被绑定到一个函数对象。
当提升变量声明时,初始化不
跟随,但“落后”。变量初始化为
undefined
在函数体的开头,将被分配
代码中原始位置的值。 (实际上,它将在每个位置分配一个值,其中发生具有相同名称的变量的声明。)
提升顺序也很重要:函数声明优先于具有相同名称的变量声明,最后一个函数声明优先于之前具有相同名称的函数声明。
一些例子......
var foo = 1;
function bar() {
if (!foo) {
var foo = 10 }
return foo; }
bar() // 10
变量foo
被提升到函数的顶部,初始化为undefined
,因此!foo
为true
,因此foo
已分配{{} 1}}。 10
范围之外的foo
不起作用,也不受影响。
bar
函数声明优先于变量声明,最后一个函数声明“坚持”。
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
。
答案 11 :(得分:31)
第一个例子是函数声明:
function abc(){}
第二个例子是函数表达式:
var abc = function() {};
主要区别在于如何悬挂(举起和宣布)。在第一个示例中,整个函数声明被提升。在第二个例子中,只有var&#39; abc&#39;被提升,其值(函数)将是未定义的,函数本身仍保持在声明它的位置。
简单地说:
//this will work
abc(param);
function abc(){}
//this would fail
abc(param);
var abc = function() {}
要了解有关此主题的更多信息,我强烈建议您这样做 link
答案 12 :(得分:28)
就代码维护成本而言,更有利于命名函数:
我怀疑命名函数的PROS会更多。被列为命名函数优势的是匿名函数的缺点。
从历史上看,匿名函数出现在JavaScript无法作为列出具有命名函数的成员的语言中出现:
{
member:function() { /* How do I make "this.member" a named function? */
}
}
答案 13 :(得分:24)
我在代码中使用变量方法是出于一个非常具体的原因,其理论已在上面以一种抽象的方式介绍,但是一个例子可能会帮助像我这样的人,JavaScript专业知识有限。
我需要运行160个独立设计的品牌代码。大多数代码都在共享文件中,但品牌特定的东西在一个单独的文件中,每个文件都有一个。
某些品牌需要特定功能,有些则不需要。有时我必须添加新功能来执行新的品牌特定事物。我很乐意更改共享编码,但我不想更改所有160套品牌文件。
通过使用变量语法,我可以在共享代码中声明变量(本质上是一个函数指针),并分配一个普通的存根函数,或者设置为null。
需要特定功能实现的一个或两个品牌可以定义它们的函数版本,并在需要时将其分配给变量,其余的则不做任何事情。我可以在共享代码中执行它之前测试null函数。
根据上面人们的评论,我认为也可以重新定义静态函数,但我认为变量解决方案很好而且清晰。
答案 14 :(得分:22)
Greg's Answer已经足够好了,但我仍然希望在刚才看到的Douglas Crockford's视频中添加一些内容。
功能表达:
var foo = function foo() {};
功能声明:
function foo() {};
函数语句只是var
语句的简写,其值为function
。
所以
function foo() {};
扩展为
var foo = function foo() {};
进一步扩展到:
var foo = undefined;
foo = function foo() {};
他们都被提升到代码顶部。
答案 15 :(得分:22)
在计算机科学术语中,我们讨论匿名函数和命名函数。我认为最重要的区别是匿名函数没有绑定到名称,因此名称是匿名函数。在JavaScript中,它是在运行时动态声明的第一个类对象。
有关匿名函数和lambda演算的更多信息,维基百科是一个良好的开端(http://en.wikipedia.org/wiki/Anonymous_function)。
答案 16 :(得分:18)
@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." );
答案 17 :(得分:16)
其他答案中未提及的另一个区别是,如果您使用匿名函数
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
检索构造函数的名称。
答案 18 :(得分:14)
第一个(函数doSomething(x))应该是对象表示法的一部分。
第二个(var doSomething = function(x){ alert(x);}
)只是创建一个匿名函数并将其分配给变量doSomething
。所以doSomething()将调用该函数。
您可能想知道函数声明和函数表达式是什么。
函数声明定义了一个命名函数变量,而不需要变量赋值。函数声明作为独立构造出现,不能嵌套在非函数块中。
function foo() {
return 3;
}
ECMA 5(13.0)将语法定义为
函数标识符(FormalParameterList opt ){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 (FormalParameterList opt ){FunctionBody}
答案 19 :(得分:14)
我列出了以下差异:
函数声明可以放在代码中的任何位置。即使在定义出现在代码之前调用它,它也会在函数声明被提交到内存或以某种方式被提升之前执行,之后页面中的任何其他代码开始执行。
看看下面的功能:
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
如果函数表达式在调用之前未定义,则会导致错误。此外,这里函数定义本身不会像函数声明一样移动到顶部或提交到内存。但是我们分配函数的变量会被提升并且未定义被赋值给它。
使用函数表达式的相同函数:
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()}
答案 20 :(得分:14)
如果您要使用这些功能来创建对象,您将获得:
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
答案 21 :(得分:11)
鉴于&#34;命名函数出现在堆栈跟踪中&#34;参数,现代JavaScript引擎实际上非常能够表示匿名函数。
在撰写本文时,V8,SpiderMonkey,Chakra和Nitro总是通过他们的名字来引用命名函数。如果它有一个匿名函数,它们几乎总是通过匿名函数引用它。
SpiderMonkey可以找出从另一个函数返回的匿名函数的名称。其余的不能。
如果你真的,真的希望你的迭代器和成功回调显示在跟踪中,你也可以为它们命名......
[].forEach(function iterator() {});
但在大多数情况下,它不值得强调。
'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'));
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
答案 22 :(得分:11)
下面列出的两种不同功能声明之间有四个值得注意的比较。
以下是有效的,因为function add()
的范围限定为最近的块:
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
function add(a, b){
return a + b;
}
&#13;
以下内容不起作用(因为var 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;
}
&#13;
以下内容不起作用,因为add
在使用后声明。
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
var add=function(a, b){
return a + b;
}
&#13;
当以这种方式声明时,函数function thefuncname(){}
的名称是 thefuncname 。
function foobar(a, b){}
console.log(foobar.name);
&#13;
var a = function foobar(){};
console.log(a.name);
&#13;
否则,如果函数声明为function(){}
,则函数 .name是用于存储函数的第一个变量。
var a = function(){};
var b = (function(){ return function(){} });
console.log(a.name);
console.log(b.name);
&#13;
如果没有为函数设置变量,则函数名称为空字符串(""
)。
console.log((function(){}).name === "");
&#13;
最后,虽然分配函数的变量最初设置名称,但设置为函数的连续变量不会更改名称。
var a = function(){};
var b = a;
var c = b;
console.log(a.name);
console.log(b.name);
console.log(c.name);
&#13;
在Google的V8和Firefox的Spidermonkey中,可能存在几微秒的JIST编译差异,但最终结果完全相同。为了证明这一点,让我们通过比较两个空白代码片段的速度来检查JSPerf在微基准测试中的效率。 JSPerf tests are found here。jsben.ch testsare found here。而且,{{3}}。正如你所看到的,当应该没有时,会有明显的区别。如果你真的是像我这样的表演狂,那么在尝试减少范围中的变量和函数的数量时尤其是消除多态性(例如使用相同的变量来存储两种不同的类型)可能更值得。
当您使用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);
})();
&#13;
但是,当我们使用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);
})();
&#13;
有趣的是,如果我们将变量声明为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);
})();
&#13;
&#34;最近的区块&#34;是最近的&#34;功能,&#34; (包括异步函数,生成器函数和异步生成器函数)。然而,有趣的是,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}
&#13;
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;
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;
}
})();
&#13;
if
,else
,for
,while
,try
/ catch
/ finally
,{ {1}},switch
/ do
,while
)
with
&#13;
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()
&#13;
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()
&#13;
答案 23 :(得分:10)
在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() {}
答案 24 :(得分:9)
关于效果:
V8
的新版本引入了几个引擎优化,SpiderMonkey
也是如此。
表达式和声明之间几乎没有区别。
现在函数表达式appears to be faster。
Anonymous
函数表达式appear to have better performance 反对Named
函数表达式。
答案 25 :(得分:9)
两者都是定义函数的不同方式。不同之处在于浏览器如何解释并将它们加载到执行上下文中。
第一种情况是函数表达式,只有当解释器到达该行代码时才会加载。因此,如果您按照以下方式执行此操作,则会出现 functionOne不是函数的错误。
functionOne();
var functionOne = function() {
// Some code
};
原因是在第一行没有为functionOne赋值,因此未定义。我们试图将其称为函数,因此我们遇到了错误。
在第二行,我们将一个匿名函数的引用分配给functionOne。
第二种情况是在执行任何代码之前加载的函数声明。因此,如果您喜欢以下内容,则在代码执行之前加载声明时不会出现任何错误。
functionOne();
function functionOne() {
// Some code
}
答案 26 :(得分:8)
它们非常相似,只有一些小差异,第一个是分配给匿名函数的变量(函数声明),第二个是在JavaScript中创建函数的常规方法(匿名函数声明),两者都有用法,缺点和优点:
<强> 1。功能表达
var functionOne = function() {
// Some code
};
函数表达式将函数定义为较大的函数的一部分 表达式语法(通常是变量赋值)。功能 通过函数定义表达式可以命名或匿名。功能 表达式不能以“function”开头(因此括号 围绕下面的自我调用示例。)
为函数赋值变量,意味着没有Hoisting,因为我们知道JavaScript中的函数可以提升,意味着它们可以在声明之前被调用,而变量需要在访问它们之前声明,所以在这种情况下意味着我们无法在它声明之前访问该函数,也可能是你编写函数的一种方式,对于返回另一个函数的函数,这种声明也是有意义的,同样在ECMA6&amp;上面你可以将它分配给一个可用于调用匿名函数的箭头函数,这种声明方式也是在JavaScript中创建构造函数的更好方法。
<强> 2。功能声明
function functionTwo() {
// Some code
}
函数声明定义了一个没有的命名函数变量 需要变量赋值。函数声明以 独立构造,不能嵌套在非功能块中。 将它们视为变量声明的兄弟是有帮助的。 就像变量声明必须以“var”,Function开头一样 声明必须以“function”开头。
这是在JavaScript中调用函数的常规方法,在你甚至声明它之前可以调用这个函数,因为在JavaScript中所有函数都被提升,但是如果你有'使用严格的&#39;这不会像预期的那样赢得翻转,这是一个很好的方法来调用所有正常的函数,这些函数的行数不大,也不是构造函数。
此外,如果您需要有关如何在JavaScript中提升的更多信息,请访问以下链接:
答案 27 :(得分:6)
这只是声明函数的两种可能方式,在第二种方式中,您可以在声明之前使用该函数。
答案 28 :(得分:5)
new Function()
可用于在字符串中传递函数体。因此,这可用于创建动态功能。同时传递脚本而不执行脚本。
var func = new Function("x", "y", "return x*y;");
function secondFunction(){
var result;
result = func(10,20);
console.log ( result );
}
secondFunction()
答案 29 :(得分:2)
这称为函数表达式:
var w = 5;
var h = 6;
function RectArea(width, height) { //declaring the function
return area = width * height;
} //note you do not need ; after }
RectArea(w,h); //calling or executing the function
console.log("Area of Rectangle: " + area);
// This should return the following result in the console:
// Area of Rectangle: 30
这称为函数声明:
core-site.xml
希望这有助于解释函数表达式和函数声明之间的区别以及如何使用它们。谢谢。
答案 30 :(得分:2)
第一个是匿名函数表达式,而第二个是函数声明。匿名函数根本没有名称。匿名函数表达式和函数语句之间的主要区别是函数名称。
匿名函数易于键入,并且许多库和工具倾向于鼓励这种惯用的代码风格。但是,匿名函数有一些缺点:
可读性::匿名函数忽略了一个可能会导致代码可读性较低的名称。
调试::匿名函数在堆栈跟踪中没有有用的名称,这会使调试更加困难。
自引用:如果函数需要引用自身,例如递归,该怎么办?
命名函数表达式:
为函数表达式提供一个名称可以非常有效地解决所有这些缺点,并且没有明显的缺点。最佳做法是始终为函数表达式命名:
setTimeout(function timeHandler() { // <-- look, a name here!
console.log("I've waited 1 second");
}, 1000);
命名IIFE(立即调用函数表达式):
(function IIFE(str) { // <-- look, always name IIFEs!
console.log(str); // "Hello!"
})('Hello!');
对于分配给变量的函数,这种情况下命名函数不是很常见,并且可能引起混淆,在这种情况下,箭头函数可能是更好的选择。
答案 31 :(得分:1)
我更喜欢将函数定义为变量:
let first = function(x){
return x[0];
}
而不是:
function first(){
....
}
因为我可以在定义函数时使用表达式和装饰器。例如:
let safe = function(f){
try {f()...}
}
let last = safe(function(x){return x[0]}).
ES6还要短得多:
let last = x => x[0]
...........
function last(x){
return x[0];
}
......
let last = safe(x => x[0]);
答案 32 :(得分:1)
JS中的表达:返回值的东西
示例:在chrome控制台中尝试以下操作:
a = 10
output : 10
(1 + 3)
output = 4
声明/声明:不返回值的东西
例如:
if (1 > 2) {
// do something.
}
这里(1> 2)是一个表达式,但是&#39; if&#39;不是。它没有返回任何东西。
同样,我们有功能声明/声明与功能表达
让我们举一个例子:
// test.js
var a = 10;
// function expression
var fun_expression = function() {
console.log("Running function Expression");
}
// funciton expression
function fun_declaration() {
console.log("Running function Statement");
}
重要:强> 当JavaScript引擎运行上述js文件时会发生什么。
当这个js运行时会发生以下情况:
现在假设我们将js更新为。
// test.js
console.log(a) //output: udefined (No error)
console.log(fun_expression) // output: undefined (No error)
console.log(fun_expression()) // output: Error. As we trying to invoke undefined.
console.log(fun_declaration()) // output: running function statement (As fun_declaration is already hoisted in the memory).
var a = 10;
// function expression
var fun_expression = function() {
console.log('Running function expression')
}
// function declaration
function fun_declaration() {
console.log('running function declaration')
}
console.log(a) // output: 10
console.log(fun_expression()) //output: Running function expression
console.log(fun_declaration()) //output: running function declaration
注释中上面提到的输出应该有助于理解函数表达式和函数声明/声明之间的不同。
答案 33 :(得分:1)
需要注意的重要一点是:-
让我们有两个功能:-
--prelude=empty
在上述情况下,将给出未定义总和的错误,但
sum(1,2);
const sum = function(first, second) {
return first + second;
}
此功能不会出现任何错误,因为在这种情况下将发生Hoisting。
答案 34 :(得分:0)
两个函数之间的另一个区别是functionOne可以用作可以在其中包含多个函数的变量,而functionTwo可以保留一些代码块,这些代码块在被调用时全部执行。请检查以下内容:
CASE WHEN rn >= 4 THEN -rn END
您可以选择要调用的函数。例如functionOne.sayHello或functionOne。 redirectPage。如果调用functionTwo,则将执行整个代码块。
答案 35 :(得分:0)
JavaScript具有一流的功能。这意味着可以像对待其他变量一样对待它们。可以将函数作为参数传递给函数,从函数返回,并可以存储在变量中。
但是,将函数存储在变量(函数表达式)中并不是创建函数的唯一方法,这也可以通过函数声明完成。以下是主要区别:
undefined
的值。这里是一个例子:
try {
functionOne();
} catch (e) {
console.log('i cant run because im not hoisted');
}
functionTwo();
// function expression, does not get hoisted
let functionOne = function randomName() {
// Some code
};
// function declaration, gets hoisted
function functionTwo() {
console.log('I get hoisted');
}
try {
randomName(); // this isn't the proper name, it is functionOne
} catch (e) {
console.log('You cant call me with randomName my name is function one');
}
:
答案 36 :(得分:0)
var functionOne = function() {}
在运行时定义,而function functionTwo() {}
在解析时定义。
// Run-Time function declaration
functionOne(); // Calling functionOne function here will give an Error
var functionOne = function () {
// Some code
};
// Parse-Time function declaration
functionTwo(); // Calling functionTwo function will not give an Error
function functionTwo() {
// Some code...
}
运行时与解析时之间的解释 javascript run-time vs parse-time
答案 37 :(得分:0)
在调试器/DevTools 中的断点期间,如果您在控制台中使用格式 function functionName() {}
,则随后不能在控制台中使用 functionName()
(它表示“未定义”),而在 var functionName = function() {}
之后,您可以使用该函数。
答案 38 :(得分:-1)
您不能在函数声明中使用.bind()
方法,但可以在函数表达式中使用。
函数声明:
function x() {
console.log(this)
}.bind('string')
x()
函数表达式:
var x = function() {
console.log(this)
}.bind('string')
x()
答案 39 :(得分:-2)
函数声明和表达式之间的提升行为很重要,但是两者之间还有另一个区别:
MDN建议的一般做法是使用函数表达式代替if
语句中的声明。在链接的示例中,if
语句内的函数声明在Chrome和Firefox上的行为相同,但在Safari上的行为不是。
免责声明:macOS无法运行Microsoft Edge,因此我无法验证。
// function declaration example
var hoisted = "foo" in this;
console.log(`'foo' name ${hoisted ? "is" : "is not"} hoisted. typeof foo is ${typeof foo}`);
if (false) {
function foo() {
return 1;
}
}
// In Chrome:
// 'foo' name is hoisted. typeof foo is undefined
// In Firefox:
// 'foo' name is hoisted. typeof foo is undefined
// In Safari:
// 'foo' name is hoisted. typeof foo is function
将foo
转换为函数表达式将在所有三个Web浏览器中产生一致的结果。
// function expression example
var hoisted = "foo" in this;
console.log(`'foo' name ${hoisted ? "is" : "is not"} hoisted. typeof foo is ${typeof foo}`);
if (false) {
var foo = function() {
return 1;
}
}
// In Chrome:
// 'foo' name is hoisted. typeof foo is undefined
// In Firefox:
// 'foo' name is hoisted. typeof foo is undefined
// In Safari:
// 'foo' name is hoisted. typeof foo is undefined