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

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

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

这两种方式是:

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

使用这两种不同方法的原因是什么?每种方法的优缺点是什么?有一种方法可以通过一种方法完成,而另一种方法无法完成吗?

回答

不同之处在于它functionOne是一个函数表达式,因此只在到达该行时定义,而是functionTwo一个函数声明,并且只要执行其周围的函数或脚本(由于提升)就会定义.

例如,一个函数表达式:

// TypeError: functionOne is not a function
functionOne();
var functionOne = function() {
console.log("Hello!");
};

并且,一个函数声明:

// Outputs: "Hello!"
functionTwo();
function functionTwo() {
console.log("Hello!");
}

这也意味着您无法使用函数声明有条件地定义函数:

if (test) {
// Error or misbehavior
function functionThree() { doSomething(); }
}

上面的实际定义functionThree与... test的值无关 - 除非use strict有效,否则它只会引发错误.

  • @Greg:顺便说一句,差异不仅在于它们在不同时间被解析.本质上,你的`functionOne`只是一个赋予它的匿名函数的变量,而`functionTwo`实际上是一个命名函数.在两者上调用`.toString()`以查看差异.在某些您希望以编程方式获取函数名称的情况下,这很重要.
  • @Greg关于解析时间与运行时间的答案部分不正确.在JavaScript中,函数声明不是在分析时定义的,而是在运行时定义的.过程如下:解析源代码 - >评估JavaScript程序 - >初始化全局执行上下文 - >执行声明绑定实例化.在此过程中,函数声明将被实例化(参见[第10.5章]的步骤5(http://es5.github.com/#x10.5)).
  • 有两种不同.第一个是`函数表达式`第二个是`函数声明`.您可以在这里阅读更多相关主题:http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/
  • 这种现象的术语称为提升.
  • @Jason Bunting ..不确定你在这里得到了什么,.toString()似乎为两者返回了基本相同的值(函数定义):http://cl.ly/2a2C2Y1r0J451o0q0B1B
  • @Jon z:`functionTwo`有一个名字.它显示在函数的字符串表示中,在函数的非标准`name`属性中(https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Name)以及堆栈跟踪和调试器.这是一件有用的事情.
  • @ user1803405我从未遇到过将函数定义赋值给变量声明而不是普通的旧vanilla函数声明是有益的.函数声明完成了变量声明技术所做的一切,并具有提升和命名的附加好处(用于函数的内部引用以及更容易的调试).我当前公司的一些开发人员喜欢将变量声明作为在使用前声明的强制执行,但我不断反对它,因为它没有提供真正的价值.
  • The sentence "due to hoisting" might give a wrong impression that only the named function gets hoisted. In fact, both `var functionOne` as well as `function functionTwo` get hoisted to some degree - it's just that functionOne is set to undefined (you could call it half-hoisting, variables always get hoisted only to that degree) whereas function functionTwo is fully hoisted in that it's defined and declared. Invoking something that's undefined will of course then throw a typeError.
  • 还要注意,你可以做到这两点!但是,命名的函数名称仅在函数内可用.见http://ejohn.org/apps/learn/#14
  • 内存使用情况怎么样?在某些情况下,`var functionName = function`样式不能早先被垃圾收集吗?
  • 此问题的命名vs未命名方面对于分析/调试代码也很重要.匿名函数本身很难分析,因为您对使用内存的函数的反馈有限.为变量分配匿名函数不会命名该函数.没有规则阻止你也命名一个函数表达式,即 - >`var x = function myFunctionsName(){...};`但是,我不知道什么时候函数表达式指向一个命名函数(而不是一个匿名函数)一)定义(解析时间与运行时间).
  • 这完全是为了提升一些解析或运行时间的东西.functionTwo的定义在脚本块的顶部被提升,只有functionOne变量的声明被提升到块的顶部,定义保持原样,因此在分配之前函数未定义.
  • @MattDiPasquale由于"var"声明,变量"functionTwo"在调用它的行的范围内.但是,在下一行之前,该变量未分配值.因此,当调用"functionTwo"时,其值仍未定义.尝试将`undefined`作为函数调用会导致错误.
  • 那么使用`var function`有什么好处吗?

首先,我想纠正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 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()- 答案是:它取决于浏览器.

  • 使用命名函数的一个重要原因是调试器可以使用该名称来帮助您理解调用堆栈或堆栈跟踪.当你看到调用堆栈并看到"匿名函数"10级深度时,它很糟糕...
  • 我指的是RoBorg,但他无处可寻.简单:RoBorg ===格雷格.这就是历史可以在互联网时代重写的方式.;-)
  • var xyz = function abc(){}; console.log(xyz === abc); 我测试的所有浏览器(Safari 4,Firefox 3.5.5,Opera 10.10)都给了我"Undefined variable:abc".
  • 总的来说,我认为这篇文章很好地解释了利用函数声明的差异和优势.我同意不同意将函数表达式赋值用于变量的好处,特别是因为"好处"似乎是声明一个全局实体的提议......并且每个人都知道你不应该混淆全局命名空间, 对?;-)
  • `var abc = function(){}; console.log(abc.name);`不再生成``"`,而是生成`"abc"`.
  • @Antimony函数声明与块不同.这应该更好地解释:http://stackoverflow.com/questions/17409945/function-declarations-within-blocks-according-to-the-google-javascript-style-gui
  • @CristianGarcia我建议不要使用`abc = function abc()`来定义全局函数,因为(a)它在严格模式下被禁止(除非`abc`在其他地方预先声明,但是你真的搞得一团糟你的双手).(b)您应该尽可能减少添加到全局范围内的内容的数量.(c)任何试图找出"abc"来自何处的人都会感到困惑.
  • IMO,如果您打算在前端使用全局,只需将其吸收,并在第一次声明时将window.foo ="".

