我什么时候应该在 ECMAScript 6 中使用箭头函数? [英] When should I use arrow functions in ECMAScript 6?

查看:34
本文介绍了我什么时候应该在 ECMAScript 6 中使用箭头函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 () =>{}function () {} 我们得到了两种非常相似的在 ES6 中编写函数的方法.在其他语言中,lambda 函数通常通过匿名来区分自己,但在 ECMAScript 中,任何函数都可以是匿名的.这两种类型中的每一种都有唯一的使用域(即当 this 需要显式绑定或显式不绑定时).在这些领域之间,有大量的情况可以使用任何一种符号.

With () => {} and function () {} we are getting two very similar ways to write functions in ES6. In other languages lambda functions often distinguish themselves by being anonymous, but in ECMAScript any function can be anonymous. Each of the two types have unique usage domains (namely when this needs to either be bound explicitly or explicitly not be bound). Between those domains there are a vast number of cases where either notation will do.

ES6 中的箭头函数至少有两个限制:

Arrow functions in ES6 have at least two limitations:

  • 不要使用new,不能在创建prototype
  • 时使用
  • 修复了在初始化时绑定到作用域的this
  • Don't work with new and cannot be used when creating prototype
  • Fixed this bound to scope at initialisation

抛开这两个限制,理论上箭头函数几乎可以在任何地方替代常规函数.在实践中使用它们的正确方法是什么?是否应该使用箭头函数,例如:

These two limitations aside, arrow functions could theoretically replace regular functions almost anywhere. What is the right approach using them in practice? Should arrow functions be used e.g.:

  • 他们工作的任何地方",即在任何地方,函数都不必与 this 变量无关,我们也没有创建对象.
  • 只有需要它们的地方",即需要绑定到特定范围的事件侦听器、超时时间
  • 具有短"功能,但不具有长"功能
  • 仅用于不包含其他箭头函数的函数
  • "everywhere they work", i.e. everywhere a function does not have to be agnostic about the this variable and we are not creating an object.
  • only "everywhere they are needed", i.e. event listeners, timeouts, that need to be bound to a certain scope
  • with 'short' functions, but not with 'long' functions
  • only with functions that do not contain another arrow function

我正在寻找在 ECMAScript 的未来版本中选择适当函数符号的指南.该指南需要明确,以便可以教授给团队中的开发人员,并保持一致,以便不需要不断地从一种函数表示法来回重构.

I am looking for a guideline to selecting the appropriate function notation in the future version of ECMAScript. The guideline will need to be clear, so that it can be taught to developers in a team, and to be consistent so that it does not require constant refactoring back and forth from one function notation to another.

这个问题针对那些在即将到来的 ECMAScript 6 (Harmony) 的上下文中考虑过代码风格并且已经使用过该语言的人.

推荐答案

不久前,我们的团队将其所有代码(一个中型 AngularJS 应用程序)迁移到使用 Traceur 巴别塔.我现在对 ES6 及更高版本中的函数使用以下经验法则:

A while ago our team migrated all its code (a mid-sized AngularJS app) to JavaScript compiled using Traceur Babel. I'm now using the following rule of thumb for functions in ES6 and beyond:

  • 在全局范围内和 Object.prototype 属性中使用 function.
  • 使用 class 作为对象构造函数.
  • 在其他地方使用 =>.
  • Use function in the global scope and for Object.prototype properties.
  • Use class for object constructors.
  • Use => everywhere else.

为什么几乎到处都使用箭头函数?

Why use arrow functions almost everywhere?

  1. 作用域安全:当一致地使用箭头函数时,保证一切都使用与根相同的thisObject.如果即使是单个标准函数回调与一堆箭头函数混合在一起,范围也有可能变得混乱.
  2. 紧凑性:箭头函数更易于阅读和编写.(这可能看起来很自以为是,所以我将进一步举几个例子.)
  3. 清晰:当几乎所有东西都是箭头函数时,任何常规的 function 都会立即突出用于定义范围.开发人员可以随时查找下一个更高的 function 语句,以了解 thisObject 是什么.
  1. Scope safety: When arrow functions are used consistently, everything is guaranteed to use the same thisObject as the root. If even a single standard function callback is mixed in with a bunch of arrow functions there's a chance the scope will become messed up.
  2. Compactness: Arrow functions are easier to read and write. (This may seem opinionated so I will give a few examples further on.)
  3. Clarity: When almost everything is an arrow function, any regular function immediately sticks out for defining the scope. A developer can always look up the next-higher function statement to see what the thisObject is.

为什么总是在全局作用域或模块作用域上使用常规函数?

Why always use regular functions on the global scope or module scope?

  1. 表示不应访问thisObject的函数.
  2. 最好明确处理 window 对象(全局范围).
  3. 许多 Object.prototype 定义存在于全局范围内(想想 String.prototype.truncate 等),并且那些通常必须属于 function 类型 无论如何.在全局范围内始终使用 function 有助于避免错误.
  4. 全局范围内的许多函数都是旧式类定义的对象构造函数.
  5. 函数可以命名为1.这有两个好处: (1) 编写function foo(){} 比编写const foo = () => 更不笨拙;{} — 特别是在其他函数调用之外.(2) 函数名称显示在堆栈跟踪中.虽然为每个内部回调命名会很乏味,但命名所有公共函数可能是个好主意.
  6. 函数声明被提升,(意味着可以在声明之前访问它们),这是静态实用程序函数中的一个有用属性.
  1. To indicate a function that should not access the thisObject.
  2. The window object (global scope) is best addressed explicitly.
  3. Many Object.prototype definitions live in the global scope (think String.prototype.truncate, etc.) and those generally have to be of type function anyway. Consistently using function on the global scope helps avoid errors.
  4. Many functions in the global scope are object constructors for old-style class definitions.
  5. Functions can be named1. This has two benefits: (1) It is less awkward to writefunction foo(){} than const foo = () => {} — in particular outside other function calls. (2) The function name shows in stack traces. While it would be tedious to name every internal callback, naming all the public functions is probably a good idea.
  6. Function declarations are hoisted, (meaning they can be accessed before they are declared), which is a useful attribute in a static utility function.

