首页 > JavaScript > var functionName = function() {} vs function functionName() {}这两种不同方法的原因优缺点是什么

var functionName = function() {} vs function functionName() {}这两种不同方法的原因优缺点是什么

上一篇 下一篇

网友问题:

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

两种方式是:

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

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

分割线

网友回答:

首先,我想纠正 Greg: 也是作用域 — 名称是在遇到此定义的范围内定义的。例:function abc(){}abc

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

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

var xyz = function abc(){};

xyz将像往常一样被定义,在所有浏览器中都是未定义的,除了Internet Explorer – 不要依赖它被定义。但它将在其体内定义:abc

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"

使用组合样式的一个令人信服的理由是函数对象的“name”属性(Internet Explorer 不支持)。基本上当你定义一个函数时,比如

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

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

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

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

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

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

在上面的例子中,我们可以对外部名称做同样的事情,但它会太笨拙(而且速度更慢)。

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

在内心深处,JavaScript 以不同的方式对待这两种语句。这是一个函数声明:

function abc(){}

abc在当前作用域的任何地方都定义了以下内容:

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

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

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

此外,它还通过声明提升:return

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

这是一个函数表达式:

var xyz = function(){};

xyz以下是从分配点定义的:

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

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

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

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

有趣的事实:

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

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

var abc = function(){};

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

abc = function(){};

我知道我在全球范围内定义了它,前提是我没有在范围链中的任何位置定义。即使在 内部使用,这种定义样式也是有弹性的。虽然定义abceval()

function abc(){};

取决于上下文,可能会让您猜测它的实际定义位置,尤其是在 – 答案是:这取决于浏览器。eval()

分割线

网友回答:

区别在于它是一个函数表达式,因此仅在到达该行时定义,而它是一个函数声明,一旦执行其周围的函数或脚本(由于提升),就会定义。functionOnefunctionTwo

例如,函数表达式:

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

分割线

网友回答:

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

条款:

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

快速列表:

  • 函数声明
  • “匿名”表达式(尽管有这个术语,但有时会创建带有名称的函数)function
  • 命名表达式function
  • 访问器函数初始值设定项 (ES5+)
  • 箭头函数表达式 (ES2015+)(与匿名函数表达式一样,它不涉及显式名称,但可以使用名称创建函数)
  • 对象初始值设定项 (ES2015+) 中的方法声明
  • (ES2015+) 中的构造函数和方法声明class

函数声明

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

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
}

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

虽然直到 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 中,如果可能的话,通过从上下文推断函数来为其分配一个名称。在上面的示例中,名称为 。当函数是属性初始值设定项的值时,也会执行类似的操作。(有关何时发生这种情况和规则的详细信息,请在规范中搜索 – 它无处不在。ySetFunctionName

命名表达式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.definePropertiesObject.create

箭头函数表达式 (ES2015+)

ES2015为我们带来了箭头功能。下面是一个例子:

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

看到那个东西藏在电话里了吗?这是一个函数。n => n * 2map()

关于箭头函数的几件事:

  1. 他们没有自己的.相反,它们在定义它们的上下文中关闭。(它们也会关闭,并且在相关的情况下。这意味着它们内部与创建它们的位置相同,并且无法更改。thisthisargumentssuperthisthis
  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() {
    }
};

区别(除了冗长)是方法可以使用,但函数不能。因此,例如,如果你有一个使用方法语法定义(比如)的对象,它可以用来获取返回的值(在可能用它做其他事情之前),而 ES5 版本将不得不这样做。supervalueOfsuper.valueOf()Object.prototype.valueOfObject.prototype.valueOf.call(this)

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

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

ES2015 为我们带来了语法,包括声明的构造函数和方法:class

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + " " + this.lastName;
    }
}

上面有两个函数声明:一个用于构造函数,它获取名称,另一个用于 ,它是分配给 的函数。PersongetFullNamePerson.prototype

模板简介:该模板名称为【var functionName = function() {} vs function functionName() {}这两种不同方法的原因优缺点是什么】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【JavaScript】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 593次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 02-07
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
微信文章 html5 响应式 企业网站 微信公众平台 单页式简历模板 微信模板 微信素材 微信图片 自适应
您可能会喜欢的其他模板