7634

我最近开始维护别人的JavaScript代码。我正在修复错误,添加功能,并尝试整理代码,使其更加一致。

前一位开发人员使用了两种声明函数的方法,我无法确定其背后是否有原因。

这两种方法是:

var functionOne=函数(){//一些代码};

而且,

函数functionTwo(){//一些代码}

使用这两种不同方法的原因是什么?每种方法的优缺点是什么?有什么用一种方法可以做而用另一种方法不能做的吗?

0

42个答案42

重置为默认值
5595

区别在于functionOne(功能一)是一个函数表达式因此,只有当到达该行时才进行定义,而功能二是一个函数声明并在执行其周围的函数或脚本时立即定义(由于吊装).

例如,函数表达式:

//TypeError:functionOne不是函数functionOne();var functionOne=函数(){console.log(“你好!”);};

以及,一个函数声明:

//输出:“你好!”函数Two();函数functionTwo(){console.log(“你好!”);}

历史上,块内定义的函数声明在浏览器之间的处理不一致。严格模式(ES5中引入)通过将函数声明的范围限定到其封闭块来解决此问题。

'使用严格';{//注意这个方块!函数functionThree(){console.log(“你好!”);}}函数三();//引用错误

5
  • 函数定义是在代码进入周围的块时执行的,而不是在代码进入封闭函数时执行的。我不知道事情是否总是这样,但如果使用块,这将是不可避免的常数定义一个被其中的函数封闭的变量,并一致地应用该规则可能比只在不可避免的情况下应用它要好。 评论 2020年10月26日21:37
  • 52
    “由于提升”这句话可能会给人一种错误的印象,即只有命名的函数被提升。事实上,两者都是var函数一以及功能功能二提升到某种程度-只是functionOne被设置为undefined(可以称之为半提升,变量总是提升到那个程度),而functionTwo是完全提升的,因为它是定义和声明的。调用未定义的内容当然会抛出typeError。 评论 2020年12月4日19:19
  • 6
    还有一个微小的变化变量案例,使用时let functionFour=函数(){…}.在这种情况下,声明let函数Four已吊装。但它不会被初始化,即使使用未定义值。因此,它产生了一个略有不同的错误:Uncaught ReferenceError:初始化之前无法访问“functionFour”同样适用于常数. 评论 2022年1月5日15:11
  • @railsshas_legence那么,如果它的行为与“根本没有提升”完全相同,那么称它为“半提升”又有什么意义呢?
    – 瓦诺姆
    评论 2022年4月2日19:57
  • 7
    @不过,它的行为与“根本没有吊装”不同。如果它没有被提升,你会得到一个ReferenceError。因为它被吊起来了,你会得到一个TypeError。在控制台中比较这两条语句:1。提升();var提升=函数(){}2。未吊装();const notHoisted=函数(){}。在第一种情况下,它是一个TypeError,因为您试图调用undefined(但它确实被挂起了,这就是为什么它至少是undefined-这比什么都没有还多)。在第二种情况下,它甚至没有未定义,您只会得到一个普通的ReferenceError。 评论 2022年4月14日14:12
2097

首先,我想纠正格雷格:函数abc(){}也有作用域-名称美国广播公司在遇到此定义的范围中定义。例子:

函数xyz(){函数abc(){};//abc在这里定义。。。}// ...但不是在这里

其次,可以将这两种风格结合起来:

var xyz=函数abc(){};

xyz公司将一如既往地定义,美国广播公司在所有浏览器中都未定义,但Internet Explorer-不要依赖于它的定义。但它将在其体内定义:

var xyz=函数abc(){//这里可以看到xyz//abc在这里可见}//这里可以看到xyz//这里未定义abc

如果要在所有浏览器上为函数别名,请使用此类声明:

函数abc(){};var xyz=abc;

在这种情况下xyz公司美国广播公司是同一对象的别名:

console.log(xyz===abc);//打印“true”

使用组合样式的一个引人注目的原因是函数对象的“name”属性(Internet Explorer不支持). 基本上,当您定义如下函数时

函数abc(){};console.log(abc.name);//打印“abc”

它的名称是自动指定的。但当你这样定义它时

var abc=函数(){};console.log(abc.name);//打印“”

它的名称是空的&我们创建了一个匿名函数并将其分配给某个变量。

使用组合样式的另一个很好的原因是使用一个简短的内部名称来表示自身,同时为外部用户提供一个长的非冲突名称:

//假设really.long.external.scoped为{}really.long.external.scoped.name=函数快捷方式(n){//让它递归地调用自己:快捷方式(n-1);// ...//让它作为回调传递:someFunction(快捷方式);// ...}

在上面的示例中,我们可以对外部名称执行相同的操作,但它太笨拙(并且速度较慢)。

(另一种表示自身的方式是使用参数.被叫,它仍然相对较长,在严格模式下不受支持。)

实际上,JavaScript对这两种语句的处理方式不同。这是一个函数声明:

函数abc(){}

美国广播公司以下是当前范围内所有地方的定义:

//我们可以在这里调用它abc();//作品//然而,它是在那里定义的。函数abc(){}//我们可以再打一次abc();//作品

此外,它还通过返回声明:

//我们可以在这里调用它abc();//作品回报;函数abc(){}

这是一个函数表达式:

var xyz=函数(){};

xyz公司以下是从赋值点定义的:

//我们不能在这里说xyz();//未定义!!!//现在它被定义了xyz=函数(){}//我们可以在这里调用它xyz();//作品

函数声明与函数表达式是格雷格证明存在差异的真正原因。

有趣的事实:

var xyz=函数abc(){};console.log(xyz.name);//打印“abc”

就个人而言,我更喜欢“函数表达式”声明,因为这样我可以控制可见性。当我像这样定义函数时

var abc=函数(){};

我知道我在本地定义了函数。当我像这样定义函数时

abc=函数(){};

我知道,如果我没有定义美国广播公司范围链中的任何位置。这种定义风格即使在内部使用也很有弹性评估().虽然定义

函数abc(){};

取决于上下文,可能会让您猜测它的实际定义位置,特别是在以下情况下评估()-答案是:这取决于浏览器。

7
  • 1
    var abc=函数(){};console.log(abc.name);//“abc”//自2021年起
    – lfx冷却
    评论 2021年4月3日16:27
  • 5
    显然,JS运行时变得更智能了。然后把它包装起来:varabc=(()=>function(){})();console.log(abc.name);//没有什么 评论 2021年4月15日1:17
  • @EugeneLazutkin,您正在执行函数并尝试读取结果的名称。删除“();”部分和您的示例都是正确的;) 评论 2021年10月7日16:51
  • @EugeneLazutkin定义一个函数并立即调用它,也称为IIFE(立即调用的函数表达式),这是实现词法范围的一种方法(IIFE内部的任何内容都不能从其外部访问)。因此美国广播公司不是函数本身,而是该函数的返回值。abc.name为空是有意义的,因为abc返回一个未命名的函数@ikirachen提到移除()因为这就是调用函数的原因。如果没有这个,它只是用多余的括号括起来。 评论 2021年11月16日23:43
  • 1
    要清楚的是,这是一种在括号内声明的变量中实现更严格范围的方法,使用变量将像往常一样使用函数范围,但在括号外,该匿名函数不再可访问。谢天谢地,这些天我们有,它使用普通(理智的)人所期望的块范围。最好假装一下变量在我看来并不存在。 评论 2021年11月16日23:48
741
+200

以下是创建函数的标准表单的摘要:(最初是为另一个问题编写的,但在移至规范问题后进行了修改。)

条款:

快速列表:

  • 函数声明

  • “匿名”功能表达式(尽管有这个术语,但有时会创建带有名称的函数)

  • 命名功能表达式

  • 访问器函数初始化器(ES5+)

  • 箭头函数表达式(ES2015+)(与匿名函数表达式一样,它不包含显式名称,但可以使用名称创建函数)

  • 对象初始化器中的方法声明(ES2015+)

  • 中的构造函数和方法声明(2015年版以上)

功能声明

第一种形式是函数声明,如下所示:

函数x(){控制台.log('x');}

函数声明是宣言; 它不是一个语句或表达式。因此,您不需要使用;(尽管这样做是无害的)。

当执行进入其出现的上下文时,将处理函数声明,之前执行任何循序渐进的代码。它创建的函数被赋予了一个正确的名称(x个在上面的示例中),该名称被放在声明出现的范围中。

因为它在同一上下文中的任何分步代码之前进行处理,所以您可以这样做:

x();//即使在声明之上也有效函数x(){控制台.log('x');}

在ES2015之前,该规范没有涵盖如果将函数声明放在像这样的控制结构中,JavaScript引擎应该做什么尝试如果转换虽然等,如下所示:

if(某些条件){函数foo(){//<====此处}//成为龙}

因为它们经过处理之前分步代码是运行的,当它们处于控制结构中时,很难知道要做什么。

虽然这样做不是明确规定在ES2015之前允许的延伸支持块中的函数声明。不幸的是(不可避免的),不同的引擎做了不同的事情。

截至ES2015,该规范规定了要做什么。事实上,它给出了三件独立的事情:

  1. 如果处于松散模式在web浏览器上,JavaScript引擎应该做一件事
  2. 如果在web浏览器上处于松散模式,JavaScript引擎应该执行其他操作
  3. 如果在严格的模式(浏览器与否),JavaScript引擎应该做另一件事

松散模式的规则很复杂,但在严格的模式下,块中的函数声明很容易:它们是块的本地声明(它们有分块示波器(这也是ES2015中的新功能),并将其提升至区块顶部。因此:

“使用严格”;if(某些条件){foo();//效果很好函数foo(){}}console.log(foo类型);//“undefined”(`foo`不在此处的范围内//因为它不在同一个街区)

“匿名”功能表达式

第二种常见形式称为匿名函数表达式:

var y=函数(){控制台.log('y');};

与所有表达式一样,它在代码的逐步执行中达到时进行求值。

在ES5中,此操作创建的函数没有名称(它是匿名的)。在ES2015中,如果可能的话,通过从上下文推断,为函数分配一个名称。在上面的示例中,名称为。当函数是属性初始值设定项的值时,会执行类似的操作。(有关发生这种情况的时间和规则的详细信息,请搜索设置功能名称在中规范-它出现了到处都是地点。)

命名功能表达式

第三种形式是命名函数表达式(“NFE”):

var z=函数w(){控制台.log('zw')};

此操作创建的函数具有正确的名称(w个在这种情况下)。与所有表达式一样,在代码的逐步执行过程中达到该值时,将对其进行求值。函数的名称为添加到表达式出现的范围;名称在功能本身的范围内:

var z=函数w(){console.log(类型w);//“功能”};console.log(类型w);//“未定义”

请注意,NFE经常是JavaScript实现的错误源。例如,IE8及更早版本处理非金融实体完全不正确,在两个不同时间创建两个不同的函数。Safari的早期版本也有问题。好消息是,当前版本的浏览器(IE9及更高版本,当前的Safari)不再存在这些问题。(但遗憾的是,截至本文撰写之时,IE8仍在广泛使用,因此在网络代码中使用NFE仍然是个问题。)

访问器函数初始化器(ES5+)

有时,函数可能在很大程度上不被注意的情况下潜入;情况就是这样存取函数。以下是一个示例:

var对象={值:0,获取f(){返回this.value;},集合f(v){this.value=v;}};控制台.log(obj.f);//0console.log(obj.f类型);//“数字”

请注意,当我使用该函数时,我没有使用()! 那是因为它是一个访问函数用于属性。我们以正常方式获取和设置属性,但在幕后调用函数。

还可以使用创建访问器函数对象定义属性对象定义属性和第二个参数对象.创建.

箭头函数表达式(ES2015+)

ES2015为我们带来箭头函数。以下是一个示例:

变量a=[1,2,3];var b=a.map(n=>n*2);console.log(b.join(“,”));//2, 4, 6

看到了吗n=>n*2藏在里面的东西地图()打电话?这是一个函数。

关于箭头功能的几件事:

  1. 他们没有自己的相反,他们关闭这个定义它们的上下文。(他们也接近了论据如果相关,超级的.)这意味着它们内部与它们是在哪里创建的,并且无法更改。

  2. 正如您在上面注意到的那样,您没有使用关键字功能; 相反,您使用=>.

这个n=>n*2上面的例子是它们的一种形式。如果有多个参数传递函数,则使用parens:

变量a=[1,2,3];var b=a.map((n,i)=>n*i);console.log(b.join(“,”));//0, 2, 6

(记住阵列#映射将条目作为第一个参数传递,将索引作为第二个参数传递。)

在这两种情况下,函数体只是一个表达式;函数的返回值将自动成为该表达式的结果(您不需要使用显式返回).

如果您要做的不仅仅是一个表达式,请使用{}和一个显式返回(如果需要返回值),正常情况下:

变量a=[{第一个:“乔”,最后一个:“博客”},{第一个:“Albert”,最后一个:“Bloggs”},{第一个:“玛丽”,最后一个:“奥尔布赖特”}];a=a.sort((a,b)=>{var rv=a.last.localeCompare(b.last);如果(rv===0){rv=a.first.localeCompare(b.first);}返回rv;});console.log(JSON.stringify(a));

没有的版本{ ... }称为箭头函数,带有表达式体简洁的正文.(同时:A简洁的箭头功能。)那个有{ ... }定义主体是一个带有功能体.(同时:A冗长的箭头功能。)

对象初始化器中的方法声明(ES2015+)

ES2015允许以更短的形式声明引用名为方法定义; 它看起来像这样:

var o={foo(){}};

在ES5和更早版本中,几乎等效的是:

var o={foo:函数foo(){}};

不同之处(除了冗长)在于,方法可以使用超级的,但函数不能。例如,如果你有一个定义了的值使用方法语法,它可以使用super.valueOf()以获取值对象.原型.值可能会返回(在对其进行其他操作之前),而ES5版本则必须这样做Object.prototype.valueOf.call(this)而不是。

这也意味着该方法具有对其定义对象的引用,因此如果该对象是临时的(例如,您要将其传递到对象分配作为源对象之一),方法语法能够这意味着对象被保留在内存中,否则它可能会被垃圾收集(如果JavaScript引擎没有检测到这种情况,如果没有任何方法使用,则处理它超级的).

中的构造函数和方法声明(ES2015+)

ES2015带给我们语法,包括声明的构造函数和方法:

类人员{构造函数(firstName,lastName){this.firstName=名字;this.lastName=姓氏;}获取完整名称(){return this.firstName+“”+this.lastName;}}

上面有两个函数声明:一个用于构造函数,它获取名称,一个用于获取全名,这是分配给的函数人员.原型.

0
173

谈到全球背景变量语句和功能声明最后将创建一个不可删除的属性,但两者的值可以被覆盖.

这两种方法之间的细微差别在于,当变量实例化进程运行(在实际代码执行之前)用声明的所有标识符变量将用初始化未定义,以及功能声明的将从那时起可用,例如:

警报(foo类型);//'函数',它已经可用警报(条形图类型);//'未定义'函数foo(){}var bar=函数(){};警报(条形图类型);//'函数'

The assignment of the酒吧 函数表达式发生直到运行时。

由创建的全局属性功能声明可以像变量值一样无任何问题地覆盖,例如:

功能测试(){}test=空;

两个示例之间的另一个明显区别是,第一个函数没有名称,但第二个函数有名称,这在调试(即检查调用堆栈)时非常有用。

关于您编辑的第一个示例(foo=function(){alert('hello!');};),这是一项未声明的任务,我强烈建议您始终使用变量关键字。

有赋值,没有变量语句中,如果在作用域链中找不到引用的标识符,则它将成为可删除的全局对象的属性。

此外,未声明的赋值会引发引用错误在ECMAScript 5上严格模式.

A必须阅读:

注释:此答案已从合并另一个问题其中,OP的主要疑问和误解是,标识符声明带有功能声明,无法覆盖,但情况并非如此。

0
146

您在那里发布的两个代码片段的行为方式几乎完全相同。

然而,行为上的区别在于第一种变体(var functionOne=函数(){}),该函数只能在代码中的该点之后调用。

使用第二种变体(函数functionTwo()),函数可用于在声明函数的位置上方运行的代码。

这是因为对于第一个变量,函数被分配给变量foo公司在运行时。在第二种情况下,将函数分配给该标识符,foo公司,在解析时。

更多技术信息

JavaScript有三种定义函数的方法。

  1. 第一段代码显示了函数表达式。这涉及使用“函数”运算符创建函数-该运算符的结果可以存储在任何变量或对象属性中。这样函数表达式就很强大了。函数表达式通常称为“匿名函数”,因为它不必有名称,
  2. 你的第二个例子是函数声明。这使用“函数”语句创建函数。该函数在解析时可用,可以在该范围内的任何位置调用。稍后您仍可以将其存储在变量或对象属性中。
  3. 定义函数的第三种方法是“Function()”构造函数,它没有显示在您的原始帖子中。不建议使用此方法,因为它的工作方式与评估(),这有它的问题。
125

更好的解释格雷格的回答

函数Two();函数functionTwo(){}

为什么没有错误?我们总是被告知表达式是从上到下执行的(??)

因为:

始终移动函数声明和变量声明(已吊装)JavaScript解释器不可见到其包含范围的顶部。显然,函数参数和语言定义的名称已经存在。本樱桃

这意味着代码如下:

functionOne();---------------var functionOne;|实际上是|functionOne();var functionOne=函数(){|已解释|-->};                              |    like | functionOne=函数(){---------------      };

请注意,声明的赋值部分没有被挂起。只有名字被提起。

但在使用函数声明的情况下,整个函数体也将被提升:

函数Two();--------------函数functionTwo(){|实际上是};函数functionTwo(){|解释的|-->}|like|functionTwo();---------------
0
107

其他评论家已经谈到了上述两种变体的语义差异。我想指出一个风格上的差异:只有“赋值”变体才能设置另一个对象的属性。

我经常使用这样的模式构建JavaScript模块:

(函数(){var导出={};函数privateUtil(){...}exports.publicUtil=函数(){...};回流出口;})();

使用此模式,您的公共函数将全部使用赋值,而您的私有函数使用声明。

(还要注意,赋值应该在语句后要求使用分号,而声明禁止使用分号。)

91

当您需要避免覆盖函数先前的定义时,可以说明何时首选第一种方法而不是第二种方法。

使用

if(条件){函数myfunction(){//一些代码}}

,此定义我的功能将覆盖以前的任何定义,因为它将在解析时完成。

While期间

if(条件){var myfunction=函数(){//一些代码}}

定义的工作是否正确我的功能只有当条件满足。

0
72

一个重要的原因是添加一个且只有一个变量作为命名空间的“根”。。。

var MyNamespace={}MyNamespace.foo=函数(){}

var MyNamespace={foo:function(){},...}

名称空间有许多技术。随着大量可用的JavaScript模块的出现,它变得更加重要。

另请参见如何在JavaScript中声明名称空间?

0
64

吊装 是JavaScript解释器将所有变量和函数声明移动到当前范围顶部的操作。

然而,只有实际的申报被提起。把作业留在原处。

  • 页面内声明的变量/函数是全局的,可以在该页面的任何位置访问。
  • 函数内声明的变量/函数具有局部范围。表示它们在函数体(范围)内可用/访问,在函数体外不可用。

变量

Javascript被称为松散类型语言。这意味着Javascript变量可以保存任何数据类型Javascript会根据运行时提供的值/文字自动更改变量类型。

global_Page=10;变量global_Page;«未定义«整数文字,数字类型。-------------------global_Page=10;«编号global_Page='Yash';|已解释|global_Page='Yash';«字符串«字符串文字,字符串类型。«AS«global_Page=真;«布尔var global_Page=true;||global_Page=函数(){«函数«布尔类型-------------------var local_functionblock;«未定义global_Page=函数(){local_functionblock=777;«数字var本地功能块=777;};//将函数指定为数据。};

功能

函数标识符_opt(FormalParameterList_opt){FunctionBody|语句序列«回报;默认未定义«返回“一些数据”;}
  • 页面内声明的函数被提升到具有全局访问权限的页面顶部。
  • 函数块中声明的函数被提升到块的顶部。
  • 函数的默认返回值为“未定义',变量声明默认值也“未定义”

    功能块全局的范围。有关未定义页面的范围|不可用。

函数声明

函数globalAccess()}                                  -------------------     }globalAccess();||函数globalAccess(){«重新定义/覆盖。本地访问();«吊装为«功能本地访问(){函数globalAccess(){|}本地访问();-------------------localAccess();«函数仅在globalAccess()中使用访问。函数localAccess(){}}全局访问();}本地访问();«ReferenceError,因为函数未定义

函数表达式

10;                 «文字(10);                «表达式(10).toString()->“10”变量a;a=10;«表达式var a.toString()->“10”(函数invoke(){«表达式函数console.log(“自调用”);(函数(){});                                                               }) ()->“自调用”变量f;f=函数(){«表达式变量函数console.log('var函数');f()->“var函数”};

分配给变量的函数示例:

(函数selfExecuting(){console.log('IIFE-立即调用的函数表达式');}());var anonymous=函数(){console.log('匿名函数表达式');};var namedExpression=InternalUSE函数(事实){if(事实===1){返回1;}var localExpression=函数(){console.log(“父函数范围的本地”);};globalExpression=函数(){log('创建一个新的全局变量,然后分配这个函数。');};//回报//未定义。为_InternalUSE返回事实*(事实-1);};namedExpression();全局表达式();

javascript解释为

var匿名;var namedExpression;var globalExpression;anonymous=函数(){console.log('匿名函数表达式');};namedExpression=InternalUSE函数(事实){var localExpression;if(事实===1){返回1;}localExpression=函数(){console.log(“本地到父函数范围”);};globalExpression=函数(){log('创建一个新的全局变量,然后分配这个函数。');};为_InternalUSE返回事实*(事实-1);//默认未定义。};命名表达式(10);globalExpression();

您可以使用jsperf测试运行器


ES5构造函数函数类:使用Function.prototype.bind创建的函数对象

JavaScript将函数视为一级对象,因此作为一个对象,您可以为函数分配属性。

函数形状(id){//函数声明this.id=id;};//向函数添加原型方法。Shape.prototype.getID=函数(){返回this.id;};Shape.prototype.setID=函数(id){this.id=id;};var expFn=形状;//函数表达式var funObj=新形状();//Function对象funObj.hasOwnProperty('原型');//funObj.setID(10);console.log(funObj.getID());//10

引入ES6箭头功能:箭头函数表达式的语法较短,最适合非方法函数,并且不能用作构造函数。

ArrowFunction:ArrowParameters=>ConciseBody.

const-fn=(item)=>{return item&1?“奇数”:“偶数”;};控制台.log(fn(2));//偶数控制台.log(fn(3));//奇数
0
53

𝗧𝗵𝗲𝗿𝗲 𝗮𝗿𝗲 𝗳𝗼𝘂𝗿 𝗻𝗼𝘁𝗲𝘄𝗼𝗿𝘁𝗵𝘆 𝗰𝗼𝗺𝗽𝗮𝗿𝗶𝘀𝗼𝗻𝘀 𝗯𝗲𝘁𝘄𝗲𝗲𝗻 𝘁𝗵𝗲 𝘁𝘄𝗼 𝗱𝗶𝗳𝗳𝗲𝗿𝗲𝗻𝘁 𝗱𝗲𝗰𝗹𝗮𝗿𝗮𝘁𝗶𝗼𝗻𝘀 𝗼𝗳 𝗳𝘂𝗻𝗰𝘁𝗶𝗼𝗻𝘀 𝗮𝘀 𝗹𝗶𝘀𝘁𝗲𝗱 𝗯𝗲𝗹𝗼𝘄.

  1. 功能的可用性(范围)

以下工作是因为函数add()作用域为最近的块:

尝试{console.log(“成功:”,添加(1,1));}捕捉(e){console.log(“错误:”+e);}函数加法(a,b){返回a+b;}

以下操作不起作用,因为在将函数值赋给变量之前调用了该变量添加.

尝试{console.log(“成功:”,添加(1,1));}捕捉(e){console.log(“错误:”+e);}var add=函数(a,b){返回a+b;}

上面的代码在功能上与下面的代码相同。请注意,显式分配add=未定义是多余的,因为只是做var添加;var add=未定义.

var add=未定义;尝试{console.log(“成功:”,添加(1,1));}捕捉(e){console.log(“错误:”+e);}add=函数(a,b){返回a+b;}

以下操作不起作用,因为var添加=开始一个表达式并导致以下结果函数add()是表达式而不是块。命名函数仅对其自身及其周围的块可见。作为函数add()这里是一个表达式,它没有周围的块,所以它只对自身可见。

尝试{console.log(“成功:”,添加(1,1));}捕捉(e){console.log(“错误:”+e);}var add=函数add(a,b){返回a+b;}

  1. (功能).name(名称)

函数的名称函数thefuncname(){}办公室名称当它以这种方式声明时。

函数foobar(a,b){}console.log(foobar.name);

var a=函数foobar(){};控制台.log(a.name);

否则,如果函数声明为函数(){},的功能.name是用于存储函数的第一个变量。

变量a=函数(){};var b=(函数(){return function(){}});控制台.log(a.name);控制台.log(b.name);

如果没有为函数设置变量,则函数名为空字符串("").

console.log((function(){}).name===“”);

最后,当函数所赋的变量最初设置名称时,设置给函数的连续变量不会更改名称。

变量a=函数(){};var b=a;var c=b;console.log(a.name);控制台.log(b.name);控制台.log(c.name);

  1. 性能

在Google的V8和Firefox的Spidermonkey中,JIT编译可能会有几微秒的差异,但最终结果是完全相同的。为了证明这一点,让我们通过比较两个空白代码段的速度来检查JSPerf在微基准测试中的效率。这个JSPerf测试位于此处.并且这里有jsben.ch测试正如你所看到的,当应该没有差异时,会有明显的差异。如果你真的像我一样是一个性能怪胎,那么在减少范围内变量和函数的数量,特别是消除多态性(例如使用相同的变量存储两种不同的类型)时,你可能更值得一试。

  1. 可变变异性

当您使用变量关键字来声明变量,然后可以像这样为变量重新分配不同的值。

(函数(){“使用严格”;var foobar=函数(){};//初始值尝试{foobar=“你好,世界!”;//新值console.log(“[no error]”);}捕获(错误){console.log(“ERROR:”+ERROR.message);}console.log(foobar,window.foobar);})();

然而,当我们使用常量语句时,变量引用变为不可变。这意味着我们不能给变量赋值。但是,请注意,这并不会使变量的内容不可变:如果是这样的话常量arr=[],那么你仍然可以arr[10]=“示例”。只做一些类似的事情arr=“新值”arr=[]将抛出如下所示的错误。

(函数(){“使用严格”;const foobar=函数(){};//初始值尝试{foobar=“你好,世界!”;//新值console.log(“[no error]”);}捕获(错误){console.log(“ERROR:”+ERROR.message);}console.log(foobar,window.foobar);})();

有趣的是,如果我们将变量声明为函数funcName(){},则变量的不变性与使用声明它相同变量.

(函数(){“使用严格”;函数foobar(){};//初始值尝试{foobar=“你好,世界!”;//新值console.log(“[no error]”);}捕获(错误){console.log(“ERROR:”+ERROR.message);}console.log(foobar,window.foobar);})();

𝗪𝗵𝗮𝘁 𝗜𝘀 𝗧𝗵𝗲 "𝗡𝗲𝗮𝗿𝗲𝘀𝘁 𝗕𝗹𝗼𝗰𝗸"

“最近的块”是最近的“函数”(包括异步函数、生成器函数和异步生成器函数)。然而,有趣的是函数functionName(){}表现得像var functionName=函数(){}当在非闭包块外的项目表示闭包时。观察。

  • 正常var add=函数(){}

尝试{//如果变量不存在,typeof只会返回“undefined”if(typeof add!==“undefined”){添加(1,1);//只是为了证明console.log(“非块”);}else if(add===未定义){//如果add不存在,则抛出异常console.log('Behaves like var add=function(a,b){return a+b});}}捕获(e){console.log(“是块”);}var add=函数(a,b){return a+b}

  • 正常函数add(){}

尝试{//如果变量不存在,typeof只会返回“undefined”if(typeof add!==“undefined”){添加(1,1);//只是为了证明console.log(“非块”);}else if(add===未定义){//如果add不存在,则抛出异常log('行为类似于var add=function(a,b){return a+b}')}}捕获(e){console.log(“是块”);}函数加法(a,b){返回a+b;}

  • 功能

尝试{//如果变量不存在,typeof只会返回“undefined”if(typeof add!==“undefined”){添加(1,1);//只是为了证明console.log(“非块”);}else if(add===未定义){//如果add不存在,则抛出异常log('行为类似于var add=function(a,b){return a+b}')}}捕捉(e){console.log(“是块”);}(函数(){函数加法(a,b){返回a+b;}})();

  • 声明(例如如果其他的对于虽然尝试/抓住/最后转换/虽然具有)

尝试{//如果变量不存在,typeof只会返回“undefined”if(typeof add!==“undefined”){添加(1,1);//只是为了证明console.log(“非块”);}else if(add===未定义){//如果add不存在,则抛出异常log('行为类似于var add=function(a,b){return a+b}')}}捕捉(e){console.log(“是块”);}{函数加法(a,b){返回a+b;}}

  • 带有的箭头函数var add=函数()

尝试{//如果变量不存在,typeof只会返回“undefined”if(typeof add!==“undefined”){添加(1,1);//只是为了证明console.log(“非块”);}else if(add===未定义){//如果add不存在,则抛出异常log('行为类似于var add=function(a,b){return a+b}')}}捕捉(e){console.log(“是块”);}(()=>{var add=函数(a,b){返回a+b;}})();

  • 箭头函数函数add()

尝试{//如果变量不存在,typeof只会返回“undefined”if(typeof add!==“undefined”){添加(1,1);//只是为了证明console.log(“非块”);}else if(add===未定义){//如果add不存在,则抛出异常log('行为类似于var add=function(a,b){return a+b}')}}捕捉(e){console.log(“是块”);}(() => {函数加法(a,b){返回a+b;}})();

0
50

我添加了我自己的答案,因为其他人都已经彻底覆盖了吊装部分。

很长一段时间以来,我一直想知道哪种方式更好,多亏了http://jsperf.com现在我知道了:)

在此处输入图像描述

函数声明速度更快,这才是web开发真正重要的,对吗?;)

1
41

一旦建立了绑定,函数声明和分配给变量的函数表达式的行为是相同的。

然而,在怎样什么时候函数对象实际上与其变量相关联。这种差异是由于称为变量提升在JavaScript中。

基本上,所有函数声明和变量声明都被提升到功能声明出现在其中(这就是为什么我们说JavaScript有功能范围).

  • 提升函数声明时,函数体“跟随”因此,当函数体被求值时,变量将立即绑定到函数对象。

  • 挂起变量声明时,初始化会跟随,但被“甩在后面”。变量初始化为未定义在函数体的开头,将是分配位于代码中原始位置的值。(实际上,它将被赋值为每一个名称相同的变量声明所在的位置。)

提升的顺序也很重要:函数声明优先于同名的变量声明,最后一个函数声明优先于前一个同名的函数声明。

一些示例。。。

变量foo=1;功能栏(){if(!foo){变量foo=10}返回foo;}bar()//10

变量foo公司被提升到函数顶部,初始化为未定义,所以foo公司真的,所以foo公司已分配10. Thefoo公司的外部酒吧的范围不起任何作用,并且未被触及。

函数f(){返回a;函数a(){return 1};变量a=4;函数a(){return 2}}f()()//2函数f(){返回a;变量a=4;函数a(){return 1};函数a(){return 2}}f()()//2

函数声明优先于变量声明,最后一个函数声明“坚持”。

函数f(){变量a=4;函数a(){return 1};函数a(){return 2};返回a;}f()//4

在这个例子中用第二个函数声明求值得到的函数对象初始化,然后赋值4.

变量a=1;函数b(){a=10;回报;函数a(){}}b();a//1号

这里首先提升函数声明,声明并初始化变量。接下来,分配此变量10换句话说:赋值不赋值给外部变量.

0
38

第一个示例是函数声明:

函数abc(){}

第二个示例是函数表达式:

var abc=函数(){};

主要区别在于它们的吊装方式(吊装和申报)。在第一个示例中,整个函数声明被挂起。在第二个示例中,只提升了var“abc”,其值(函数)将未定义,函数本身仍保持在声明的位置。

简单地说:

//这会管用的abc(参数);函数abc(){}//这将失败abc(参数);var abc=函数(){}

为了进一步研究这个话题,我强烈建议你链接

0
37

就代码维护成本而言,命名函数更可取:

  • 独立于声明地点(但仍受范围限制)。
  • 更能抵抗条件初始化之类的错误(如果需要,您仍然可以重写)。
  • 通过分别分配本地函数和范围功能,代码变得更加可读。通常在作用域中,功能优先,然后是本地函数的声明。
  • 在调试器中,您将清楚地看到调用堆栈上的函数名,而不是“匿名/已评估”函数。

我怀疑以下是命名函数的更多PROS。命名函数的优点是匿名函数的缺点。

从历史上看,由于JavaScript语言无法列出具有命名函数的成员,因此出现了匿名函数:

{member:function(){/*如何使“this.member”成为命名函数*/}}
0
33

Greg的回答已经足够好了,但我仍然想补充一些我刚才在观看时学到的东西道格拉斯·克罗福德视频。

函数表达式:

var foo=函数foo(){};

函数语句:

函数foo(){};

函数语句只是变量带有的语句功能值。

所以

函数foo(){};

扩展到

var foo=函数foo(){};

它进一步扩展到:

var foo=未定义;foo=函数foo(){};

它们都被提升到了代码的顶部。

视频截图

0
33

在计算机科学术语中,我们讨论匿名函数和命名函数。我认为最重要的区别是匿名函数没有绑定到名称,因此命名为匿名函数。在JavaScript中,它是在运行时动态声明的第一类对象。

有关匿名函数和lambda演算的更多信息,维基百科是一个很好的开端:匿名函数.

0
32

我在代码中使用变量方法是出于一个非常具体的原因,上面以抽象的方式介绍了它的理论,但一个示例可能会帮助像我这样的一些JavaScript专业知识有限的人。

我有需要与160个独立设计的品牌一起运行的代码。大多数代码都在共享文件中,但品牌特定的内容在单独的文件中,每个品牌对应一个文件。

有些品牌需要特定的功能,有些则不需要。有时我必须添加新的功能来完成新的品牌特定的事情。我很高兴更改共享代码,但我不想更改所有160组品牌文件。

通过使用变量语法,我可以在共享代码中声明变量(本质上是函数指针),然后分配一个简单的存根函数,或者设置为null。

需要特定函数实现的一两个品牌可以定义其函数版本,并根据需要将其分配给变量,其他品牌则什么都不做。在共享代码中执行空函数之前,我可以测试它。

从上面人们的评论来看,我认为可能也可以重新定义静态函数,但我认为变量解决方案很好且清晰。

0
26

@尤根·拉扎特金举了一个例子指定一个可以使用的指定函数快捷方式()作为对自身的内部引用。作者给出了另一个示例-复制分配给另一个对象的递归函数在他的学习高级Javascript教程。虽然将函数分配给属性并不是严格意义上的问题,但我建议您积极尝试教程,通过单击右上角的按钮来运行代码,然后双击代码进行编辑。

本教程中的示例:中的递归调用大喊():

当原始忍者对象被移除时,测试失败。(第13页)

函数断言(谓词,消息){if(!谓词){throw new Error(消息);}}var忍者={大叫:函数(n){返回n>0?忍者叫喊(n-1)+“a”:“hiy”;}};断言(ninja.yell(4)==“hiyaaaa”,“单个对象也不太糟糕。”);var武士={yell:ninja.yell};var忍者=空;尝试{武士大喊(4);}捕获(e){断言(false,“嗯,这不好!忍者吼去哪里了?”);}

如果您命名将要递归调用的函数,测试将通过。(第14页)

函数断言(谓词,消息){if(!谓词){throw new Error(消息);}}var忍者={yell:函数yell(n){返回n>0?大喊(n-1)+“a”:“hiy”;}};断言(ninja.yell(4)==“hiyaaaa”,“按照我们的预期工作!”);var武士={yell:ninja.yell};var忍者={};assert(samurai.ellle(4)==“hiyaaaa”,“方法正确地调用自己。”);控制台.log(武士吼叫(4));

0
21

其他答案中没有提到的另一个区别是,如果使用匿名函数并将其用作构造函数,如

var functionOne=函数(){//一些代码};var one=新函数一();console.log(one.constructor.name);

然后一个结构名称将不会定义。功能名称是非标准的,但受Firefox、Chrome、其他Webkit衍生浏览器和IE 9+支持。

使用

函数functionTwo(){//一些代码}two=新函数two();

可以将构造函数的名称作为字符串检索two.constructor.name(双结构名称).

0
20

第一个(函数doSomething(x))应该是对象符号的一部分。

第二个(var doSomething=函数(x){alert(x);})就是简单地创建一个匿名函数并将其分配给一个变量,做点什么。因此doSomething()将调用该函数。

你可能想知道函数声明函数表达式是。

函数声明定义了一个命名的函数变量,而不需要变量赋值。函数声明作为独立的构造出现,不能嵌套在非函数块中。

函数foo(){返回3;}

ECMA 5(13.0)将语法定义为
函数标识符(FormalParameterList选择){函数体}

在上述条件下,函数名在其作用域和其父函数的作用域内可见(否则将无法访问)。

在函数表达式中

函数表达式将函数定义为较大表达式语法的一部分(通常是变量赋值)。通过函数表达式定义的函数可以是命名的或匿名的。函数表达式不应以“Function”开头。

//匿名函数表达式var a=函数(){返回3;}//命名函数表达式var a=函数foo(){返回3;}//自调用函数表达式(函数foo(){警报(“hello!”);})();

ECMA 5(13.0)将语法定义为
函数标识符选择(形式参数列表选择){函数体}

0
19

我列出了以下差异:

  1. 函数声明可以放在代码中的任何位置。即使在定义出现在代码中之前调用它,它也会在函数声明提交到内存时执行,或者在页面中的任何其他代码开始执行之前以某种方式被提升。

    看看下面的函数:

    函数outerFunction(){函数foo(){返回1;}return foo();函数foo(){返回2;}}警报(outerFunction());//显示器2

    这是因为在执行过程中,它看起来像:-

    function foo(){//将第一个函数声明移到顶部返回1;}function foo(){//第二个函数声明被移到顶部返回2;}函数outerFunction(){return foo();}警报(outerFunction())//所以从上到下执行,//最后一个foo()返回显示的2

    函数表达式如果在调用之前未定义,将导致错误。此外,这里的函数定义本身不会像函数声明中那样移到顶部或提交到内存中。但我们将函数赋给的变量被提升并未定义被分配给它。

    使用函数表达式的相同函数:

    函数outerFunction(){var foo=函数(){返回1;}return foo();var foo=函数(){返回2;}}警报(outerFunction());//显示1

    这是因为在执行过程中,它看起来像:

    函数outerFunction(){var foo=未定义;var foo=未定义;foo=函数(){返回1;};返回foo();foo=function(){//此函数表达式不可访问返回2;};}警报(outerFunction());//显示1
  2. 在非函数块中编写函数声明是不安全的,例如如果因为他们无法接近。

    if(测试){函数x(){doSomething();}}
  3. 像下面这样的命名函数表达式可能无法在9版之前的Internet Explorer浏览器中使用。

    var today=函数today(){return new Date()}
0
15

关于性能:

的新版本V8发动机引入了几个在后台运行的优化蜘蛛猴.

现在,表达式和声明之间几乎没有区别。
函数表达式似乎更快了现在。

铬62.0.3202 铬测试

FireFox火狐55 Firefox测试

铬金丝雀63.0.3225 铬金丝雀试验


匿名函数表达式表现似乎更好反对命名函数表达式。


Firefox浏览器 Firefox命名_匿名 铬金丝雀 铬金丝雀名_匿名 Chrome命名_匿名

5
  • 1
    结果差异太小,不能将其视为差异。如果你进行100次测试,你会得到100个结果。 评论 2020年10月14日12:59
  • @RonnySherer,你熟悉jsperf吗?测试是在运行了1000多万次之后进行的! 评论 2020年10月14日14:44
  • 1
    每种测量都有干扰。计算机的状态不同,这不是计算机上运行的唯一进程。当差异如此之小时,这意味着你不能依赖它,事实上它是一样的。试着一个接一个地运行10次正常测试,你会发现数字不同。很接近,但不一样。 评论 2020年10月17日21:30
  • @RonnySherer js-perf创建了一个虚拟环境,特别是用于考虑具有这些小差异的进程。它没有在我的电脑上运行。它只运行于此。当事情这么小的时候,也许有人根本不应该在乎。但我总是正确计算并报告它。如果有人想在数十亿次迭代的循环中使用它,那么他应该选择性能最佳的函数。 评论 2020年10月18日13:09
  • 1
    虚拟环境位于服务器上,该服务器可能会执行其他操作。我做了一些测试。结果从来都不完全一样。 评论 2020年10月21日14:54
14

如果您要使用这些函数来创建对象,您将得到:

var objectOne=新函数一();console.log(objectOne.__proto__);//打印“Object{}”,因为构造函数是匿名函数var objectTwo=新函数Two();console.log(objectTwo.__proto__);//打印“functionTwo{}”,因为构造函数是一个命名函数
14

命名函数与匿名函数

第一个函数语法是匿名函数表达式:

var functionOne=函数(){//做点什么。。。};

而第二个是函数声明:

函数functionTwo(){//做点什么。。。}

两者之间的主要区别是函数名,因为匿名函数没有名字可以叫。匿名函数快速且易于声明,许多库和工具倾向于鼓励这种惯用的代码风格。然而,匿名函数有一些缺点:

  • 可读性:匿名函数省略了一个可能导致代码可读性降低的名称。

  • 调试:匿名函数在堆栈跟踪中没有名称,这会使调试更加困难。

  • 自我参考:如果函数需要引用自身,比如递归,该怎么办。

命名函数表达式

为函数表达式提供名称可以有效地解决所有这些缺点,并且没有明显的缺点。最佳实践是始终为函数表达式命名:

setTimeout(函数timeHandler(){//<--看,这里有个名字!console.log(“我已经等了一秒钟”);}, 1000);

命名IIFE(立即调用函数表达式)

(函数IIFE(str){//<--看,总是命名IIFE!console.log(str);//“你好!”})(“你好!”);

对于分配给变量的函数,在这种情况下,命名函数并不常见,可能会引起混淆,在这种情形下,箭头函数可能是更好的选择。

13

在JavaScript中,有两种创建函数的方法:

  1. 函数声明:

    函数fn(){console.log(“Hello”);}fn();

    这是一个非常基本的、自解释的语言,在许多语言中使用,并且是C语言家族的标准。我们声明了一个定义它的函数,并通过调用它来执行它。

    你应该知道的是,函数实际上是JavaScript中的对象;在内部,我们为上面的函数创建了一个对象,并给它一个名为fn的名称,或者对该对象的引用存储在fn中。函数是JavaScript中的对象;函数的实例实际上是一个对象实例。

  2. 函数表达式:

    var fn=函数(){console.log(“Hello”);}fn();

    JavaScript具有一级函数,即创建函数并将其分配给变量,就像创建字符串或数字并将其指定给变量一样。这里,将fn变量分配给函数。这个概念的原因是函数是JavaScript中的对象;fn指向上述函数的对象实例。我们已经初始化了一个函数并将其赋给了一个变量。它不是在执行函数和分配结果。

参考:JavaScript函数声明语法:var fn=function(){}vs function fn(){{}

12

根据“命名函数显示在堆栈跟踪中”参数,现代JavaScript引擎实际上能够表示匿名函数。

在撰写本文时,V8、SpiderMonkey、Chakra和Nitro总是按名称引用命名函数。如果匿名函数有标识符,它们几乎总是通过它的标识符来引用它。

SpiderMonkey可以找出从另一个函数返回的匿名函数的名称。其余的都不行。

如果您真的、真的希望迭代器和成功回调出现在跟踪中,那么也可以为它们命名。。。

[].forEach(函数迭代器(){});

但在大多数情况下,这不值得过分强调。

线束(小提琴)

'使用严格';var a=函数(){抛出新错误();},b=函数b(){抛出新错误();},c=函数d(){抛出新错误();},e={f: a、,g: b中,h: c、,i: 函数(){抛出新错误();},j: 函数j(){抛出新错误();},k: 函数l(){抛出新错误();}},m=(函数(){返回函数(){抛出新错误();};}()),n=(函数(){返回函数n(){抛出新错误();};}()),o=(函数(){返回函数p(){抛出新错误();};}());console.log([a,b,c].concat(Object.keys(e).reduce(function(values,key)){返回值.concat(e[key]);},[])).concat([m,n,o]).reduce(函数(logs,func){尝试{func();}捕获(错误){return logs.concat('函数名称:'+函数名称+'\n'+'跟踪:\n'+error.stack);//需要在Nitro中手动记录错误对象。}},[]).join('\n\n');

V8发动机

功能名称:跟踪:错误位于(http://localhost:8000/test.js:4:11)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:b跟踪:错误在b(http://localhost:8000/test.js:7:15)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:d跟踪:错误在d(http://localhost:8000/test.js:10:15)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:跟踪:错误位于(http://localhost:8000/test.js:4:11)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:b跟踪:错误在b(http://localhost:8000/test.js:7:15)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:d跟踪:错误在d(http://localhost:8000/test.js:10:15)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27函数名称:跟踪:错误在e.i(http://localhost:8000/test.js:17:19)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:j跟踪:错误在j(http://localhost:8000/test.js:20:19)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:l跟踪:错误在l(http://localhost:8000/test.js:23:19)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:跟踪:错误http://localhost:8000/test.js:28:19http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:n跟踪:错误在n(http://localhost:8000/test.js:33:19)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27功能名称:p跟踪:错误在p(http://localhost:8000/test.js:38:19)http://localhost:8000/test.js:47:9位于Array.reduce(本机)http://localhost:8000/test.js:44:27test.js:42

蜘蛛猴

函数名称:跟踪:一个@http://localhost:8000/test.js:4:5@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:b跟踪:b条@http://localhost:8000/test.js:7:9@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:d跟踪:d日@http://localhost:8000/test.js:10:9@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:跟踪:一个@http://localhost:8000/test.js:4:5@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:b跟踪:b条@http://localhost:8000/test.js:7:9@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:d跟踪:d日@http://localhost:8000/test.js:10:9@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:跟踪:网址:http://localhost:8000/test.js:17:13@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:j跟踪:j个@http://localhost:8000/test.js:20:13@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:l跟踪:我@http://localhost:8000/test.js:23:13@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:跟踪:米</<@http://localhost:8000/test.js:28:13@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1函数名称:n跟踪:n个@http://localhost:8000/test.js:33:13@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1功能名称:p跟踪:第页@http://localhost:8000/test.js:38:13@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1

脉轮

func.name:未定义跟踪:错误位于(http://localhost:8000/test.js:4:5)在匿名函数(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在b(http://localhost:8000/test.js:7:9)在匿名函数(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在d(http://localhost:8000/test.js:10:9)在匿名函数处(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误位于(http://localhost:8000/test.js:4:5)在匿名函数(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在b(http://localhost:8000/test.js:7:9)在匿名函数(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在d(http://localhost:8000/test.js:10:9)在匿名函数(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在e.i(http://localhost:8000/test.js:17:13)在匿名函数(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在j(http://localhost:8000/test.js:20:13)在匿名函数(http://localhost:8000/test.js:47:9)在全局代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在l(http://localhost:8000/test.js:23:13)在匿名函数(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在匿名函数(http://localhost:8000/test.js:28:13)在匿名函数处(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在n处(http://localhost:8000/test.js:33:13)在匿名函数(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)func.name:未定义跟踪:错误在p(http://localhost:8000/test.js:38:13)在匿名函数(http://localhost:8000/test.js:47:9)在全球代码(http://localhost:8000/test.js:42:1)

硝基

功能名称:跟踪:一个@http://localhost:8000/test.js:4:22http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33函数名称:b跟踪:b@http://localhost:8000/test.js:7:26http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33功能名称:d跟踪:d日@http://localhost:8000/test.js:10:26http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33功能名称:跟踪:一个@http://localhost:8000/test.js:4:22http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33功能名称:b跟踪:b条@http://localhost:8000/test.js:7:26http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33函数名称:d跟踪:d日@http://localhost:8000/test.js:10:26http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33函数名称:跟踪:我@http://localhost:8000/test.js:17:30http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33功能名称:j跟踪:j个@http://localhost:8000/test.js:20:30http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33功能名称:l跟踪:我@http://localhost:8000/test.js:23:30http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33功能名称:跟踪:http://localhost:8000/test.js:28:30http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33功能名称:n跟踪:n个@http://localhost:8000/test.js:33:30http://localhost:8000/test.js:47:13reduce@[本机代码]全球性的代码@http://localhost:8000/test.js:44:33功能名称:p跟踪:第页@http://localhost:8000/test.js:38:30http://localhost:8000/test.js:47:13reduce@[本机代码]全球的代码@http://localhost:8000/test.js:44:33
12

两者都是定义函数的不同方式。区别在于浏览器如何解释并将其加载到执行上下文中。

第一种情况是函数表达式,它仅在解释器到达该代码行时加载。因此,如果您像下面这样做,您将得到一个错误,即functionOne不是函数.

functionOne();var functionOne=函数(){//一些代码};

原因是在第一行没有为functionOne赋值,因此它是未定义的。我们试图将其作为函数调用,因此出现了一个错误。

在第二行,我们将匿名函数的引用分配给functionOne。

第二种情况是在执行任何代码之前加载的函数声明。因此,如果您喜欢下面的操作,那么在代码执行之前加载声明时,不会出现任何错误。

functionOne();函数functionOne(){//一些代码}
11

它们非常相似,但有一些小的区别,第一个是分配给匿名函数的变量(函数声明),第二个是用JavaScript(匿名函数声明)创建函数的常规方法,两者都有用法、优点和缺点:

1.函数表达式

var functionOne=函数(){//一些代码};

函数表达式将函数定义为表达式语法(通常是变量赋值)。功能通过函数定义的表达式可以是命名的或匿名的。功能表达式不能以“function”开头(因此括号围绕下面的自调用示例)。

将变量赋给函数,意味着没有提升,正如我们所知,JavaScript中的函数可以提升,意味着可以在声明之前调用它们,而需要在访问变量之前声明变量,所以在这种情况下,我们不能在声明之前访问函数,也可以通过这种方式编写函数,对于返回另一个函数的函数,这种声明是有意义的,在ECMA6和上面,您可以将其分配给一个箭头函数,该函数可用于调用匿名函数,而且这种声明方式是在JavaScript中创建Constructor函数的更好方法。

2.功能声明

函数functionTwo(){//一些代码}

函数声明定义了一个命名函数变量需要变量赋值。函数声明如下所示独立构造,不能嵌套在非功能块中。将它们视为变量声明的兄弟姐妹是很有帮助的。正如变量声明必须以“var”开头一样,Function声明必须以“function”开头。

这是在JavaScript中调用函数的正常方式,甚至可以在JavaScript中将其声明为所有函数都被提升之前调用此函数,但如果您使用“use strict”,则不会像预期的那样提升,这是调用所有行数不太大且也不是构造函数的正常函数的好方法。

此外,如果您需要有关JavaScript中吊装工作原理的更多信息,请访问以下链接:

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting网站

8

这只是声明函数的两种可能方法,第二种方法是在声明之前使用函数。

0

不是你想要的答案吗?浏览标记的其他问题问自己的问题.