var 函数名称 = function() {} vs function 函数名称() {}
- 2024-11-02 21:00:00
- admin 原创
- 32
问题描述:
我最近开始维护其他人的 JavaScript 代码。我正在修复错误、添加功能,并尝试整理代码并使其更加一致。
之前的开发人员使用了两种声明函数的方式,我不知道这背后是否有原因。
这两种方式是:
var functionOne = function() {
// Some code
};
和,
function functionTwo() {
// Some code
}
使用这两种不同方法的原因是什么?每种方法的优缺点是什么?是否有一种方法可以做到而另一种方法做不到的事情?
解决方案 1:
不同之处在于functionOne
是一个函数表达式,因此仅在到达该行时才定义,而functionTwo
是一个函数声明,并且在执行其周围的函数或脚本时立即定义(由于提升)。
例如函数表达式:
// TypeError: functionOne is not a function
functionOne();
var functionOne = function() {
console.log("Hello!");
};
运行代码片段Hide results展开片段
并且,函数声明:
// Outputs: "Hello!"
functionTwo();
function functionTwo() {
console.log("Hello!");
}
运行代码片段Hide results展开片段
过去,不同浏览器对块内定义的函数声明的处理方式不一致。严格模式(ES5 中引入)通过将函数声明的作用域限定在其封闭的块内来解决此问题。
'use strict';
{ // note this block!
function functionThree() {
console.log("Hello!");
}
}
functionThree(); // ReferenceError
运行代码片段Hide results展开片段
解决方案 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"
使用组合样式的一个令人信服的理由是函数对象的“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()
——答案是:它取决于浏览器。
解决方案 3:
以下是创建函数的标准形式的概述:(最初是为另一个问题编写的,但在移入规范问题后进行了改编。)
条款:
ES5:ECMAScript 第 5 版,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
、、、等)中,JavaScript 引擎应该做什么,如下所示: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 实现中的错误来源。例如,IE8 及更早版本完全错误地处理了 NFE ,在两个不同的时间创建了两个不同的函数。Safari 的早期版本也存在问题。好消息是,当前版本的浏览器(IE9 及更高版本、当前 Safari)不再存在这些问题。(但遗憾的是,截至撰写本文时,IE8 仍在广泛使用,因此在 Web 代码中使用 NFE 仍然存在问题。)
访问器函数初始化器 (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
末尾的都会在全局对象上创建不可删除的属性,但两者的值都可以被覆盖。
这两种方式的细微差别在于,当变量实例化过程运行时(在实际代码执行之前),所有用 声明的标识符都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
语句,如果在范围链中找不到引用的标识符,它将成为全局对象的可删除属性。
此外,ReferenceError
在 ECMAScript 5严格模式下,未声明的赋值会抛出一个错误。
必读:
揭秘命名函数表达式
注意:这个答案是从另一个问题合并而来的,在该问题中,OP 的主要疑问和误解是用 , 声明的标识符FunctionDeclaration
不能被覆盖,但事实并非如此。
解决方案 5:
您在此处发布的两段代码片段在几乎所有用途上都会以相同的方式运行。
然而,行为上的不同在于,对于第一个变体(var functionOne = function() {}
),该函数只能在代码中的该点之后调用。
使用第二种变体(function functionTwo()
),该函数可供在声明该函数的位置上运行的代码使用。
这是因为,在第一种情况下,函数foo
在运行时被分配给变量。在第二种情况下,函数foo
在解析时被分配给该标识符。
更多技术信息
JavaScript 有三种定义函数的方式。
第一个代码片段显示了一个函数表达式。这涉及使用“函数”运算符来创建函数 - 该运算符的结果可以存储在任何变量或对象属性中。函数表达式的功能非常强大。函数表达式通常被称为“匿名函数”,因为它不必有名称,
第二个示例是函数声明。它使用“function”语句来创建函数。该函数在解析时可用,并且可以在该范围内的任何地方调用。您以后仍可以将其存储在变量或对象属性中。
定义函数的第三种方法是“Function()”构造函数,这在您的原始帖子中没有显示。不建议使用此方法,因为它的工作方式与 相同
eval()
,但存在问题。
解决方案 6:
对Greg 的回答有更好的解释
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
}
}
myfunction
仅当condition
满足时才进行正确的定义。
解决方案 9:
一个重要的原因是添加一个且仅一个变量作为命名空间的“根”......
var MyNamespace = {}
MyNamespace.foo= function() {
}
或者
var MyNamespace = {
foo: function() {
},
...
}
命名空间技术有很多种。随着 JavaScript 模块的增多,命名空间变得越来越重要。
另请参阅如何在 JavaScript 中声明命名空间?
解决方案 10:
提升 是 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
解决方案 11:
会员资格包括每周时间表上安排的所有拳击课程,无需锁定合同价格有显示请先阅读后评论。
功能的可用性(范围)
以下方法可行,因为function add()
范围是最近的块:
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
function add(a, b){
return a + b;
}
运行代码片段Hide results展开片段
以下操作不起作用,因为在将函数值分配给变量之前调用了该变量add
。
try {
console.log("Success: ", add(1, 1));
} catch(e) {
console.log("ERROR: " + e);
}
var add=function(a, b){
return a + b;
}
运行代码片段Hide results展开片段
上述代码的功能与以下代码相同。请注意,显式赋值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;
}
运行代码片段Hide results展开片段
以下不起作用,因为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;
}
运行代码片段Hide results展开片段
(函数)名称
当以这种方式声明函数时,其名称function thefuncname(){}
为thefuncname 。
function foobar(a, b){}
console.log(foobar.name);
运行代码片段Hide results展开片段
var a = function foobar(){};
console.log(a.name);
运行代码片段Hide results展开片段
否则,如果函数声明为function(){}
,则函数.name 是用于存储该函数的第一个变量。
var a = function(){};
var b = (function(){ return function(){} });
console.log(a.name);
console.log(b.name);
运行代码片段Hide results展开片段
如果没有为函数设置变量,则函数名称为空字符串 ( ""
)。
console.log((function(){}).name === "");
运行代码片段Hide results展开片段
最后,虽然分配给该函数的变量最初会设置名称,但分配给该函数的后续变量不会改变名称。
var a = function(){};
var b = a;
var c = b;
console.log(a.name);
console.log(b.name);
console.log(c.name);
运行代码片段Hide results展开片段
表现
在 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);
})();
运行代码片段Hide results展开片段
但是,当我们使用 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);
})();
运行代码片段Hide results展开片段
有趣的是,如果我们将变量声明为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);
})();
运行代码片段Hide results展开片段
我们正在寻找“合作伙伴”
“最近的块”是最近的“函数”(包括异步函数、生成器函数和异步生成器函数)。然而,有趣的是,对于非闭包块中的闭包外部项,其function functionName() {}
行为类似于when。观察。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}
运行代码片段Hide results展开片段
普通的
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;
}
运行代码片段Hide results展开片段
功能
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;
}
})();
运行代码片段Hide results展开片段
语句(例如
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;
}
}
运行代码片段Hide results展开片段
箭头函数
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;
}
})();
运行代码片段Hide results展开片段
箭头函数
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;
}
})();
运行代码片段Hide results展开片段
解决方案 12:
我添加自己的答案只是因为其他人都已经彻底介绍了提升部分。
我很久以来就一直在想哪种方法更好,现在感谢http://jsperf.com,我知道了:)
函数声明更快,这在 Web 开发中真正重要,对吧?;)
解决方案 13:
一旦建立绑定,函数声明和分配给变量的函数表达式的行为就相同。
然而,函数对象如何以及何时与其变量实际关联存在差异。这种差异是由于 JavaScript 中的变量提升机制造成的。
基本上,所有函数声明和变量声明都会提升到声明所在的函数的顶部(这就是我们说 JavaScript 具有函数作用域的原因)。
当函数声明被提升时,函数体会“跟随”,因此当函数体被评估时,变量将立即绑定到函数对象。
当变量声明被提升时,初始化不会随之
而来,而是“被遗留”。变量在
undefined
函数体的开头被初始化,并将在代码中的原始位置被赋值。(实际上,它将在每次出现同名变量声明的位置
被赋值。)
提升的顺序也很重要:函数声明优先于同名的变量声明,最后一个函数声明优先于前面的同名函数声明。
一些例子...
var foo = 1;
function bar() {
if (!foo) {
var foo = 10 }
return foo; }
bar() // 10
变量foo
被提升到函数顶部,初始化为,undefined
即,因此被赋值。的作用域之外不起作用,不受影响。!foo
`truefoo
10foo
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
。
解决方案 14:
第一个例子是函数声明:
function abc(){}
第二个例子是函数表达式:
var abc = function() {};
主要区别在于它们如何被提升(提升和声明)。在第一个例子中,整个函数声明被提升。在第二个例子中,只有变量“abc”被提升,其值(函数)将未定义,而函数本身仍保留在声明的位置。
简单来说:
//this will work
abc(param);
function abc(){}
//this would fail
abc(param);
var abc = function() {}
要了解有关此主题的更多信息,我强烈建议您使用此
链接
解决方案 15:
从代码维护成本的角度来看,命名函数是更可取的:
独立于声明它们的位置(但仍然受范围限制)。
更能抵抗条件初始化等错误(如果需要,您仍然可以覆盖)。
通过将本地函数与作用域功能分开分配,代码的可读性会更高。通常在作用域中,功能首先出现,然后是本地函数的声明。
在调试器中,您将清楚地看到调用堆栈上的函数名称,而不是“匿名/已评估”函数。
我猜想命名函数的优点还不止这些。而命名函数的优点恰恰是匿名函数的缺点。
从历史上看,匿名函数的出现是因为 JavaScript 语言无法列出具有命名函数的成员:
{
member:function() { /* How do I make "this.member" a named function? */
}
}
解决方案 16:
格雷格的回答已经足够好了,但我仍然想补充一些我刚刚观看道格拉斯·克罗克福德 (Douglas Crockford) 的视频时学到的内容。
函数表达式:
var foo = function foo() {};
函数语句:
function foo() {};
函数语句只是var
带有值的语句的简写function
。
所以
function foo() {};
扩展为
var foo = function foo() {};
进一步扩展为:
var foo = undefined;
foo = function foo() {};
并且它们都被提升到了代码的顶部。
解决方案 17:
在计算机科学术语中,我们谈论的是匿名函数和命名函数。我认为最重要的区别是匿名函数不与名称绑定,因此得名匿名函数。在 JavaScript 中,它是在运行时动态声明的一流对象。
有关匿名函数和 lambda 演算的更多信息,维基百科是一个很好的开始:匿名函数。
解决方案 18:
我出于一个非常具体的原因在我的代码中使用变量方法,其理论已经在上面以抽象的方式介绍过了,但是一个例子可能会对一些像我这样 JavaScript 专业知识有限的人有所帮助。
我有需要运行 160 个独立设计品牌的代码。大多数代码都在共享文件中,但品牌特定的内容则位于单独的文件中,每个品牌一个文件。
有些品牌需要特定功能,有些则不需要。有时我必须添加新功能来执行新的品牌特定操作。我很乐意更改共享编码,但我不想更改所有 160 组品牌文件。
通过使用变量语法,我可以在共享代码中声明变量(本质上是一个函数指针)并分配一个简单的存根函数,或者设置为空。
然后,需要特定函数实现的一两个品牌可以定义其函数版本,并根据需要将其分配给变量,其余品牌则不执行任何操作。在共享代码中执行函数之前,我可以测试函数是否为空。
从上面人们的评论来看,我认为重新定义静态函数也是可能的,但我认为变量解决方案很好而且很清晰。
解决方案 19:
@EugeneLazutkin给出了一个例子,他将一个分配的函数命名为能够用作shortcut()
其自身的内部引用。John Resig给出了另一个例子 -在他的Learning Advanced Javascript教程中复制分配给另一个对象的递归函数。虽然将函数分配给属性并不是这里的问题,但我建议积极尝试本教程 - 通过单击右上角的按钮运行代码,然后双击代码以根据您的喜好进行编辑。
本教程中的示例:递归调用yell()
:
当原始忍者物体被移除时,测试失败。(第 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?" );
}
运行代码片段Hide results展开片段
如果您命名将被递归调用的函数,则测试将会通过。(第 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));
运行代码片段Hide results展开片段
解决方案 20:
其他答案中没有提到的另一个区别是,如果你使用匿名函数并将其用作构造函数,如
var functionOne = function() {
// Some code
};
var one = new functionOne();
console.log(one.constructor.name);
运行代码片段Hide results展开片段
则one.constructor.name
不会被定义。Function.name
是非标准的,但受到 Firefox、Chrome、其他 Webkit 衍生浏览器和 IE 9+ 支持。
和
function functionTwo() {
// Some code
}
two = new functionTwo();
可以使用 以字符串形式检索构造函数的名称two.constructor.name
。
解决方案 21:
第一个(函数 doSomething(x))应该是对象符号的一部分。
第二个(var doSomething = function(x){ alert(x);}
)只是创建一个匿名函数并将其分配给一个变量。doSomething
因此 doSomething() 将调用该函数。
您可能想知道什么是函数声明和函数表达式。
函数声明定义命名函数变量,无需变量赋值。函数声明作为独立结构出现,不能嵌套在非功能块中。
function foo() {
return 3;
}
ECMA 5 (13.0) 将语法定义为
function Identifier ( 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 }
解决方案 22:
我列出了以下差异:
函数声明可以放在代码中的任何位置。即使它在定义出现在代码中之前被调用,它也会随着函数声明被提交到内存中或以提升的方式被执行,在页面中的任何其他代码开始执行之前。
看一下下面的函数:
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()}
解决方案 23:
关于性能:
新版本V8
引入了几项底层优化,因此也是如此SpiderMonkey
。
现在表达式和声明之间几乎没有区别。
函数表达式现在似乎更快了。
Chrome 62.0.3202
火狐 55
Chrome Canary 63.0.3225
Anonymous
函数表达式似乎
比函数表达式具有更好的性能Named
。
Firefox
Chrome 金丝雀版Chrome
解决方案 24:
如果你使用这些函数来创建对象,你将获得:
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
解决方案 25:
命名函数与匿名函数
第一个函数语法是匿名函数表达式:
var functionOne = function() {
// do something...
};
而第二个是函数声明:
function functionTwo () {
// do something...
}
两者之间的主要区别在于函数名称,因为匿名函数没有名称可调用。匿名函数声明起来既快速又简单,许多库和工具都倾向于鼓励这种惯用的代码风格。但是,匿名函数有一些缺点:
可读性:匿名函数省略了名称,这可能会导致代码的可读性降低。
调试:匿名函数在堆栈跟踪中没有名称,这会使调试更加困难。
自引用:如果函数需要引用自身,例如进行递归,该怎么办?
命名函数表达式
为函数表达式提供一个名称可以非常有效地解决所有这些缺点,并且没有明显的缺点。最佳做法是始终为函数表达式命名:
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!');
对于分配给变量的函数,在这种情况下命名函数并不是很常见并且可能会引起混淆,在这种情况下,箭头函数可能是一个更好的选择。
解决方案 26:
在 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() {}
解决方案 27:
鉴于“命名函数出现在堆栈跟踪中”的论点,现代 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 + '
' +
'Trace:
' +
error.stack);
// Need to manually log the error object in Nitro.
}
}, []).join('
'));
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
解决方案 28:
两者都是定义函数的不同方式。不同之处在于浏览器如何解释它们并将其加载到执行上下文中。
第一种情况是函数表达式,只有当解释器到达该行代码时才会加载。因此,如果你像下面这样操作,你会得到一个错误,提示 functionOne不是函数。
functionOne();
var functionOne = function() {
// Some code
};
原因是第一行没有为 functionOne 赋值,因此它是未定义的。我们试图将其作为函数调用,因此我们收到错误。
在第二行,我们将匿名函数的引用分配给 functionOne。
第二种情况是函数声明在执行任何代码之前加载。因此,如果您按照以下方式操作,则不会出现任何错误,因为声明在代码执行之前加载。
functionOne();
function functionOne() {
// Some code
}
解决方案 29:
它们非常相似,但有一些细微的差别,第一个是分配给匿名函数的变量(函数声明),第二个是在 JavaScript 中创建函数的正常方式(匿名函数声明),两者都有用法、缺点和优点:
1. 函数表达式
var functionOne = function() {
// Some code
};
函数表达式将函数定义为更大表达式语法(通常是变量赋值)的一部分。通过函数表达式定义的函数可以是命名的或匿名的。函数表达式不能以“function”开头(因此下面的自调用示例周围有括号)。
将变量分配给函数意味着不能进行提升,我们知道 JavaScript 中的函数可以进行提升,这意味着它们可以在声明之前被调用,而变量需要在访问之前进行声明,所以在这种情况下我们不能在声明函数之前访问它,这也可以是你编写函数的一种方式,对于返回另一个函数的函数,这种声明是有意义的,在 ECMA6 及更高版本中,你可以将其分配给箭头函数,该箭头函数可用于调用匿名函数,这种声明方式也是在 JavaScript 中创建构造函数的更好方法。
2. 函数声明
function functionTwo() {
// Some code
}
函数声明定义命名函数变量,无需变量赋值。函数声明作为独立结构出现,不能嵌套在非功能块中。将它们视为变量声明的兄弟会很有帮助。正如变量声明必须以“var”开头一样,函数声明必须以“function”开头。
这是在 JavaScript 中调用函数的正常方式,可以在声明它之前调用该函数,因为在 JavaScript 中所有函数都会被提升,但是如果您使用“use strict”,则不会按预期提升,这是调用所有行数不多且不是构造函数的正常函数的好方法。
此外,如果您需要有关 JavaScript 中提升工作原理的更多信息,请访问以下链接:
https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
解决方案 30:
这只是声明函数的两种可能方式,在第二种方式中,您可以在声明之前使用函数。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件