var functionName = function() {} 还是 function functionName() {}

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

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

这两种方法是:

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

想问问使用这两种不同方法的原因是什么?每种方法的优缺点是什么?有没有什么可以用一种方法做的事情不能用另一种方法做?

1183691 次浏览

不同之处在于functionOne是一个函数表达式,因此仅在到达该行时定义,而functionTwo是一个函数声明,并且在其周围的函数或脚本执行后立即定义(由于吊装)。

例如,一个函数表达式:

// TypeError: functionOne is not a functionfunctionOne();
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

首先,我想纠正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;

在这种情况下,xyzabc都是同一个对象的别名:

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 hereabc(); // Works
// Yet, it is defined down there.function abc(){}
// We can call it againabc(); // Works

此外,它通过return语句提升:

// We can call it hereabc(); // Worksreturn;function abc(){}

这是一个函数表达式:

var xyz = function(){};

这里的xyz是从赋值点定义的:

// We can't call it herexyz(); // UNDEFINED!!!
// Now it is definedxyz = function(){}
// We can call it herexyz(); // works

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

有趣的事实:

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

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

var abc = function(){};

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

abc = function(){};

我知道我在全局范围内定义了它,前提是我没有在作用域链中的任何地方定义abc。即使在eval()中使用,这种定义风格也是弹性的。虽然定义

function abc(){};

这取决于上下文,可能会让你猜测它实际上是在哪里定义的,特别是在eval()的情况下-答案是:这取决于浏览器。

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

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

在代码维护成本方面,命名函数更可取:

  • 独立于声明的地方(但仍受范围限制)。
  • 更能抵抗条件初始化等错误(如果需要,您仍然可以覆盖)。
  • 通过将局部函数与作用域功能分开分配,代码变得更具可读性。通常在作用域中,功能先行,然后是局部函数的声明。
  • 在调试器中,您将清楚地看到调用堆栈上的函数名称,而不是“匿名/评估”函数。

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

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

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

您在那里发布的两个代码片段几乎出于所有目的,其行为方式相同。

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

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

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

更多技术信息

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

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

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

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

 alert(typeof foo); // 'function', it's already availablealert(typeof bar); // 'undefined'function foo () {}var bar = function () {};alert(typeof bar); // 'function'

barFunctionExpression的赋值一直持续到运行时。

FunctionDeclaration创建的全局属性可以像变量值一样被覆盖而没有任何问题,例如:

 function test () {}test = null;

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

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

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

此外,未声明的赋值在ECMAScript 5上的严格模式下抛出ReferenceError

A必须阅读:

说明:这个答案已经从另一个问题合并,其中OP的主要疑问和误解是使用FunctionDeclaration声明的标识符不能被覆盖,事实并非如此。

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

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

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

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

另见如何在JavaScript中声明命名空间?

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

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

(function(){var exports = {};
function privateUtil() {...}
exports.publicUtil = function() {...};
return exports;})();

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

(另请注意,赋值应该要求语句后有分号,而声明禁止它。

@陈志立给出了一个例子,其中命名一个分配的函数以便能够使用#0作为对自身的内部引用。johnresig给出了另一个例子-学习高级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?" );}

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

function assert(predicate, message) { if(!predicate) { throw new Error(message); } }
var ninja = {yell: function yell(n){return n > 0 ? yell(n-1) + "a" : "hiy";}};assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 
var samurai = { yell: ninja.yell };var ninja = {};assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );
console.log(samurai.yell(4));

在其他答案中没有提到的另一个区别是,如果您使用匿名函数

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

并将其用作构造函数,如

var one = new functionOne();

Function.name是非标准的,但Firefox,Chrome,其他Webkit派生的浏览器和IE 9+都支持。

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

可以将构造函数的名称作为带有two.constructor.name的字符串检索。

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

我有需要运行160个独立设计的品牌的代码。大多数代码都在共享文件中,但特定于品牌的东西在单独的文件中,每个品牌一个。

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

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

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

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

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

