提问人:Richard Garside 提问时间:12/3/2008 最后编辑:Richard Garside 更新时间:9/4/2023 访问量:1210300
var functionName = function() {} 与函数 functionName() {}
var functionName = function() {} vs function functionName() {}
问:
我最近开始维护别人的 JavaScript 代码。我正在修复错误,添加功能,并尝试整理代码并使其更加一致。
以前的开发人员使用了两种声明函数的方法,我无法确定其背后是否有原因。
这两种方式是:
var functionOne = function() {
// Some code
};
和
function functionTwo() {
// Some code
}
使用这两种不同方法的原因是什么,每种方法的优缺点是什么?有什么事情可以用一种方法完成,而另一种方法却无法完成吗?
答:
区别在于它是一个函数表达式,因此仅在到达该行时定义,而是一个函数声明,并在其周围的函数或脚本执行后立即定义(由于提升)。functionOne
functionTwo
例如,函数表达式:
// 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
var
let functionFour = function () {...}
let functionFour
undefined
const
首先,我想更正 Greg: is scoped too — 该名称是在遇到此定义的范围内定义的。例:function abc(){}
abc
function xyz(){
function abc(){};
// abc is defined here...
}
// ...but not here
其次,可以将两种风格结合起来:
var xyz = function abc(){};
xyz
将像往常一样被定义,在除 Internet Explorer 之外的所有浏览器中都未定义 - 不要依赖它被定义。但它将在它的体内定义:abc
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"
使用组合样式的一个令人信服的理由是函数对象的“name”属性(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()
评论
abc
()
var
let
var
在计算机科学术语中,我们谈论匿名函数和命名函数。我认为最重要的区别是匿名函数不绑定到名称,因此名称为匿名函数。在 JavaScript 中,它是在运行时动态声明的第一类对象。
有关匿名函数和 lambda 演算的更多信息,维基百科是一个好的开始:匿名函数。
在代码维护成本方面,命名函数更可取:
- 独立于它们被宣布的地方(但仍然受到范围的限制)。
- 更能抵抗条件初始化等错误(如果需要,您仍然可以覆盖)。
- 通过将本地函数与作用域功能分开分配,代码变得更具可读性。通常,在作用域中,首先是功能,然后是本地函数的声明。
- 在调试器中,您将清楚地看到调用堆栈上的函数名称,而不是“匿名/评估”函数。
我怀疑命名函数的更多优点如下。被列为命名函数的优点,对于匿名函数来说却是缺点。
从历史上看,匿名函数的出现是因为 JavaScript 作为一种语言无法列出具有命名函数的成员:
{
member:function() { /* How do I make "this.member" a named function? */
}
}
您在那里发布的两个代码片段,几乎在所有目的上都以相同的方式运行。
但是,行为的区别在于,对于第一个变体 (),该函数只能在代码中的该点之后调用。var functionOne = function() {}
使用第二个变体 () 时,该函数可用于在声明该函数的位置上运行的代码。function functionTwo()
这是因为对于第一个变体,函数在运行时分配给变量。在第二种情况下,函数在解析时分配给该标识符 。foo
foo
更多技术信息
JavaScript 有三种定义函数的方法。
- 第一个代码段显示了一个函数表达式。这涉及使用“function”运算符创建函数 - 该运算符的结果可以存储在任何变量或对象属性中。这样一来,函数表达式就很强大了。函数表达式通常被称为“匿名函数”,因为它不必有名称,
- 第二个示例是函数声明。这使用“function”语句来创建函数。该函数在分析时可用,并且可以在该作用域中的任何位置调用。以后仍可将其存储在变量或对象属性中。
- 定义函数的第三种方法是“Function()”构造函数,它没有出现在您的原始帖子中。不建议使用它,因为它的工作方式与 相同,但有其问题。
eval()
说到全局上下文,语句和末尾的 a 都会在全局对象上创建一个不可删除的属性,但两者的值都可以被覆盖。var
FunctionDeclaration
这两种方式之间的微妙区别在于,当变量实例化过程运行时(在实际代码执行之前),所有声明的标识符都将使用 ,并且 使用的标识符将从那一刻起可用,例如: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
由 a 创建的全局属性可以毫无问题地被覆盖,就像变量值一样,例如:FunctionDeclaration
function test () {}
test = null;
两个示例之间的另一个明显区别是,第一个函数没有名称,但第二个函数有名称,这在调试(即检查调用堆栈)时非常有用。
关于您编辑的第一个示例(),这是一个未声明的作业,我强烈建议您始终使用关键字。foo = function() { alert('hello!'); };
var
使用赋值时,如果没有语句,如果在作用域链中找不到引用的标识符,它将成为全局对象的可删除属性。var
此外,未声明的赋值会在严格模式下对 ECMAScript 5 抛出一个。ReferenceError
必读:
注意:此答案已从另一个问题合并而来,其中 OP 的主要疑问和误解是,用 声明的标识符不能被覆盖,但事实并非如此。FunctionDeclaration
一个重要的原因是添加一个且只有一个变量作为命名空间的“根”...
var MyNamespace = {}
MyNamespace.foo= function() {
}
或
var MyNamespace = {
foo: function() {
},
...
}
有许多用于命名空间的技术。随着大量可用的 JavaScript 模块,它变得更加重要。
其他评论者已经介绍了上述两个变体的语义差异。我想注意一个风格上的差异:只有“赋值”变体才能设置另一个对象的属性。
我经常使用这样的模式构建 JavaScript 模块:
(function(){
var exports = {};
function privateUtil() {
...
}
exports.publicUtil = function() {
...
};
return exports;
})();
使用此模式,您的公共函数将全部使用赋值,而您的私有函数使用声明。
(另请注意,赋值应要求在语句后使用分号,而声明则禁止使用分号。
@EugeneLazutkin举了一个例子,他命名了一个赋值函数,以便能够使用 shortcut()
作为对自身的内部引用。John Resig 在他的 Learning Advanced Javascript 教程中给出了另一个例子 - 复制分配给另一个对象的递归函数。虽然严格来说,将函数分配给属性并不是这里的问题,但我建议积极尝试本教程 - 通过单击右上角的按钮运行代码,然后双击代码以根据自己的喜好进行编辑。
教程中的示例:递归调用:yell()
删除原始 ninja 对象时,测试失败。(第 13 页)
function assert(predicate, message) { if(!predicate) { throw new Error(message); } }
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?" );
}
如果命名将以递归方式调用的函数,则测试将通过。(第 14 页)
function assert(predicate, message) { if(!predicate) { throw new Error(message); } }
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." );
console.log(samurai.yell(4));
其他答案中未提及的另一个区别是,如果您使用匿名函数并将其用作构造函数,则
var functionOne = function() {
// Some code
};
var one = new functionOne();
console.log(one.constructor.name);
则不会被定义。 是非标准的,但 Firefox、Chrome、其他 Webkit 衍生浏览器和 IE 9+ 都支持。one.constructor.name
Function.name
跟
function functionTwo() {
// Some code
}
two = new functionTwo();
可以使用 以字符串形式检索构造函数的名称。two.constructor.name
我在我的代码中使用变量方法是一个非常具体的原因,上面已经以抽象的方式介绍了其理论,但一个例子可能会帮助一些像我这样 JavaScript 专业知识有限的人。
我有代码,我需要使用 160 个独立设计的品牌来运行。大多数代码都在共享文件中,但特定于品牌的内容位于一个单独的文件中,每个品牌一个文件。
有些品牌需要特定的功能,有些则不需要。有时我必须添加新功能来做新的品牌特定事情。我很乐意更改共享代码,但我不想更改所有 160 组品牌文件。
通过使用变量语法,我可以在共享代码中声明变量(本质上是函数指针),并分配一个微不足道的存根函数,或设置为 null。
然后,需要函数特定实现的一个或两个品牌可以定义其函数版本,并根据需要将其分配给变量,其余的则不执行任何操作。我可以在共享代码中执行 null 函数之前对其进行测试。
从上面人们的评论中,我收集到也可以重新定义静态函数,但我认为变量解决方案很好,很清楚。
第一个(函数 doSomething(x))应该是对象表示法的一部分。
第二个 () 只是创建一个匿名函数并将其分配给变量 .所以 doSomething() 将调用该函数。var doSomething = function(x){ alert(x);}
doSomething
您可能想知道什么是函数声明和函数表达式。
函数声明定义命名函数变量,而无需赋值变量。函数声明作为独立构造出现,不能嵌套在非功能块中。
function foo() {
return 3;
}
ECMA 5 (13.0) 将语法定义为
function Identifier ( 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) 将语法
定义为函数 Identifier opt ( FormalParameterListopt ) { FunctionBody }
一旦建立绑定,函数声明和分配给变量的函数表达式的行为就相同。
然而,函数对象实际与其变量关联的方式和时间是有区别的。这种差异是由于 JavaScript 中称为变量提升的机制造成的。
基本上,所有函数声明和变量声明都被提升到发生声明的函数的顶部(这就是为什么我们说 JavaScript 具有函数范围)。
当函数声明被提升时,函数体“跟随” 因此,当函数体被计算时,变量将立即 绑定到函数对象。
当一个变量声明被提升时,初始化不会随之而来,而是被“抛在后面”。该变量初始化为函数体的开头,并将在代码中的原始位置分配一个值。(实际上,它将在发生同名变量声明的每个位置分配一个值。
undefined
提升的顺序也很重要:函数声明优先于同名的变量声明,最后一个函数声明优先于前面同名的函数声明。
一些例子...
var foo = 1;
function bar() {
if (!foo) {
var foo = 10 }
return foo; }
bar() // 10
变量被提升到函数的顶部,初始化为 ,即 ,所以被赋值。范围之外不起任何作用,也不受影响。foo
undefined
!foo
true
foo
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
a
何时首选第一种方法而不是第二种方法的例证是,何时需要避免重写函数的先前定义。
跟
if (condition){
function myfunction(){
// Some code
}
}
,此定义将覆盖任何先前的定义,因为它将在解析时完成。myfunction
而
if (condition){
var myfunction = function (){
// Some code
}
}
仅在满足时才进行定义。myfunction
condition
如果您使用这些函数创建对象,您将获得:
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
以下是创建函数的标准表单的概要: (最初是为另一个问题编写的,但在移入规范问题后进行了调整。
条款:
- ES5:ECMAScript 第 5 版,2009 年
- ES2015:ECMAScript 2015(也称为“ES6”)
快速列表:
函数声明
“匿名”表达式(尽管有这个术语,但有时会创建带有名称的函数)
function
命名表达式
function
访问器函数初始值设定项 (ES5+)
箭头函数表达式 (ES2015+)(与匿名函数表达式一样,不涉及显式名称,但可以创建具有名称的函数)
对象初始值设定项中的方法声明 (ES2015+)
(ES2015+) 中的构造函数和方法声明
class
函数声明
第一种形式是函数声明,如下所示:
function x() {
console.log('x');
}
函数声明是一个声明;它不是陈述或表达。因此,您不会在它后面加上一个(尽管这样做是无害的)。;
在执行任何分步代码之前,当执行进入函数声明所在的上下文时,将处理函数声明。它创建的函数被赋予一个专有名称(在上面的示例中),并且该名称被放在声明出现的范围内。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
}
而且,由于它们是在运行分步代码之前处理的,因此当它们处于控制结构中时,要知道该做什么是很棘手的。
虽然直到 ES2015 才指定这样做,但它是支持块中函数声明的允许扩展。不幸的是(不可避免地),不同的引擎做了不同的事情。
从 ES2015 开始,规范说明了要做什么。事实上,它提供了三件独立的事情要做:
- 如果在松散模式下不在 Web 浏览器上,则 JavaScript 引擎应该做一件事
- 如果在 Web 浏览器上处于松散模式,则 JavaScript 引擎应该执行其他操作
- 如果处于严格模式(无论是否使用浏览器),JavaScript 引擎应该做另一件事
松散模式的规则很棘手,但在严格模式下,块中的函数声明很容易:它们是块的本地函数(它们具有块范围,这在 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
SetFunctionName
命名表达式function
第三种形式是命名函数表达式 (“NFE”):
var z = function w() {
console.log('zw')
};
这创建的函数具有正确的名称(在本例中)。与所有表达式一样,在逐步执行代码时会计算此值。函数的名称不会添加到表达式出现的范围内;该名称位于函数本身的范围内:w
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
请注意,NFE 经常是 JavaScript 实现的 bug 来源。例如,IE8 及更早版本完全错误地处理 NFE,在两个不同的时间创建两个不同的函数。早期版本的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
=>
上面的例子是其中的一种形式。如果有多个参数要传递函数,请使用 parens: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
在这两种情况下,函数的主体都只是一个表达式;函数的返回值将自动成为该表达式的结果(您不使用 explicit )。return
如果您要执行的不仅仅是单个表达式,请像往常一样使用 and 显式(如果需要返回值):{}
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() {
}
};
区别(除了冗长)是方法可以使用,但函数不能。因此,例如,如果你有一个使用方法语法定义(比如)的对象,它可以用来获取返回的值(在可能对它做其他事情之前),而 ES5 版本将不得不这样做。super
valueOf
super.valueOf()
Object.prototype.valueOf
Object.prototype.valueOf.call(this)
这也意味着该方法具有对定义它的对象的引用,因此,如果该对象是临时的(例如,您将其作为源对象之一传递),则方法语法可能意味着该对象保留在内存中,否则它可能会被垃圾回收(如果 JavaScript 引擎未检测到这种情况并在没有方法使用时对其进行处理)。Object.assign
super
(ES2015+) 中的构造函数和方法声明class
ES2015 为我们带来了语法,包括声明的构造函数和方法:class
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
上面有两个函数声明:一个用于构造函数,它获取名称 ,另一个用于 ,这是分配给 的函数。Person
getFullName
Person.prototype
第一个示例是函数声明:
function abc(){}
第二个示例是函数表达式:
var abc = function() {};
主要区别在于它们是如何被提升(提升和宣布)的。在第一个示例中,整个函数声明被提升。在第二个示例中,仅提升了 var 'abc',其值(函数)将未定义,并且函数本身仍保留在声明的位置。
简单地说:
//this will work
abc(param);
function abc(){}
//this would fail
abc(param);
var abc = function() {}
要了解有关此主题的更多信息,我强烈建议您使用此链接
对格雷格的回答有更好的解释
functionTwo();
function functionTwo() {
}
为什么没有错误?我们总是被教导说,表达式是从上到下执行的(??
因为:
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();
---------------
鉴于“命名函数显示在堆栈跟踪中”的论点,现代 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'));
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
我添加自己的答案只是因为其他人都彻底涵盖了起重部分。
很长一段时间以来,我一直在思考哪种方式更好,多亏了 http://jsperf.com 现在我知道了:)
函数声明更快,这才是 Web 开发中真正重要的东西,对吧?;)
评论
这只是声明函数的两种可能方式,在第二种方式中,您可以在声明之前使用函数。
格雷格的回答已经足够好了,但我仍然想补充一些我刚才通过观看道格拉斯·克罗克福德的视频学到的东西。
函数表达式:
var foo = function foo() {};
功能语句:
function foo() {};
函数语句只是具有值的语句的简写。var
function
所以
function foo() {};
扩展到
var foo = function foo() {};
这进一步扩展到:
var foo = undefined;
foo = function foo() {};
它们都被提升到代码的顶部。
我在下面列出了差异:
函数声明可以放在代码中的任何位置。即使它在定义出现在代码中之前被调用,它也会在函数声明提交到内存时执行,或者在页面中的任何其他代码开始执行之前以某种方式被提升。
请看下面的功能:
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()}
两者都是定义函数的不同方式。区别在于浏览器如何解释它们并将其加载到执行上下文中。
第一种情况是函数表达式,它仅在解释器到达该行代码时加载。因此,如果您像下面这样操作,您将收到一个错误,指出 functionOne 不是函数。
functionOne();
var functionOne = function() {
// Some code
};
原因是在第一行没有为 functionOne 赋值,因此它是未定义的。我们试图将其作为函数调用,因此我们收到一个错误。
在第二行中,我们将匿名函数的引用分配给 functionOne。
第二种情况是在执行任何代码之前加载的函数声明。因此,如果您确实喜欢以下内容,则在代码执行之前加载声明时不会出现任何错误。
functionOne();
function functionOne() {
// Some code
}
提升是 JavaScript 解释器将所有变量和函数声明移动到当前作用域顶部的操作。
但是,只有实际的声明被吊起。通过将任务留在原地。
- 在页面中声明的变量/函数是全局的,可以在该页面中的任何位置访问。
- 变量/函数在函数中声明具有局部作用域。表示它们在函数体(作用域)内可用/访问,在函数体外不可用。
Javascript 被称为松散类型语言。这意味着 Javascript 变量可以保存任何数据类型的值。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',变量声明默认值也是 '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 构造函数类:使用 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
new Function()
可用于在字符串中传递函数的主体。因此,这可用于创建动态函数。同时传递脚本而不执行脚本。
var func = new Function("x", "y", "return x*y;");
function secondFunction(){
var result;
result = func(10,20);
console.log ( result );
}
secondFunction()
评论
在 JavaScript 中,有两种方法可以创建函数:
功能声明:
function fn(){ console.log("Hello"); } fn();
这是非常基本的、不言自明的,在许多语言中使用,并且是 C 系列语言的标准。我们声明了一个函数,定义了它,并通过调用它来执行它。
你应该知道的是,函数实际上是 JavaScript 中的对象;在内部,我们为上述函数创建了一个对象,并为其命名,称为 fn,或者对该对象的引用存储在 fn 中。函数是 JavaScript 中的对象;函数的实例实际上是一个对象实例。
函数表达式:
var fn=function(){ console.log("Hello"); } fn();
JavaScript 具有一流的函数,即创建一个函数并将其分配给变量,就像您创建一个字符串或数字并将其分配给变量一样。在这里,fn 变量被分配给一个函数。这个概念的原因是函数是 JavaScript 中的对象;fn 指向上述函数的对象实例。我们已经初始化了一个函数并将其分配给一个变量。它不是执行函数并分配结果。
参考:JavaScript 函数声明语法:var fn = function() {} vs function fn() {}
它们非常相似,但有一些小的区别,第一个是分配给匿名函数(函数声明)的变量,第二个是在 JavaScript 中创建函数的正常方法(匿名函数声明),两者都有用法、缺点和优点:
1. 函数表达式
var functionOne = function() {
// Some code
};
函数表达式将函数定义为 表达式语法(通常是变量赋值)。功能 通过 Functions 定义 表达式可以是命名的,也可以是匿名的。功能 表达式不得以“function”开头(因此括号 围绕下面的自调用示例)。
将变量分配给函数,意味着没有提升,因为我们知道 JavaScript 中的函数可以提升,意味着它们可以在声明之前被调用,而变量需要在访问它们之前声明,所以在这种情况下,我们不能在声明之前访问函数,也可能是你编写函数的一种方式, 对于返回另一个函数的函数,这种声明可能是有意义的,同样在 ECMA6 及更高版本中,您可以将其分配给可用于调用匿名函数的箭头函数,而且这种声明方式是在 JavaScript 中创建构造函数的更好方法。
2. 功能声明
function functionTwo() {
// Some code
}
函数声明定义一个命名函数变量,而不 需要变量赋值。函数声明以 独立构造,不能嵌套在非功能块中。 将它们视为变量声明的同级是很有帮助的。 就像变量声明必须以“var”开头一样,函数 声明必须以“function”开头。
这是在 JavaScript 中调用函数的正常方式,这个函数可以在你声明它之前就被调用,因为在 JavaScript 中所有函数都会被提升,但如果你有“使用严格”,这不会像预期的那样提升,这是调用所有正常函数的好方法,这些函数的行数不大,也不是构造函数。
此外,如果您需要有关 JavaScript 中吊装工作原理的更多信息,请访问以下链接:
https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
关于性能:
新版本引入了几个底层优化,也是如此。V8
SpiderMonkey
现在,表达式和声明之间几乎没有区别。
函数表达式现在似乎更快了。
Anonymous
与函数表达式相比,函数表达式似乎具有更好的性能。Named
评论
下面列出了两个不同的函数声明之间有四个值得注意的比较。
- 函数的可用性(范围)
以下工作有效,因为范围限定为最近的块: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
以这种方式声明函数时,函数的名称是 funcname。function thefuncname(){}
function foobar(a, b){}
console.log(foobar.name);
var a = function foobar(){};
console.log(a.name);
否则,如果将函数声明为 ,则 function.name 是用于存储该函数的第一个变量。function(){}
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 编译差异,但最终结果是完全相同的。为了证明这一点,让我们通过比较两个空白代码片段的速度来检查 JSPerf 在微基准测试中的效率。JSPerf 测试可在此处找到。而且,jsben.ch 测试都可以在这里找到。如您所见,当应该没有时,存在明显的差异。如果你真的像我一样是一个性能怪胎,那么尝试减少作用域中变量和函数的数量,特别是消除多态性(例如使用相同的变量来存储两种不同的类型)可能更值得你。
- 可变可变性
当您使用关键字声明变量时,您可以像这样为变量重新分配不同的值。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-statement 时,变量引用变得不可变。这意味着我们不能为变量赋值。但是请注意,这并不能使变量的内容不可变:如果这样做,那么您仍然可以这样做。只执行类似或会引发错误的事情,如下所示。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);
})();
什么是“最近的区块”
“最近的块”是最接近的“函数”(包括异步函数、生成器函数和异步生成器函数)。然而,有趣的是,a 的行为类似于 a 在非闭合块中对所述闭包之外的项目。观察。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
do
while
with
)
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;
}
})();
我更喜欢将函数定义为变量:
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]);
JS 中的表达式:返回值
的东西 示例:在 chrome 控制台中尝试以下内容:
a = 10
output : 10
(1 + 3)
output = 4
声明/语句:不返回值
的东西 示例:
if (1 > 2) {
// do something.
}
这里 (1>2) 是一个表达式,但“if”表示不是。它没有返回任何东西。
同样,我们有函数声明/语句与函数表达式
让我们举个例子:
// 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运行时,会发生以下事情:
- 内存将创建变量“a”和“fun_expression”。并且将为函数语句“fun_declaration”创建内存
- “a”将被分配为“undefined”。“fun_expression”将被指定为“未定义”。“fun_declaration”将完整地留在记忆中。
注意:上面的第 1 步和第 2 步称为“执行上下文 - 创建阶段”。
现在假设我们将 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
上面注释中提到的输出应该有助于理解函数表达式和函数语句/声明之间的区别。
这称为函数表达式:
var getRectArea = function(width, height) {
return width * height;
};
console.log("Area of Rectangle: " + getRectArea(3,4));
// This should return the following result in the console:
// Area of Rectangle: 12
这称为函数声明:
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
希望这有助于解释函数表达式和函数声明之间的区别以及如何使用它们。谢谢。
需要注意的重要一点是:-
让有两个功能:-
sum(1,2);
const sum = function(first, second) {
return first + second;
}
在上述情况下,它会给出未定义总和的错误,但
sum(1,2);
function sum(first, second) {
return first + second;
}
此功能不会出现任何错误,因为在这种情况下会进行吊装。
命名函数与匿名函数
第一个函数语法是 Anonymous Function Expression:
var functionOne = function() {
// do something...
};
而第二个是函数声明:
function functionTwo () {
// do something...
}
两者之间的主要区别在于函数名称,因为匿名函数没有要调用的名称。 匿名函数的声明既快速又容易,许多库和工具倾向于鼓励这种惯用的代码风格。但是,匿名函数有一些缺点:
可读性:匿名函数省略了可能导致代码可读性降低的名称。
调试:匿名函数在堆栈跟踪中没有名称,这可能会使调试更加困难。
Self-Reference:如果函数需要引用自身,例如递归,该怎么办。
命名函数表达式
为函数表达式提供名称可以非常有效地解决所有这些缺点,并且没有明显的缺点。最佳做法是始终命名函数表达式:
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!');
对于分配给变量的函数,在这种情况下,命名函数不是很常见,可能会引起混淆,在这种情况下,箭头函数可能是更好的选择。
两个函数之间的另一个区别是 functionOne 可以用作一个变量,该变量可以在其中保存多个函数,而 functionTwo 包含一些代码块,这些代码块在调用时全部执行。请查看以下内容:
var functionOne = (function() {
return {
sayHello: function(){
console.log('say hello')
},
redirectPage:function(_url){
window.location.href = _url;
}
}
})();
您可以选择要调用的函数。例如 functionOne.sayHello 或 functionOne。重定向页面。如果你调用functionTwo,那么整个代码块将被执行。
差值函数声明和函数表达式:
Javascript 具有一流的函数。这意味着它们可以像对待任何其他变量一样对待。函数可以作为函数中的参数传递,从函数返回,并且可以存储在变量中。
但是,将函数存储在变量(函数表达式)中并不是创建函数的唯一方法,这也可以通过函数声明来完成。以下是主要区别:
- 函数表达式可以是匿名的,而函数声明必须具有名称。
- 两者都具有用于标识函数的 name 属性。函数表达式的 name 属性是它绑定到的变量的名称,而函数声明的名称只是给定的名称。
- 函数声明被提升,而函数表达式则不被提升。只有变量被提升为具有 的值。
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');
}
:
评论
let functionOne
undefined
在运行时定义,在解析时定义。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 运行时与解析时之间的解释
不能在函数声明中使用该方法,但可以对函数表达式使用该方法。.bind()
功能声明:
function x() {
console.log(this)
}.bind('string')
x()
函数表达式:
var x = function() {
console.log(this)
}.bind('string')
x()
在调试器/DevTools 中的断点期间,如果在控制台中使用该格式,则无法随后在控制台中使用 (它显示“未定义”) ,而在 之后,可以使用该函数。function functionName() {}
functionName()
var functionName = function() {}
请参阅此问题。
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
var add=function(a, b){
return a + b;
}
评论
上一个:如何重定向到另一个网页?
评论