这是创建函数的标准表单的纲要:( 最初是为另一个问题编写的,但在被移入规范问题后进行了调整.)

条款:

  • ES5:ECMAScript第5版,2009年
  • ES2015:ECMAScript 2015(也称为"ES6")

快速清单:

功能声明

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

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

函数声明是一个声明 ; 这不是一个陈述或表达.因此,你不要跟随它;(尽管这样做是无害的).

在执行任何逐步执行代码之前,执行进入其出现的上下文时,将处理函数声明.它创建的函数具有正确的名称(x在上面的示例中),并且该名称放在声明出现的范围内.

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

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

直到ES2015,该规范并没有涵盖,如果你把一个控制结构像内部函数声明中的JavaScript引擎应该做的事情try,if,switch,while,等等,是这样的:

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

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

尽管在ES2015之前没有指定这样做,但它是一个允许的扩展来支持块中的函数声明.不幸的是(并且不可避免地),不同的引擎做了不同的事情.

从ES2015开始,规范说明了该怎么做.事实上,它提供了三个单独的事情:

  1. 如果松散模式不在 Web浏览器上,JavaScript引擎应该做一件事
  2. 如果在Web浏览器上处于松散模式,则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.defineProperty,Object.defineProperties和不太知名的第二个参数创建访问器函数Object.create.

箭头功能表达(ES2015 +)

ES2015为我们带来了箭头功能.这是一个例子:

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

看到n => n * 2隐藏在map()通话中的东西?这是一个功能.

关于箭头功能的一些事情:

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

没有的版本{ ... }被称为带有表达体简洁体的箭头函数.(另外:一个简洁的箭头功能.){ ... }定义主体的那个是带有功能体的箭头功能.(另外:一个冗长的箭头功能.)