第二个(var doSomething = function(x){ alert(x);})只是简单地创建一个匿名函数并将其分配给一个变量doSomething。所以do的()将调用该函数。

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

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

function foo() {return 3;}

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

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

在函数表达式中

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

// Anonymous function expressionvar a = function() {return 3;}
// Named function expressionvar a = function foo() {return 3;}
// Self-invoking function expression(function foo() {alert("hello!");})();

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

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

然而,在如何有一个区别,函数对象实际上与其变量相关联。这种差异是由于JavaScript中称为可变提升的机制。

基本上,所有函数声明和变量声明都被提升到声明发生的函数顶部(这就是为什么我们说JavaScript有职能范围)。

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

  • 当变量声明被提升时,初始化不会跟随,但被“留下”。变量初始化为undefined在函数体的开头,并且将是分配代码中原始位置的值。(实际上,它将在位置分配一个值,在那里发生同名变量的声明。)

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

一些例子…

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

变量foo被提升到函数的顶部,初始化为undefined,因此!footrue,因此foo被分配给10bar范围之外的foo没有任何作用,也没有被触及。

function f() {return a;function a() {return 1};var a = 4;function a() {return 2}}f()() // 2
function f() {return a;var a = 4;function a() {return 1};function a() {return 2}}f()() // 2

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

function f() {var a = 4;function a() {return 1};function a() {return 2};return a; }f() // 4

在本例中,a使用计算第二个函数声明产生的函数对象进行初始化,然后分配4

var a = 1;function b() {a = 10;return;function a() {}}b();a // 1

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

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

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

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

虽然

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

仅当满足condition时才定义myfunction的正确工作。

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

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

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

条款:

快速列表:

  • 功能声明

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

  • 命名function表达式

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

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

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

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

功能声明

第一种形式是函数声明,它看起来像这样:

function x() {console.log('x');}

函数声明是声明;它不是语句或表达式。因此,您不会在它后面加上;(尽管这样做是无害的)。

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

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

x(); // Works even though it's above the declarationfunction x() {console.log('x');}

在ES2015之前,规范并没有涵盖如果您将函数声明放入tryifswitchwhile等控制结构中,JavaScript引擎应该做什么,如下所示:

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

由于它们被处理之前逐步运行代码,因此当它们处于控制结构中时知道该怎么做是很棘手的。

尽管在ES2015之前这样做并不是指定,但它是支持块中函数声明的容许延伸。不幸的是(也是不可避免的),不同的引擎做了不同的事情。

从ES2015开始,规范说明了要做什么。事实上,它给出了三件独立的事情要做:

  1. 如果在Web浏览器上处于松散模式没有,JavaScript引擎应该做一件事
  2. 如果在Web浏览器上处于松散模式,JavaScript引擎应该做其他事情
  3. 如果在严格模式下(浏览器与否),JavaScript引擎应该做另一件事

松散模式的规则很棘手,但在严格模式下,块中的函数声明很容易:它们是块的本地(它们有块范围,这在ES2015中也是新的),它们被提升到块的顶部。所以:

"use strict";if (someCondition) {foo();               // Works just finefunction 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);         // 0console.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

看到隐藏在map()调用中的n => n * 2了吗?这是一个函数。

关于箭头函数的几件事:

  1. 它们没有自己的this。相反,它们关闭了定义它们的上下文的this。(它们也关闭了arguments和相关的super。)这意味着它们中的this与创建它们的this相同,不能更改。

  2. 正如您在上面注意到的,您不使用关键字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的函数。

第一个例子是一个函数声明:

function abc(){}

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

var abc = function() {};

主要区别在于它们是如何被提升的(提升和声明)。在第一个例子中,整个函数声明都被提升。在第二个例子中,只有var'abc'被提升,它的值(函数)将是未定义的,函数本身保持在它被声明的位置。

简单地说:

//this will workabc(param);function abc(){}
//this would failabc(param);var abc = function() {}

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

更好的解释格雷格的回答

functionTwo();function functionTwo() {}

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

原因:

函数声明和变量声明总是被JavaScript解释器无形地移动(hoisted)到其包含范围的顶部。显然,函数参数和语言定义的名称已经存在。本樱桃

这意味着像这样的代码:

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

请注意,声明的赋值部分没有被提升,只有名称被提升。

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

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

鉴于“命名函数显示在堆栈跟踪中”的论点,现代JavaScript引擎实际上非常能够表示匿名函数。

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

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

如果你真的,真的希望你的迭代器和成功回调显示在跟踪中,你也可以命名它们。

[].forEach(function iterator() {});

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

线束(小提琴

'use strict';
var a = function () {throw new Error();},b = function b() {throw new Error();},c = function d() {throw new Error();},e = {f: a,g: b,h: c,i: function () {throw new Error();},j: function j() {throw new Error();},k: function l() {throw new Error();}},m = (function () {return function () {throw new Error();};}()),n = (function () {return function n() {throw new Error();};}()),o = (function () {return function p() {throw new Error();};}());
console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {return values.concat(e[key]);}, [])).concat([m, n, o]).reduce(function (logs, func) {
try {func();} catch (error) {return logs.concat('func.name: ' + func.name + '\n' +'Trace:\n' +error.stack);// Need to manually log the error object in Nitro.}
}, []).join('\n\n'));

V8

