var functionName = function(){}与function functionName(){}


6871

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

以前的开发人员使用了两种方法来声明函数,如果有背后的原因,我将无法解决。

两种方式是:

var functionOne = function() {
    // Some code
};
function functionTwo() {
    // Some code
}

使用这两种不同方法的原因是什么,每种方法的利弊是什么?有什么方法可以用另一种方法不能完成的?


198
permadi.com/tutorial/jsFunc/index.html是关于javascript函数的非常不错的页面
uzay95'4

67
相关的是这篇关于命名函数表达式的出色文章。
Phrogz

12
@CMS引用了这篇文章:kangax.github.com/nfe/#expr-vs-decl
2011年

106
您需要注意两件事:#1在JavaScript中,声明被提升。这意味着var a = 1; var b = 2;var a; var b; a = 1; b = 2。因此,当您声明functionOne时,将对其进行声明,但不会立即设置其值。由于functionTwo只是一个声明,因此将其放在范围的顶部。#2 functionTwo可让您访问name属性,这在尝试调试某些内容时会很有帮助。
xavierm02 '02

65
哦,顺便说一句,正确的语法是带有“;” 分配后且声明后不包含。例如,function f(){}VS var f = function(){};
xavierm02 '02 / 08/21

Answers:


5042

区别在于它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


631
@Greg:顺便说一句,不同之处不仅在于它们在不同的时间被解析。本质上,您functionOne只是一个分配了匿名函数的变量,而functionTwo实际上是一个命名函数。呼吁.toString()双方看到的区别。在某些情况下,这是很重要的,您希望以编程方式获取函数的名称。
詹森·邦廷

6
@Jason Bunting ..不确定您在这里得到什么,.toString()似乎为两个返回基本上相同的值(函数定义): cl.ly/2a2C2Y1r0J451o0q0B1B
Jon z

124
两者都有。第一个是a function expression,第二个是a function declaration。:你可以阅读更多的话题在这里javascriptweblog.wordpress.com/2010/07/06/...
米哈尔Kuklis

127
@Greg您的答案中有关解析时间与运行时间的部分不正确。在JavaScript中,函数声明不是在解析时定义的,而是在运行时定义的。过程如下:解析源代码->评估JavaScript程序->初始化全局执行上下文->执行声明绑定实例化。在此过程中,将实例化函数声明(请参见第10.5章的第 5步)。
森那维达斯

102
这种现象的术语称为提升。
柯林·皮尔

1941

首先,我想更正Greg:function abc(){}也有作用域-名称abc是在遇到该定义的作用域中定义的。例:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

其次,可以将两种样式结合起来:

var xyz = function abc(){};

xyz将与往常一样abc定义,在所有浏览器中均未定义,但Internet Explorer除外-不要依赖于已定义。但它将在其内部定义:

var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

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

function abc(){};
var xyz = abc;

在这种情况下,xyz和和abc都是同一对象的别名:

console.log(xyz === abc); // prints "true"

使用组合样式的一个令人信服的原因是功能对象的“名称”属性(Internet Explorer不支持)。基本上,当您定义类似

function abc(){};
console.log(abc.name); // prints "abc"

它的名称是自动分配的。但是当你定义它像

var abc = function(){};
console.log(abc.name); // prints ""

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

使用组合样式的另一个好理由是使用一个简短的内部名称来引用自身,同时为外部用户提供一个长而不会冲突的名称:

// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively:
  shortcut(n - 1);
  // ...
  // Let it pass itself as a callback:
  someFunction(shortcut);
  // ...
}

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

(引用自身的另一种方法是使用arguments.callee,它仍然相对较长,并且在严格模式下不受支持。)

内心深处,JavaScript对这两个语句的处理方式有所不同。这是一个函数声明:

function abc(){}

abc 在当前范围的任何地方都定义了以下内容:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

此外,它还通过以下return声明进行了挂起:

// We can call it here
abc(); // Works
return;
function abc(){}

这是一个函数表达式:

var xyz = function(){};

xyz 这是从分配的角度定义的:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

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

有趣的事实:

var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"

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

var abc = function(){};

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

abc = function(){};

我知道我是在全局范围内定义的,前提是我没有abc在范围链中的任何地方定义。即使在内部使用,这种定义样式也具有弹性eval()。而定义

function abc(){};

取决于上下文,并且可能让您猜测它的实际定义位置,尤其是在以下情况下eval()-答案是:它取决于浏览器。