对象构造函数

尝试实例化箭头函数会引发异常:

Object constructors

Attempting to instantiate an arrow function throws an exception:

var x = () => {};
new x(); // TypeError: x is not a constructor

因此,函数相对于箭头函数的一个主要优势是函数兼作对象构造函数:

One key advantage of functions over arrow functions is therefore that functions double as object constructors:

function Person(name) {
    this.name = name;
}

然而,功能相同的2 ECMAScript Harmony 草案类定义几乎一样紧凑:

However, the functionally identical2 ECMAScript Harmony draft class definition is almost as compact:

class Person {
    constructor(name) {
        this.name = name;
    }
}

我希望最终不鼓励使用前一种符号.对象构造函数符号可能仍被某些人用于以编程方式生成对象的简单匿名对象工厂,但不会用于其他很多事情.

I expect that use of the former notation will eventually be discouraged. The object constructor notation may still be used by some for simple anonymous object factories where objects are programmatically generated, but not for much else.

如果需要对象构造函数,则应考虑将函数转换为 class,如上所示.该语法也适用于匿名函数/类.

Where an object constructor is needed one should consider converting the function to a class as shown above. The syntax works with anonymous functions/classes as well.

坚持使用常规函数的最佳理由可能是范围安全性该死的 - 是箭头函数的可读性不如常规函数.如果您的代码一开始就没有功能,那么箭头函数可能看起来就没有必要了,而且当箭头函数没有持续使用时,它们看起来很丑陋.

The probably best argument for sticking to regular functions - scope safety be damned - would be that arrow functions are less readable than regular functions. If your code is not functional in the first place, then arrow functions may not seem necessary, and when arrow functions are not used consistently they look ugly.

自从 ECMAScript 5.1 为我们提供函数式 Array.forEachArray.map 和所有这些让我们使用函数的函数式编程特性以来,ECMAScript 已经发生了很大的变化for 循环之前会被使用.异步 JavaScript 已经起飞了很多.ES6 还将发布一个 Promise 对象,这意味着更多的匿名函数.函数式编程没有回头路可走.在函数式 JavaScript 中,箭头函数优于常规函数.

ECMAScript has changed quite a bit since ECMAScript 5.1 gave us the functional Array.forEach, Array.map and all of these functional programming features that have us use functions where for loops would have been used before. Asynchronous JavaScript has taken off quite a bit. ES6 will also ship a Promise object, which means even more anonymous functions. There is no going back for functional programming. In functional JavaScript, arrow functions are preferable over regular functions.

以这段(特别容易混淆的)代码为例3:

Take for instance this (particularly confusing) piece of code3:

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(articles => Promise.all(articles.map(article => article.comments.getList())))
        .then(commentLists => commentLists.reduce((a, b) => a.concat(b)));
        .then(comments => {
            this.comments = comments;
        })
}

与常规函数相同的一段代码:

The same piece of code with regular functions:

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(function (articles) {
            return Promise.all(articles.map(function (article) {
                return article.comments.getList();
            }));
        })
        .then(function (commentLists) {
            return commentLists.reduce(function (a, b) {
                return a.concat(b);
            });
        })
        .then(function (comments) {
            this.comments = comments;
        }.bind(this));
}

虽然任何一个箭头函数都可以用标准函数代替,但这样做几乎没有什么好处.哪个版本更易读?我会说第一个.

While any one of the arrow functions can be replaced by a standard function, there would be very little to gain from doing so. Which version is more readable? I would say the first one.

我认为是使用箭头函数还是常规函数的问题会随着时间的推移变得不那么重要.大多数函数要么变成类方法,去掉function关键字,要么变成类.函数将继续用于通过 Object.prototype 修补类.同时,我建议为任何真正应该是类方法或类的东西保留 function 关键字.

I think the question whether to use arrow functions or regular functions will become less relevant over time. Most functions will either become class methods, which make away with the function keyword, or they will become classes. Functions will remain in use for patching classes through the Object.prototype. In the mean time I suggest reserving the function keyword for anything that should really be a class method or a class.

  1. 命名箭头函数已在 ES6 规范中被推迟.它们可能仍会在未来的版本中添加.
  2. 根据规范草案,类声明/表达式创建一个与函数声明完全一样的构造函数/原型对",只要一个类不使用 extend代码>关键字.一个细微的区别是类声明是常量,而函数声明不是.
  3. 关于单语句箭头函数中的块的注意事项:我喜欢在单独调用箭头函数的地方使用块来产生副作用(例如,赋值).这样很明显可以丢弃返回值.
  1. Named arrow functions have been deferred in the ES6 specification. They might still be added a future version.
  2. According to the draft specification, "Class declarations/expressions create a constructor function/prototype pair exactly as for function declarations" as long as a class does not use the extend keyword. A minor difference is that class declarations are constants, whereas function declarations are not.
  3. Note on blocks in single statement arrow functions: I like to use a block wherever an arrow function is called for the side effect alone (e.g., assignment). That way it is clear that the return value can be discarded.

这篇关于我什么时候应该在 ECMAScript 6 中使用箭头函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