func.name:Trace:Errorat a (http://localhost:8000/test.js:4:11)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name: bTrace:Errorat b (http://localhost:8000/test.js:7:15)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name: dTrace:Errorat d (http://localhost:8000/test.js:10:15)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name:Trace:Errorat a (http://localhost:8000/test.js:4:11)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name: bTrace:Errorat b (http://localhost:8000/test.js:7:15)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name: dTrace:Errorat d (http://localhost:8000/test.js:10:15)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name:Trace:Errorat e.i (http://localhost:8000/test.js:17:19)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name: jTrace:Errorat j (http://localhost:8000/test.js:20:19)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name: lTrace:Errorat l (http://localhost:8000/test.js:23:19)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name:Trace:Errorat http://localhost:8000/test.js:28:19at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name: nTrace:Errorat n (http://localhost:8000/test.js:33:19)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27
func.name: pTrace:Errorat p (http://localhost:8000/test.js:38:19)at http://localhost:8000/test.js:47:9at Array.reduce (native)at http://localhost:8000/test.js:44:27 test.js:42

SpiderMonkey

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: bTrace:b@http://localhost:8000/test.js:7:9@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1

func.name: dTrace: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: bTrace:b@http://localhost:8000/test.js:7:9@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1

func.name: dTrace: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: jTrace:j@http://localhost:8000/test.js:20:13@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1

func.name: lTrace: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: nTrace:n@http://localhost:8000/test.js:33:13@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1

func.name: pTrace:p@http://localhost:8000/test.js:38:13@http://localhost:8000/test.js:47:9@http://localhost:8000/test.js:54:1

脉轮

func.name: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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: undefinedTrace:Errorat 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:22http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name: bTrace:b@http://localhost:8000/test.js:7:26http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name: dTrace:d@http://localhost:8000/test.js:10:26http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name:Trace:a@http://localhost:8000/test.js:4:22http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name: bTrace:b@http://localhost:8000/test.js:7:26http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name: dTrace:d@http://localhost:8000/test.js:10:26http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name:Trace:i@http://localhost:8000/test.js:17:30http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name: jTrace:j@http://localhost:8000/test.js:20:30http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name: lTrace:l@http://localhost:8000/test.js:23:30http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name:Trace:http://localhost:8000/test.js:28:30http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name: nTrace:n@http://localhost:8000/test.js:33:30http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33
func.name: pTrace:p@http://localhost:8000/test.js:38:30http://localhost:8000/test.js:47:13reduce@[native code]global code@http://localhost:8000/test.js:44:33

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

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

输入图片描述

函数声明更快,这才是Web开发中真正重要的东西,对吧?;)

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

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

函数表达式:

var foo = function foo() {};

功能说明:

function foo() {};

函数语句只是具有function值的var语句的简写。

所以

function foo() {};

扩展到

var foo = function foo() {};

它进一步扩展到:

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

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

视频截图

我列出了下面的差异:

  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 topreturn 1;}function foo() {  // The second function declaration is moved to topreturn 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 reachablereturn 2;};}alert(outerFunction()); // Displays 1
  2. It is not safe to write function declarations in non-function blocks like if because they won't be accessible.

    if (test) {function x() { doSomething(); }}
  3. Named function expression like the one below, may not work in Internet Explorer browsers prior to version 9.

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

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

第一种情况是函数表达式,它仅在解释器到达该行代码时加载。所以如果你像下面这样做,你会得到一个错误函数一不是函数

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

原因是在第一行没有将值分配给函数One,因此它是未定义的。我们试图将其作为函数调用,因此我们收到了一个错误。

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

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

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

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

但是,只有实际的声明被提升。通过将赋值留在原地。

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

变量

JavaScript被称为松散类型语言。这意味着JavaScript变量可以保存任何Data-Type的值

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

函数

function Identifier_opt ( FormalParameterList_opt ) {FunctionBody | sequence of statements
« return;  Default undefined« return 'some data';}
  • 在页面内声明的函数被提升到具有全局访问权限的页面顶部。
  • 在函数块内声明的函数被提升到块的顶部。
  • 函数的默认返回值为“未定义”,变量声明默认值也为“未定义”

    Scope with respect to function-block global.Scope with respect to page undefined | not available.

Function Declaration

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 Functionconsole.log('Self Invoking');                      (function () {});                                                               }) () -> 'Self Invoking'
var f;f = function (){        « Expression var Functionconsole.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();

您可以使用#0检查不同浏览器的函数声明、表达式测试


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

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

function Shape(id) { // Function Declarationthis.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 ObjectfunObj.hasOwnProperty('prototype'); // falsefunObj.setID( 10 );console.log( funObj.getID() ); // 10

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

#0.

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

new Function()可用于在字符串中传递函数的主体。因此,这可用于创建动态函数。也可以在不执行脚本的情况下传递脚本。

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

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

  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(){}vs函数fn(){}

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

一、功能表达

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

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

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

2.功能声明

function functionTwo() {// Some code}

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

这是在JavaScript中调用函数的正常方式,这个函数可以在你声明它之前被调用,因为在JavaScript中所有函数都被提升了,但是如果你有“使用严格”,这不会像预期的那样提升,这是调用所有正常函数的好方法,这些函数的行不大,也不是构造函数。

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

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

关于性能:

新版本的V8引入了一些底层优化,SpiderMonkey也是如此。

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

ChromeChrome测试

FireFox 55Firefox test

Chrome金丝雀Chrome金丝雀测试


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


FirefoxFirefoxnamed_anonymousChrome金丝雀Chromecanarynamed_anonymousChromeChromenamed_anonymous

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

  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()是一个表达式而不是一个块。命名函数只对它们自己和它们周围的块可见。由于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(){}以这种方式声明时,它的名称是函数名

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

Lastly, while the variable the function is assigned to initially sets the name, successive variables set to the function do not change the name.

var a = function(){};var b = a;var c = b;
console.log(a.name);console.log(b.name);console.log(c.name);

  1. 性能

在谷歌的V8和火狐的蜘蛛猴中,可能会有几微秒的JIT编译差异,但最终结果是完全相同的。为了证明这一点,让我们通过比较两个空白代码片段的速度来检查JSPerf在微基准测试中的效率。JSPerf测试在这里找到。还有,jsben.ch测试在这里找到。正如你所看到的,当不应该有的时候,会有明显的差异。如果你真的像我一样是一个性能怪胎,那么尝试减少作用域中变量和函数的数量,特别是消除多态(例如使用同一个变量存储两个不同的类型)可能更值得你。

  1. 可变性

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

(function(){"use strict";var foobar = function(){}; // initial valuetry {foobar = "Hello World!"; // new valueconsole.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 valuetry {foobar = "Hello World!"; // new valueconsole.log("[no error]");} catch(error) {console.log("ERROR: " + error.message);}console.log(foobar, window.foobar);})();

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

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

𝗪𝗵𝗮𝘁 𝗜𝘀 𝗧𝗵𝗲 &现状;𝗡𝗲𝗮𝗿𝗲𝘀𝘁 𝗕𝗹𝗼𝗰𝗸&现状;

“最近的块”是最近的“函数”(包括异步函数、生成器函数和异步生成器函数)。然而,有趣的是,当在非闭包块中对所述闭包之外的项目时,function functionName() {}的行为像var functionName = function() {}。观察。

  • 正常var add=function(){}

try {// typeof will simply return "undefined" if the variable does not existif (typeof add !== "undefined") {add(1, 1); // just to prove itconsole.log("Not a block");}else if(add===undefined){ // this throws an exception if add doesn't existconsole.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 existif (typeof add !== "undefined") {add(1, 1); // just to prove itconsole.log("Not a block");}else if(add===undefined){ // this throws an exception if add doesn't existconsole.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 existif (typeof add !== "undefined") {add(1, 1); // just to prove itconsole.log("Not a block");}else if(add===undefined){ // this throws an exception if add doesn't existconsole.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/whileelse0)

try {// typeof will simply return "undefined" if the variable does not existif (typeof add !== "undefined") {add(1, 1); // just to prove itconsole.log("Not a block");}else if(add===undefined){ // this throws an exception if add doesn't existconsole.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 existif (typeof add !== "undefined") {add(1, 1); // just to prove itconsole.log("Not a block");}else if(add===undefined){ // this throws an exception if add doesn't existconsole.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 existif (typeof add !== "undefined") {add(1, 1); // just to prove itconsole.log("Not a block");}else if(add===undefined){ // this throws an exception if add doesn't existconsole.log('Behaves like var add=function(a,b){return a+b}')}} catch(e) {console.log("Is a block");}(() => {function add(a, b){return a + b;}})();

我更喜欢将函数定义为变量:

let first = function(x){return x[0];}

而不是:

function first(){....}

因为我可以在定义函数时使用表达式和装饰器。例如:

let safe = function(f){try {f()...}}let last = safe(function(x){return x[0]}).

ES6也更短:

 let last = x => x[0]...........function last(x){return x[0];}......
let last = safe(x => x[0]);

JS中的表达式:返回值的东西
示例:在chrome控制台中尝试以下操作:

a = 10output : 10
(1 + 3)output = 4

声明/声明:不返回值的东西
示例:

if (1 > 2) {// do something.}

这里(1>2)是一个表达式,但if不是,它不返回任何东西。


同样,我们有函数声明/语句与函数表达式
举个例子:

// test.js
var a = 10;
// function expressionvar fun_expression = function() {console.log("Running function Expression");}
// funciton expression
function fun_declaration() {console.log("Running function Statement");}

重要:当JavaScript引擎运行上述js文件时会发生什么。

  • 当这个js运行时,会发生以下事情:

    1. 内存将被创建为变量'a'和'fun_expression'。内存将被创建为函数语句'fun_declaration'
    2. 'a'将被赋值为'未定义'。'fun_expression'将被赋值为'未定义'。'fun_declaration'将全部在内存中。
      注意:上面的步骤1和2称为“执行上下文-创建阶段”。

现在假设我们将js更新为。

// test.js
console.log(a)  //output: udefined (No error)console.log(fun_expression)  // output: undefined (No error)console.log(fun_expression()) // output: Error. As we trying to invoke undefined.console.log(fun_declaration()) // output: running function statement  (As fun_declaration is already hoisted in the memory).
var a = 10;
// function expressionvar fun_expression = function() {console.log('Running function expression')}
// function declaration
function fun_declaration() {console.log('running function declaration')}
console.log(a)   // output: 10console.log(fun_expression()) //output: Running function expressionconsole.log(fun_declaration()) //output: running function declaration

注释中提到的输出应该有助于理解函数表达式和函数语句/声明之间的区别。

这称为函数表达式:

var getRectArea = function(width, height) {return width * height;};
console.log("Area of Rectangle: " + getRectArea(3,4));// This should return the following result in the console:// Area of Rectangle: 12

这称为函数声明:

var w = 5;var h = 6;
function RectArea(width, height) {  //declaring the functionreturn area = width * height;}                                   //note you do not need ; after }
RectArea(w,h);                      //calling or executing the functionconsole.log("Area of Rectangle: " + area);// This should return the following result in the console:// Area of Rectangle: 30

希望这有助于解释函数表达式和函数声明的区别以及如何使用它们。谢谢。

需要注意的一点是:

有两个功能:-

sum(1,2);
const sum = function(first, second) {return first + second;}

在上面的情况下,它会给出没有定义sum的错误,但是

sum(1,2);
function sum(first, second) {return first + second;}

这个函数不会有任何错误,因为在这种情况下会发生吊装

命名函数VS.匿名函数

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

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

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

这两个函数之间的另一个区别是函数One可以用作一个变量,可以在其中保存多个函数,而函数Two保存一些代码块,这些代码块在调用时全部执行。请检查下面:

   var functionOne = (function() {return {
sayHello: function(){console.log('say hello')
},redirectPage:function(_url){window.location.href = _url;}
}})();

你可以选择调用哪个函数。例如functionOne.sayHello或函数One。重定向页面。如果你调用函数Two,那么整个代码块将被执行。

差异函数声明和函数表达式:

Javascript有第一类函数。这意味着它们可以像对待任何其他变量一样对待。函数可以作为函数中的参数传递,从函数并且可以存储在变量中返回。

然而,将函数存储在变量(函数表达式)中并不是创建函数的唯一方法,这也可以通过函数声明来完成。以下是关键区别:

  1. 函数表达式可以是匿名的,而函数声明必须有一个名称。
  2. 两者都有一个用于标识函数的name属性。函数表达式的name属性是它绑定到的变量的名称,而函数声明的名称只是给定的名称。
  3. 函数声明被提升,而函数表达式没有。只有变量被提升为值undefined

下面是一个例子:

try {functionOne();} catch (e) {console.log('i cant run because im not hoisted');}
functionTwo();
// function expression, does not get hoistedlet functionOne = function randomName() {// Some code};
// function declaration, gets hoistedfunction functionTwo() {console.log('I get hoisted');}
try {randomName(); // this isn't the proper name, it is functionOne} catch (e) {console.log('You cant call me with randomName my name is function one');}

:

var functionOne = function() {}在运行时定义,function functionTwo() {}在解析时定义。

// Run-Time function declarationfunctionOne(); // Calling functionOne function here will give an Errorvar functionOne = function () {// Some code};
// Parse-Time function declarationfunctionTwo(); // Calling functionTwo function will not give an Errorfunction functionTwo() {// Some code...}

运行时与解析时之间的解释javascript运行时vs解析时

您不能在函数声明上使用.bind()方法,但可以在函数表达式上使用。

函数声明:

function x() {console.log(this)}.bind('string')
x()

函数表达式:

var x = function() {console.log(this)}.bind('string')
x()

在调试器/DevTools中的断点期间,如果您在控制台中使用格式function functionName() {},则随后无法在控制台中使用functionName()(它表示“未定义”),而在var functionName = function() {}之后,您可以使用该函数。

这个问题

try {console.log("Success: ", add(1, 1));} catch(e) {console.log("ERROR: " + e);}
var add=function(a, b){return a + b;}