69
我指的是RoBorg,但无处可寻。简单:RoBorg === Greg。这就是在互联网时代可以重写历史的方式。;-)
Eugene Lazutkin

10
var xyz =函数abc(){}; console.log(xyz === abc); 我测试过的所有浏览器(Safari 4,Firefox 3.5.5,Opera 10.10)都给我“未定义的变量:abc”。
NVI

7
总的来说,我认为这篇文章很好地解释了使用函数声明的区别和优势。我同意在使用函数表达式赋值给变量的好处方面不同意,尤其是因为“好处”似乎是声明全局实体的提倡...而且每个人都知道您不应该使全局命名空间混乱, 对?;-)
natlee75

83
imo使用命名函数的一个重要原因是调试器可以使用该名称来帮助您理解调用堆栈或堆栈跟踪。当您查看调用堆栈并看到“匿名函数”深10级时,它很烂……
山羊2014年

3
var abc = function(){}; console.log(abc.name);不再产生"",而是产生"abc"
Qwerty

632

这是创建函数的标准表单的摘要:(本来是为另一个问题而写的,但是在移入规范问题后进行了修改。)

条款:

快速清单:

  • 功能声明

  • “匿名” 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,该规范并没有涵盖,如果你把一个函数声明等的控制结构内部的JavaScript引擎应该做的事情tryifswitchwhile,等等,是这样的:

if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}

而且由于它们是运行分步代码之前进行处理的,所以要知道它们在控制结构中时该怎么做是很棘手的。

尽管直到2015 年才指定执行此操作,但这是允许的扩展,以支持块中的函数声明。不幸的是(不可避免),不同的引擎做了不同的事情。

从ES2015开始,该规范说明了怎么做。实际上,它提供了三个独立的操作:

  1. 如果松散模式不是在Web浏览器,JavaScript引擎是应该做的一件事
  2. 如果在网络浏览器上处于松散模式,则JavaScript引擎应该做其他事情
  3. 如果处于严格模式(是否使用浏览器),则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仍在广泛使用,因此,将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.definePropertyObject.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()通话中了吗?这是一个功能。

关于箭头功能的几件事:

  1. 他们没有自己的this。相反,他们关闭了this他们定义成背景。(它们还会关闭,arguments并在需要时显示super。)这意味着this它们中的内容与this创建它们的位置相同,并且无法更改。

  2. 正如您在上述内容中所注意到的那样,您无需使用关键字function; 而是使用=>

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

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));

没有版本的版本{ ... }称为带有表达式主体简洁主体的箭头函数。(也:简洁的箭头函数。){ ... }定义主体的函数是带有函数body的箭头函数。(还:详细的箭头功能。)

对象初始化器中的方法声明(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


3
那么这个名字w就被忽略了吗?
BiAiB 2014年

8
@PellePenna:函数名称对很多事情很有用。我认为两个大问题是递归,并且函数名称显示在调用堆栈,异常跟踪等中。
TJ Crowder 2014年

4
@ChaimEliyah-“接受并不意味着这是最好的答案,它只是意味着它对提出要求的人有用。” 来源
ScrapCode'2

6
@AR:完全正确。但是,有趣的是,它上面的标题是:“首先出现最佳答案,因此总是很容易找到它们。” 由于被接受的答案甚至比具有较高投票权的答案都首先出现,因此游览可能会有些自相矛盾。;-)还有一点不准确,如果我们通过投票确定“最佳”(这是不可靠的,那正是我们所获得的),则“最佳”答案仅在您使用“投票”标签时首先显示-否则,最先的答案是活跃的答案,或者最老的答案。
TJ Crowder

1
@TJCrowder:同意。“按日期安排”有时很烦人。
ScrapCode

143

说到全局上下文,var语句和FunctionDeclaration结尾处的a 都会在全局对象上创建不可删除的属性,但是两者的值都可以被覆盖

两种方式之间的细微差别是,当变量实例化过程运行时(在实际的代码执行之前),所有用声明的标识符var将用初始化undefinedFunctionDeclaration从那时起,使用的标识符将可用,例如:

 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语句的情况下进行赋值时,如果在作用域链中找不到引用的标识符,它将成为全局对象的可删除属性。

另外,未声明的分配ReferenceError严格模式下在ECMAScript 5上抛出。

必须阅读:

注意:此答案已与另一个问题合并,其中来自OP的主要疑问和误解是,用a声明的标识符FunctionDeclaration不能被覆盖,事实并非如此。


我不知道函数可以用JavaScript覆盖!同样,解析订单对我来说是一个很大的卖点。我想我需要注意如何创建函数。
Xeoncross

2
404ing时,“名称函数表达式已神秘化”为+0。可能的镜像?:kangax.github.com/nfe
Mr_Chimp

@CMS好一个。请记住,尽管我从未看过原著,所以我不知道那是一面镜子还是只是同一标题的另一篇文章!
Mr_Chimp

@Mr_Chimp我非常确定,waybackbackmachine表示在爬网时收到302,重定向到您提供的链接。
约翰

123

您在此处张贴的两个代码段几乎可以出于所有目的以相同的方式运行。

但是,行为上的差异在于,对于第一个变体(var functionOne = function() {}),只能在代码中的该点之后调用该函数。

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

这是因为对于第一个变量,foo在运行时会将函数分配给变量。在第二步中,foo在解析时将功能分配给该标识符。

更多技术信息

JavaScript具有三种定义函数的方式。

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

103

格雷格答案的更好解释

functionTwo();
function functionTwo() {
}

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

因为:

hoistedJavaScript解释器总是将函数声明和变量声明不可见地移动()到其包含范围的顶部。函数参数和语言定义的名称显然已经存在。本樱桃

这意味着这样的代码:

functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };

请注意,声明的赋值部分未悬挂。仅悬挂名称。

但是对于函数声明,整个函数体也将被提升

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------

嗨,suhail感谢您提供有关功能主题的清晰信息。现在我的问题是,无论是变量声明(functionOne)还是函数声明(functionTwo),哪个将是声明层次结构中的第一个声明?
沙叻RB

91

其他评论者已经涵盖了以上两个变体的语义差异。我想指出一种风格上的差异:只有“赋值”变体可以设置另一个对象的属性。

我经常用以下模式构建JavaScript模块:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

    exports.publicUtil = function() {
            ...
    };

    return exports;
})();

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

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


4
据我所知,yuiblog.com / blog / 2007/06/12 / module-pattern是模块模式的原始参考。(尽管本文使用的是var foo = function(){...}语法,甚至是私有变量。
肖恩·麦克米兰

实际上,在某些旧版本的IE中并非完全如此。(function window.onload() {}是一件事。)
Ry-

77

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

if (condition){
    function myfunction(){
        // Some code
    }
}

,此定义myfunction将覆盖任何先前的定义,因为它将在解析时完成。

if (condition){
    var myfunction = function (){
        // Some code
    }
}

正确地定义myfunction仅在何时condition满足的工作。


1
这个例子很好,接近完美,但可以改进。更好的示例是在var myFunc = null;循环外部或if / elseif / else块外部进行定义。然后,您可以有条件地将不同的功能分配给同一变量。在JS中,更好的约定是将缺失值分配为null,然后分配为undefined。因此,应首先将myFunction声明为null,然后有条件地将其分配。
亚历山大·米尔斯

62

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

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

}

要么

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

有很多命名空间的技术。可用的JavaScript模块数量越来越多,这一点变得越来越重要。

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


3
看来这个答案被合并到这个问题从另一个问题,措辞可能似乎是一点点无关这个问题。您是否考虑编辑答案,以便似乎更直接地针对这个问题?(重申一下;这根本不是你的错,只是合并问题的副作用)。您也可以删除它,我想您会保持声誉。或者你可以离开它;由于它很旧,可能不会有太大的不同。
Andrew Barber

55

提升 是JavaScript解释器将所有变量和函数声明移到当前作用域顶部的操作。

但是,仅悬挂实际的声明。通过将作业留在原处。

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

变量

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

global_Page = 10;                                               var global_Page;      « undefined
    « Integer literal, Number Type.   -------------------       global_Page = 10;     « Number         
global_Page = 'Yash';                 |   Interpreted   |       global_Page = 'Yash'; « String
    « String literal, String Type.    «       AS        «       global_Page = true;   « Boolean 
var global_Page = true;               |                 |       global_Page = function (){          « function
    « Boolean Type                    -------------------                 var local_functionblock;  « undefined
global_Page = function (){                                                local_functionblock = 777 Number
    var local_functionblock = 777;                              };  
    // Assigning function as a data.
};  