对象初始化器中的方法声明(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,另一个用于for getFullName,它是一个分配给的函数Person.prototype.

  • @PellePenna:函数名称对很多东西很有用.我视图中的两个biggies是递归,并且函数的名称显示在调用堆栈,异常跟踪等中.
  • @AR:很正确.但有趣的是,正好在其上面说"最好的答案首先出现,以便他们总是很容易找到." 由于接受的答案首先出现在更高的投票答案上,因此巡回演出可能有些自相矛盾.;-)还有点不准确,如果我们通过投票确定"最佳"(这是不可靠的,这只是我们得到的),"最佳"答案仅在您使用"投票"选项卡时才会显示 - 否则,首先是有效的答案,或者是最早的答案.
  • @ChaimEliyah - "接受并不意味着它是最好的答案,它只是意味着它适用于那个问过的人." [源(http://stackoverflow.com/tour)
  • 然后名字`w`被忽略了?

说到全局上下文,var语句和FunctionDeclaration最后都将在全局对象上创建一个不可删除的属性,但两者的值都可以被覆盖.

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

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

分配bar FunctionExpression发生在运行时间.

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

 function test () {}
test = null;

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

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

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

此外,未声明的作业ReferenceError在严格模式下投入ECMAScript 5 .

必读:

  • 命名函数表达式揭秘

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

  • +0到"Names function expressions demystified"文章,因为它是404ing.可能的镜子?:http://kangax.github.com/nfe/

您在那里发布的两个代码片段几乎在所有目的下都会以相同的方式运行.

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

使用第二个variant(function functionTwo()),该函数可用于在声明函数的上方运行的代码.

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

更多技术信息

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

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

更好地解释格雷格的答案

functionTwo();
function functionTwo() {
}

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

因为:

这意味着代码如下:

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

请注意,声明的赋值部分未被提升.只有名字被悬挂.

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

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

其他评论者已经涵盖了上述两种变体的语义差异.我想要注意一个风格差异:只有"赋值"变体可以设置另一个对象的属性.

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

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

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

(另请注意,赋值在语句后应该使用分号,而声明禁止它.)

  • http://yuiblog.com/blog/2007/06/12/module-pattern/是模块模式的原始参考,据我所知.(尽管该文章甚至对私有变量使用`var foo = function(){...}`语法.

何时优先考虑第一种方法到第二种方法的说明是当你需要避免覆盖函数的先前定义时.

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

,这个定义myfunction将覆盖任何先前的定义,因为它将在分析时完成.

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

myfunction只有在condition满足时才能正确定义.


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

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

要么

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

命名空间有很多技巧.随着大量JavaScript模块的出现,它变得越来越重要.

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

  • 看来这个答案从另一个问题合并到这个问题中,而且**可能*似乎与*this*问题有点无关.你会考虑编辑答案,因此它似乎更专注于这个问题吗?(重申一下;这根本不是你的错......只是合并问题的副作用).你也可以删除它,我想你会保持你的声誉.或者你可以离开它; 因为它已经老了,它可能没什么大不了的.

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

但是,只有实际的声明才会被提升.将任务留在原处.

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

变量

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

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

功能

function Identifier_opt ( FormalParameterList_opt ) {
FunctionBody | sequence of statements
« return;  Default undefined
« return 'some data';
}
  • 在页面内声明的函数被提升到具有全局访问权限的页面顶部.
  • 在函数块内声明的函数被提升到块的顶部.
  • 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引入了箭头函数:箭头函数表达式具有较短的语法,它们最适合非方法函数,并且它们不能用作构造函数.

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd
  • 啊,你的答案......不是很暧昧吗?写得很好但是+1用于支出和写太多信息.

我正在添加我自己的答案,因为其他人都彻底覆盖了吊装部件.

我想知道现在哪种方式更好,现在感谢http://jsperf.com我知道:)

函数声明更快,这对于web开发中真正重要的是什么?;)

  • 我会说可维护性是大多数代码最重要的方面.性能很重要,但在大多数情况下,IO可能是您定义功能的一个更大的瓶颈.但是,在某些问题上,您需要获得所有性能,这在这些情况下非常有用.在这里得到答案也很好,这清楚地回答了问题中明确定义的部分.
  • 好吧,我发现它与Firefox相反.http://jsperf.com/sandytest
  • 微量标记总是失败.期待http://jsperf.com是浪费时间.你真正需要的是查看JS引擎源代码,官方文档,或至少sniff开发博客或邮件列表.

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

然而,函数对象与其变量实际关联的方式时间存在差异.这种差异是由于JavaScript中的变量提升机制造成的.

基本上,所有函数声明和变量声明都被提升到声明发生的函数的顶部(这就是为什么我们说JavaScript具有函数作用域).

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

一些例子...

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

函数声明优先于变量声明,最后一个函数声明"粘".

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.

  • 你有一种奇怪的方式来放置闭合的括号.你是Python编码器吗?看起来你试图使Javascript看起来像Python.我担心其他人会感到困惑.如果我必须维护你的JavaScript代码,我会首先让你的代码通过一个自动的prettyprinter.

第一个例子是函数声明:

function abc(){}

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

var abc = function() {};

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

简而言之:

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

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


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

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

我怀疑命名函数的PROS会更多.被列为命名函数优势的是匿名函数的缺点.

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

{
member:function() { /* How do I make "this.member" a named function? */
}
}
  • 有确认的测试:http://blog.firsov.net/2010/01/js-local-functions-anonymous-vs-named.html JS性能测试 - 范围和命名功能 - 分析

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

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

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

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

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

根据上面人们的评论,我认为也可以重新定义静态函数,但我认为变量解决方案很好而且清晰.


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

有关匿名函数和lambda演算的更多信息,维基百科是一个良好的开端(http://en.wikipedia.org/wiki/Anonymous_function).


格雷格的答案已经足够好了,但我还是想补充一点,我刚才看到道格拉斯·克罗克福德的视频.

功能表达:

var foo = function foo() {};

功能说明:

function foo() {};

函数语句只是var带有function值的语句的简写.

所以

function foo() {};

扩展到

var foo = function foo() {};

其进一步扩展到:

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

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

  • 对不起,但这是不正确的 - 我不知道Crockford试图在那张幻灯片中说些什么.函数和变量声明都始终被提升到其范围的顶部.区别在于变量赋值(无论是使用字符串,布尔值还是函数赋值)都不会被提升到顶部,而函数体(使用函数声明)则是.

@EugeneLazutkin给出了一个例子,他将一个指定的函数命名为能够shortcut()用作自身的内部引用.John Resig给出了另一个例子 - 在他的Learning Advanced Javascript教程中复制分配给另一个对象的递归函数.虽然向属性分配函数并不是严格意义上的问题,但我建议您主动尝试教程 - 通过单击右上角的按钮运行代码,然后双击代码以根据自己的喜好进行编辑.

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

删除原始ninja对象时测试失败.(第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." );

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

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.


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

第二个(var doSomething = function(x){ alert(x);})只是创建一个匿名函数并将其赋值给变量doSomething.所以doSomething()将调用该函数.

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

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

function foo() {
return 3;
}

在上述条件中,函数名称在其范围内以及其父级的范围内可见(否则它将无法访问).

并在函数表达式中

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

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

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

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

我列出了以下差异:

  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. var today = function today() {return new Date()}
    

下面列出的两种不同功能声明之间有四个值得注意的比较.

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

以下工作原因因为function add()范围限定为最近的块:

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

以下不起作用(因为add取代了add = undefined).

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

以下内容不起作用,因为var add;在使用后声明.

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

一个函数的名称var add=undefinedthefuncname当它这样声明.

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

否则,如果函数声明为var add=,则函数 .name是用于存储函数的第一个变量.

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

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

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

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

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

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

  1. 变量可变性

当您使用function thefuncname(){}关键字声明变量时,您可以将其他值重新分配给变量.

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

但是,当我们使用const语句时,变量引用变为不可变.这意味着我们无法为变量分配新值.但请注意,这并不会使变量的内容成为不可变的:如果你这样做function(){},那么你仍然可以"".只做类似varconst arr = []会抛出错误,如下所示.

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

有趣的是,如果我们将变量声明为arr[10] = "example",那么变量的不变性与声明它一样arr = "new value".

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

什么是"最近的块"

"最近的块"是最近的"函数"(包括异步函数,生成器函数和异步生成器函数).然而,有趣的arr = []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);
})();
  • 正常 function functionName() {}
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}
  • 功能
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 functionName = function() {},var add=function(){},function add(){},if,else/ for/ while,try,catch/ finally,switch)
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;
}
})();
  • 箭头功能 do
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;
}
}
  • 箭头功能 while
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;
}
})();