功能

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • 页面内部声明的函数被提升到具有全局访问权限的页面顶部。
  • 在功能块内部声明的功能被提升到该块的顶部。
  • 函数的默认返回值为“ undefined ”,变量声明的默认值也为“ 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引入了Arrow函数Arrow函数表达式具有较短的语法,它们最适合于非方法函数,并且不能用作构造函数。

ArrowFunction : ArrowParameters => ConciseBody

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd

3
嗯,你的答案...是不是模棱两可?写得好,但花费+1并写太多信息。
丹麦语

40

我添加自己的答案只是因为其他所有人都已经完全涵盖了吊装部分。

我想知道很长一段时间以来哪种方法更好,并且由于现在知道http://jsperf.com,我知道:)

在此处输入图片说明

函数声明更快,这就是Web开发人员真正重要的事情吧?;)


8
我要说,可维护性是大多数代码中最重要的方面。性能很重要,但是在大多数情况下,IO可能是定义功能的更大瓶颈。但是,在某些问题上,您需要获得所有性能,这在这些情况下很有用。在这里有一个明确回答问题明确定义的部分的答案也很好。
理查德·加塞德

3
好吧,我发现它与Firefox相反。jsperf.com/sandytest
Sandeep Nayak,

只是一个更新,因为我现在已经使用JavaScript编写了完整的函数式编程样式,所以我从不使用声明,仅使用函数表达式,因此可以按其变量名链接和调用函数。看看RamdaJS ...
Leon Gaban

1
@SandeepNayak我只是在Firefox 50.0.0 / Windows 7 0.0.0中运行您自己的测试,实际上与Leon的测试方法相同。因此,如果您的测试是正确的,我可以得出结论,jsperf的测试不是指示性的,而是全部取决于您的浏览器和/或OS版本,或在特定时刻处于当前计算机的特定状态。
ocramot

33

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

但是,在功能对象实际与其变量相关联的方式时间方面存在差异。这种差异是由于JavaScript中称为变量提升的机制引起的。

基本上,所有函数声明和变量声明都被提升到函数顶部(这就是我们说JavaScript具有函数作用域的原因)。

  • 吊起函数声明时,函数主体将“跟随”,因此在评估函数主体时,变量将立即绑定到函数对象。

  • 当一个变量声明悬挂,初始化并没有 跟随,而是“留下”。将该变量初始化为 undefined函数体的开头,并将 在代码的原始位置为其分配一个值。(实际上,它会在每个声明具有相同名称的变量的位置。)

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

一些例子...

var foo = 1;
function bar() {
  if (!foo) {
    var foo = 10 }
  return foo; }
bar() // 10

变量foo被提升到的功能,初始化的顶部undefined,从而使!footrue,所以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

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

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


3
您有一种奇怪的方式来放置右括号。您是Python编码员吗?看起来您尝试使Javascript看起来像Python。恐怕会使其他人感到困惑。如果必须维护您的JavaScript代码,我将首先让您的代码通过自动prettyprinter。
2013年

1
优秀的职位。“自执行函数”或“立即调用的函数表达式”应该很容易看到,并且他的样式偏好不应影响他的职位-这是准确的,并且可以完美地概括“起重”。+1
Ricalsin

32

第一个示例是函数声明:

function abc(){}

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

var abc = function() {};

主要区别在于如何吊起(吊起和声明)它们。在第一个示例中,整个函数声明被提升。在第二个示例中,只悬挂了var'abc',其值(函数)将是未定义的,并且函数本身保持在声明的位置。

简而言之:

//this will work
abc(param);
function abc(){}

//this would fail
abc(param);
var abc = function() {}

要研究有关此主题的更多信息,强烈建议您使用此 链接


1
你举的例子是种一样的顶级答案
GOTO

发布此答案的主要原因是在底部提供了链接。这是我完全无法理解上述问题的要素。
sla55er 2014年

1
您想要共享链接非常酷。但在SO中,指向其他信息的链接应该只是对问题或您最喜欢的答案的评论。像这样将冗长而复杂的页面弄乱,并重复信息,只是在其末尾添加一个有用的链接,这是次优的选择。不,您不会因提供链接而获得代表积分,但会为社区提供帮助。
XML

30

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

  • 与声明它们的位置无关(但仍受范围限制)。
  • 更能抵抗诸如条件初始化之类的错误(如果需要,您仍然可以覆盖)。
  • 通过与作用域功能分开分配局部功能,代码变得更具可读性。通常在范围内,功能优先,然后是局部功能的声明。
  • 在调试器中,您将清楚地在调用堆栈上看到函数名称,而不是“匿名/求值”函数。

我怀疑后面还会有更多针对命名函数的PROS。命名函数的优点被列为匿名函数的缺点。

从历史上看,匿名函数是由于JavaScript无法作为一种语言列出具有命名函数的成员而出现的:

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}

2
有可以确认的测试:blog.firsov.net/2010/01/…JS性能测试-范围和命名函数-分析
Sasha Firsov 2010年

25

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

有关匿名函数和lambda演算的更多信息,Wikipedia是一个好的开始(http://en.wikipedia.org/wiki/Anonymous_function)。


从ES2015开始(发布答案后的六年半),问题中的两个功能都被命名。
TJ Crowder

25

我在代码中使用可变方法的原因非常特殊,上面已以抽象的方式介绍了该方法的理论,但是一个示例可能会帮助一些像我这样的人,而他们的JavaScript专业知识有限。

我有需要与160个独立设计的品牌一起运行的代码。大多数代码位于共享文件中,而与品牌有关的内容位于单独的文件中,每个品牌一个。

有些品牌需要特定的功能,而有些则不需要。有时,我必须添加新功能来进行特定于品牌的事情。我很乐意更改共享编码,但是我不想更改所有160套品牌文件。

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

然后,需要该功能的特定实现的一个或两个品牌可以定义其功能版本,然后根据需要将其分配给变量,其余的则不执行任何操作。我可以在共享代码中执行null函数之前对其进行测试。

从上面的人们的评论中,我认为可能也可以重新定义静态函数,但是我认为变量解决方案非常清晰。


25

格雷格的答案足够好,但我仍然想在此基础上添加一些东西,这是我最近在观看道格拉斯·克罗克福德的电影时学到视频时。

函数表达式:

var foo = function foo() {};

功能说明:

function foo() {};

函数语句只是var带有function值的语句的简写形式。

所以

function foo() {};

扩展到

var foo = function foo() {};

进一步扩展为:

var foo = undefined;
foo = function foo() {};

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

视频截图


7
抱歉,这是不正确的-我不知道克罗福德在这张幻灯片中要说什么。函数和变量声明都始终放在其作用域的顶部。区别在于变量赋值(无论是使用字符串,布尔值还是函数赋值)都不会高高挂起,而函数体(使用函数声明)则高高在上。
Thomas Heymann


23

下表列出了两个不同的函数声明之间的四个值得注意的比较。

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

以下工作是由于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()

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function add(a, b){
  return a + b;
}

  1. (功能) .name

一个函数的名称function thefuncname(){}thefuncname当它这样声明。

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

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

var a = function(){};
var b = (function(){ return function(){} });

console.log(a.name);
console.log(b.name);

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

console.log((function(){}).name === "");

最后,虽然分配给该函数的变量最初设置了名称,但设置给该函数的连续变量不会更改名称。

var a = function(){};
var b = a;
var c = b;

console.log(a.name);
console.log(b.name);
console.log(c.name);

  1. 性能

在Google的V8和Firefox的Spidermonkey中,可能会有几微秒的JIST编译差异,但最终结果是完全相同的。为了证明这一点,让我们通过比较两个空白代码段的速度来检验JSPerf在微基准测试中的效率。在此可以找到JSPerf测试。并且,jsben.ch测试位于此处。如您所见,什么都不应该有明显的区别。如果您确实是像我这样的性能狂,那么尝试减少范围中的变量和函数的数量,尤其是消除多态性(例如使用相同的变量存储两种不同的类型)可能更值得。

  1. 可变性

使用var关键字声明变量时,可以像这样将另一个值重新分配给变量。

(function(){
    "use strict";
    var foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

但是,当我们使用const语句时,变量引用变得不可变。这意味着我们不能为变量分配新值。但是请注意,这不会使变量的内容不可变:如果您这样做了const arr = [],那么您仍然可以这样做arr[10] = "example"。仅执行类似arr = "new value"或的操作arr = []会引发错误,如下所示。

(function(){
    "use strict";
    const foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

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

(function(){
    "use strict";
    function foobar(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

什么是“最近的街区”

“最近块”是最接近的“功能”(包括异步功能,生成器功能和异步生成器功能)。但是,有趣的是,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;
    }
})();

  • 言(例如ifelseforwhiletry/ catch/ finallyswitchdo/ whilewith

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;
    }
})();


这应该是公认的且最受推崇的答案
亚伦·约翰·萨布

18

@EugeneLazutkin给出了一个示例,其中他命名了一个分配的函数,该函数可以shortcut()用作对其自身的内部引用。John Resig给出了另一个示例- 在他的Learning Advanced Javascript中复制分配给另一个对象的递归函数教程中。虽然在这里分配功能并不是严格的问题,但我还是建议您积极尝试使用本教程-单击右上角的按钮运行代码,然后双击该代码进行编辑。

教程中的示例:递归调用 yell()

删除原始忍者对象后,测试将失败。(第13页)

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 

var samurai = { yell: ninja.yell }; 
var ninja = null; 

try { 
  samurai.yell(4); 
} catch(e){ 
  assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
}

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

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 

var samurai = { yell: ninja.yell }; 
var ninja = {}; 
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );

17

其他答案中未提及的另一个区别是,如果您使用匿名函数

var functionOne = function() {
    // Some code
};

并将其用作构造函数,如

var one = new functionOne();

那么one.constructor.name将不会被定义。Function.name是非标准的,但受Firefox,Chrome,其他Webkit衍生的浏览器和IE 9+支持。

function functionTwo() {
    // Some code
}
two = new functionTwo();

可以使用来检索构造函数的名称作为字符串two.constructor.name


在第一种情况下,将不会定义名称,因为它是为变量分配的匿名函数。我认为匿名这个词是为未定义名称的事物发明的:)
Om Shankar 2013年

在此示例中,two = new成为全局函数,因为没有变种
Waqas Tahir

16

第一个(函数doSomething(x))应该是对象表示法的一部分。

第二个(var doSomething = function(x){ alert(x);})只是创建一个匿名函数并将其分配给变量,doSomething。因此doSomething()将调用该函数。

您可能想知道什么是函数声明函数表达式

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

function foo() {
    return 3;
}

ECMA 5(13.0)将语法定义为
函数标识符(FormalParameterList opt){FunctionBody}

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

并在函数表达式中

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

// 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}


16

我列出了以下差异:

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

    看一下下面的函数:

    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
  2. 它是不是安全写在非功能块函数声明一样,如果因为他们将无法访问。

    if (test) {
        function x() { doSomething(); }
    }
  3. 如下所示的命名函数表达式可能无法在版本9之前的Internet Explorer浏览器中使用。

    var today = function today() {return new Date()}

1
@Arjun如果几年前提出问题了,这是什么问题?答案不仅使OP受益,而且可能使所有SO用户受益,无论何时提出问题。回答已经被接受的问题又有什么问题呢?
SantiBailors,2015年

1
@Arjun您必须了解回答老问题也不错。如果是这样,那么SO将会有这样的障碍。想象一下,API发生了变化(尽管不是在这个问题的上下文中),有人发现了它并提供了新API的答案,这是不允许的吗?直到并且除非答案没有意义并且不属于此处,否则它将被否决并自动删除。您无需理会!!!!
Sudhansu Choudhary

15

如果使用这些函数创建对象,则会得到:

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

我似乎无法重现这一点。console.log(objectOne.__proto__);在我的控制台中显示“ functionOne {}”。为什么会有这种情况的任何想法?
Mike

我似乎也无法复制它。
daremkd

1
这是调试器的功能(显示记录的对象的“类”),并且如今,大多数功能都能够为匿名函数表达式派生名称。顺便说一句,您应该弄清楚两个实例之间没有功能上的区别。
Bergi

12

根据“命名函数显示在堆栈跟踪中”的说法,现代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

12

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

  1. 函数声明:

    function fn(){
      console.log("Hello");
    }
    fn();

    这是非常基本的,不言自明的,适用于多种语言,并跨C语言家族使用。我们声明了定义它的函数,并通过调用它来执行它。

    您应该知道的是,函数实际上是JavaScript中的对象。在内部,我们为上述函数创建了一个对象,并为其指定了名称fn或对该对象的引用存储在fn中。函数是JavaScript中的对象;函数的实例实际上是一个对象实例。

  2. 函数表达式:

    var fn=function(){
      console.log("Hello");
    }
    fn();

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

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


1
那第三种选择var fn = function fn() {...}呢?
chharvey '16

您好Chharvey,不确定您的问题,我想您是在谈论我已经提到的函数表达式。但是,如果仍然有些混乱,那就更麻烦一些了。
Anoop Rai

是的,我在问一个命名函数表达式。它与您的选项#2相似,不同之处在于该函数具有标识符。通常,此标识符与为其分配的变量相同,但并非总是如此。
chharvey

1
是命名函数表达式类似于我的选项#2。因为没有使用标识符,所以它不是强制性的。每当执行函数表达式时,都将使用保存函数对象的变量。标识符没有任何作用。
Anoop Rai

11

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

第一种情况是函数表达式,仅在解释器到达该行代码时才加载。因此,如果按照以下方式进行操作,则会收到错误消息,提示functionOne不是function

functionOne();
var functionOne = function() {
    // Some code
};

原因是在第一行没有将任何值分配给functionOne,因此未定义。我们试图将其称为函数,因此会出现错误。

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

第二种情况是在执行任何代码之前加载的函数声明。因此,如果您喜欢以下内容,则在代码执行之前加载声明不会有任何错误。

functionOne();
function functionOne() {
   // Some code
}

11

关于效果:

新版本V8引入了一些后台优化,因此也进行了优化SpiderMonkey

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

铬62.0.3202 镀铬测试

火狐55 Firefox测试

铬金丝雀63.0.3225 Chrome Canary测试


Anonymous函数表达式似乎Named函数表达式具有更好的性能


Firefox 浏览器Canary ChromeFirefox named_anonymous Chrome金丝雀named_anonymous Chrome named_anonymous


1
是的,这种差异微不足道,以至于希望开发人员会关注哪种方法更适合他们的特定需求,而不是哪种方法可能更快(根据浏览器的工作,每次尝试都会得到不同的jsperf结果-在这种程度下,大多数javascript任务无需进行微优化。
squidbe

@squidbe没有区别。在这里查看:jsperf.com/empty-tests-performance
杰克·吉芬

10

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

1.函数表达式

var functionOne = function() {
    // Some code
};

函数表达式将函数定义为更大的表达式语法(通常是变量赋值)的一部分。通过函数表达式定义的函数可以命名或匿名。函数表达式不能以“函数”开头(因此,下面的自调用示例周围带有括号)。

为函数分配变量意味着没有提升,因为我们知道JavaScript中的函数可以提升,意味着可以在声明它们之前调用它们,而在访问它们之前需要先声明变量,因此在这种情况下,我们不能在声明函数之前访问函数,这也可能是您编写函数的一种方式,对于返回另一个函数的函数,这种声明很有意义,同样在ECMA6及更高版本中,您可以将其分配给箭头函数,可以用于调用匿名函数,这种声明方式也是在JavaScript中创建构造函数的更好方法。

2.功能声明

function functionTwo() {
    // Some code
}

函数声明定义了一个命名函数变量,而无需分配变量。函数声明作为独立的构造出现,不能嵌套在非函数块中。将它们视为变量声明的兄弟是有帮助的。就像变量声明必须以“ var”开始一样,函数声明也必须以“ function”开始。

这是在JavaScript中调用函数的通常方式,可以在甚至声明它之前就调用该函数,因为在JavaScript中所有函数都被提升了,但是如果您“严格”使用,则不会按预期进行提升,这是一个好方法调用所有普通函数,这些函数不是很大,也不是构造函数。

另外,如果您需要有关提升在JavaScript中的工作方式的更多信息,请访问以下链接:

https://developer.mozilla.org/zh-CN/docs/Glossary/Hoisting


1
...also this way of declaring is a better way to create Constructor functions in JavaScript,请您详细说明,我很好奇!
Karl Morrison

原因之一是因为JavaScript中所有内置的Constructor函数都是像这样创建的Number(){[native code]}函数,您不应该与内置函数混淆,在这种情况下稍后再引用会更安全,并且您结束整理代码但不使用吊装...
Alireza

8

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


7

new Function()可用于在字符串中传递函数的主体。因此,可以将其用于创建动态功能。还传递脚本而不执行脚本。

var func = new Function("x", "y", "return x*y;");
function secondFunction(){
   var result;
   result = func(10,20);
   console.log ( result );
}

secondFunction()

尽管这是正确的,但仅此一点与所问的问题有什么关系呢?
杰克·吉芬
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.