鉴于"命名函数显示在堆栈跟踪"参数,现代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

在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 function fn(){}


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

第一种情况是函数表达式,仅在解释器到达该行代码时才加载.因此,如果您执行以下操作,您将收到一个错误,即functionOne不是函数.

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

原因是在第一行没有为functionOne赋值,因此它是未定义的.我们试图将其称为函数,因此我们得到一个错误.

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

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

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

关于表现:

新版本V8引入了几个底层优化,如此SpiderMonkey.

表达式和声明之间现在几乎没有区别.
函数表达式现在看起来更快.

Chrome 62.0.3202

FireFox 55

Chrome Canary 63.0.3225

火狐
Chrome金丝雀


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

1.功能表达

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

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

2.功能声明

function functionTwo() {
// Some code
}

这是在JavaScript中调用函数的常规方法,这个函数可以在你将它声明之前调用,因为在JavaScript中所有函数都被提升,但如果你有'使用严格',这将不会像预期的那样提升,这是一个好方法调用行中不大的所有普通函数,它们都不是构造函数.

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

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


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


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

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

第一个是匿名函数表达式

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

而第二个是函数声明

function functionTwo () {
// some code
}

两者之间的主要明显区别在于函数名称,因为匿名函数没有可调用的名称。

命名函数对比 匿名函数

匿名函数快速且易于键入,许多库和工具倾向于鼓励这种惯用的代码风格。但是,匿名函数有一些缺点

命名函数表达式:

为您的函数表达式提供一个名称非常有效地解决了所有这些缺点,并且没有明显的缺点。最佳做法是始终命名您的函数表达式:

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

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


以上是var functionName = function(){} vs function functionName(){}的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>