什么时候应该在ECMAScript 6中使用Arrow函数?

本文翻译自:When should I use Arrow functions in ECMAScript 6?

The question is directed at people who have thought about code style in the context of the upcoming ECMAScript 6 (Harmony) and who have already worked with the language. 该问题针对的是那些在即将发布的ECMAScript 6(Harmony)中考虑过代码风格的人,并且已经在使用该语言。

With () => {} and function () {} we are getting two very similar ways to write functions in ES6. 通过() => {}function () {}我们得到了两种非常相似的方法来在ES6中编写函数。 In other languages lambda functions often distinguish themselves by being anonymous, but in ECMAScript any function can be anonymous. 在其他语言中,lambda函数通常通过匿名来区分自己,但是在ECMAScript中,任何函数都可以是匿名的。 Each of the two types have unique usage domains (namely when this needs to either be bound explicitly or explicitly not be bound). 每种类型都有独特的使用域(即当this需求已被明确约束或不明确的约束)。 Between those domains there is a vast number of cases where either notation will do. 在这些域之间,有很多情况下两种表示法都会起作用。

Arrow functions in ES6 have at least two limitations: ES6中的箭头功能至少有两个限制:

  • Don't work with new 不要使用new
  • Fixed this bound to scope at initialisation 固定this绑定到初始化范围

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 eg: 应该使用箭头功能,例如:

  • "everywhere they work", ie everywhere a function does not have to be agnostic about the this variable and we are not creating an object. “无论它们在哪里工作”,即在每个地方函数都不必与this变量无关,我们也没有创建对象。
  • only "everywhere they are needed", ie 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 仅对于不包含另一个箭头功能的功能

What I am looking for is a guideline to selecting the appropriate function notation in the future version of ECMAScript. 我正在寻找的是在将来的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. 指南必须明确,以便可以向团队中的开发人员讲授,并且要保持一致,以便不需要从一个功能符号到另一个功能符号之间不断地进行重构。


#1楼

参考:https://stackoom.com/question/1YFVy/什么时候应该在ECMAScript-中使用Arrow函数


#2楼

A while ago our team migrated all its code (a mid-sized AngularJS app) to JavaScript compiled using Traceur Babel . 不久前,我们的团队将其所有代码(中型AngularJS应用程序)迁移到了使用 Traceur Babel编译的JavaScript。 I'm now using the following rule of thumb for functions in ES6 and beyond: 我现在对ES6及更高版本中的功能使用以下经验法则:

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

Why use arrow functions almost everywhere? 为什么几乎在所有地方都使用箭头功能?

  1. Scope safety: When arrow functions are used consistently, everything is guaranteed to use the same thisObject as the root. 范围安全性:始终使用箭头功能时,可以确保所有内容都使用与根相同的thisObject 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. 清晰度:当几乎所有功能都是箭头功能时,任何常规function立即突出来定义范围。 A developer can always look up the next-higher function statement to see what the thisObject is. 开发人员始终可以查找更高一级的function语句,以查看thisObject是什么。

Why always use regular functions on the global scope or module scope? 为什么总是在全局作用域或模块作用域上使用常规函数?

  1. To indicate a function that should not access the thisObject . 指示不应访问thisObject的函数。
  2. The window object (global scope) is best addressed explicitly. 最好明确地处理window对象(全局范围)。
  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. 许多Object.prototype定义都位于全局范围内(请考虑String.prototype.truncate等),并且无论如何,这些定义通常必须是类型function Consistently using function on the global scope helps avoid errors. 在全局范围内一致使用function有助于避免错误。
  4. Many functions in the global scope are object constructors for old-style class definitions. 全局范围内的许多函数都是旧式类定义的对象构造函数。
  5. Functions can be named 1 . 函数可以命名为1 This has two benefits: (1) It is less awkward to write function foo(){} than const foo = () => {} — in particular outside other function calls. 这有两个好处:(1)与const foo = () => {}相比,编写function foo(){}更为const foo = () => {} —特别是在其他函数调用之外。 (2) The function name shows in stack traces. (2)函数名称显示在堆栈跟踪中。 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;
}

However, the functionally identical 2 ES Harmony draft class definition is almost as compact: 但是,功能上相同的2 ES Harmony 草案类定义几乎一样紧凑:

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. 有些对象仍可以将对象构造函数表示法用于简单的匿名对象工厂,在这些工厂中以编程方式生成对象,而其他方面则不多。

Where an object constructor is needed one should consider converting the function to a class as shown above. 在需要对象构造函数的地方,应考虑将函数转换为如上所述的class The syntax works with anonymous functions/classes as well. 该语法也可用于匿名函数/类。


Readability of arrow functions 箭头功能的可读性

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 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. 自ECMAScript 5.1为我们提供了功能Array.forEachArray.map以及所有这些功能性编程功能(使我们使用以前曾使用过for循环的功能)以来,ECMAScript发生了很大变化。 Asynchronous JavaScript has taken off quite a bit. 异步JavaScript已经发展了很多。 ES6 will also ship a Promise object, which means even more anonymous functions. ES6还将提供Promise对象,这意味着甚至更多的匿名功能。 There is no going back for functional programming. 函数式编程无可奈何。 In functional JavaScript, arrow functions are preferable over regular functions. 在功能性JavaScript中,箭头功能优于常规功能。

Take for instance this (particularly confusing) piece of code 3 : 以这段(特别是令人困惑的)代码3为例:

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. 我会说第一个。

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. 大多数函数要么成为类方法,而这些方法不再使用function关键字,要么成为类。 Functions will remain in use for patching classes through the Object.prototype . 函数将继续用于通过Object.prototype修补类。 In the mean time I suggest reserving the function keyword for anything that should really be a class method or a class. 同时,我建议为实际上应该是类方法或类的任何内容保留function关键字。


Notes 笔记

  1. Named arrow functions have been deferred in the ES6 spec . ES6规范中推迟了命名箭头功能。 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. 根据规范草案只要类不使用extend关键字, “类声明/表达式将创建与函数声明完全相同的构造函数/原型” 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 (eg assignment). 关于单条语句箭头函数中的块的注意事项:我喜欢在单独为副作用(例如赋值)调用箭头函数的地方使用块。 That way it is clear that the return value can be discarded. 这样,很明显可以丢弃返回值。

#3楼

According to the proposal , arrows aimed "to address and resolve several common pain points of traditional Function Expression ." 根据该建议 ,箭头旨在“解决和解决传统Function Expression几个常见痛点”。 They intended to improve matters by binding this lexically and offering terse syntax. 他们意在改善结合事项this词汇,并提供简洁的语法。

However, 然而,

  • One cannot consistently bind this lexically 一个人不能一贯地用词法约束this
  • Arrow function syntax is delicate and ambiguous 箭头函数语法微妙而含糊

Therefore, arrow functions create opportunities for confusion and errors, and should be excluded from a JavaScript programmer's vocabulary, replaced with function exclusively. 因此,箭头函数会造成混乱和错误,因此应从JavaScript程序员的词汇表中排除它们,而仅将其替换为function

Regarding lexical this 关于词汇this

this is problematic: this是有问题的:

function Book(settings) {
    this.settings = settings;
    this.pages = this.createPages();
}
Book.prototype.render = function () {
    this.pages.forEach(function (page) {
        page.draw(this.settings);
    }, this);
};

Arrow functions intend to fix the problem where we need to access a property of this inside a callback. 箭头功能打算解决这个问题,我们需要访问的属性this回调中。 There are already several ways to do that: One could assign this to a variable, use bind , or use the 3rd argument available on the Array aggregate methods. 目前已经有几种方法可以做到这一点:人们可以指定this一个变量,使用bind ,或使用可用的第三个参数Array聚集方法。 Yet arrows seem to be the simplest workaround, so the method could be refactored like this: 但是箭头似乎是最简单的解决方法,因此可以像下面这样重构该方法:

this.pages.forEach(page => page.draw(this.settings));

However, consider if the code used a library like jQuery, whose methods bind this specially. 但是,请考虑代码是否使用了像jQuery这样的库,该库的方法专门this绑定。 Now, there are two this values to deal with: 现在,有两个this的值来处理:

Book.prototype.render = function () {
    var book = this;
    this.$pages.each(function (index) {
        var $page = $(this);
        book.draw(book.currentPage + index, $page);
    });
};

We must use function in order for each to bind this dynamically. 我们必须使用function ,以便each function each可以动态绑定this function We can't use an arrow function here. 我们不能在这里使用箭头功能。

Dealing with multiple this values can also be confusing, because it's hard to know which this an author was talking about: 具有多个处理this值也可能会造成混淆,因为它很难知道哪些this作者说的是:

function Reader() {
    this.book.on('change', function () {
        this.reformat();
    });
}

Did the author actually intend to call Book.prototype.reformat ? 作者实际上打算调用Book.prototype.reformat吗? Or did he forget to bind this , and intend to call Reader.prototype.reformat ? 还是他忘记绑定this ,并打算调用Reader.prototype.reformat If we change the handler to an arrow function, we will similarly wonder if the author wanted the dynamic this , yet chose an arrow because it fit on one line: 如果将处理程序更改为箭头函数,我们将同样想知道作者是否想要动态this ,但选择了箭头,因为它适合一行:

function Reader() {
    this.book.on('change', () => this.reformat());
}

One may pose: "Is it exceptional that arrows could sometimes be the wrong function to use? Perhaps if we only rarely need dynamic this values, then it would still be okay to use arrows most of the time." 一个人可能会提出:“有时箭头可能是使用错误的功能,这是例外吗?也许如果我们仅很少需要动态使用this值,那么大多数时候仍然可以使用箭头。”

But ask yourself this: "Would it be 'worth it' to debug code and find that the result of an error was brought upon by an 'edge case?'" I'd prefer to avoid trouble not just most of the time, but 100% of the time. 但是请问问自己:“调试代码并发现错误的结果是由'edge case'带来的'值得'吗?”我希望不仅在大多数时候避免麻烦,而且希望避免麻烦100%的时间。

There is a better way: Always use function (so this can always be dynamically bound), and always reference this via a variable. 有一个更好的方法:始终使用function (因此this总是可以被动态绑定),以及始终引用this经由可变。 Variables are lexical and assume many names. 变量是词法的,并具有许多名称。 Assigning this to a variable will make your intentions clear: 分配this一个变量都会让你的意图明确:

function Reader() {
    var reader = this;
    reader.book.on('change', function () {
        var book = this;
        book.reformat();
        reader.reformat();
    });
}

Furthermore, always assigning this to a variable (even when there is a single this or no other functions) ensures one's intentions remain clear even after the code is changed. 此外, 始终 this变量分配给变量(即使只有一个this或没有其他功能)也可以确保即使更改代码后,您的意图仍然清晰。

Also, dynamic this is hardly exceptional. 同样,动态this也不是例外。 jQuery is used on over 50 million websites (as of this writing in February 2016). jQuery在超过5000万个网站上使用(截至2016年2月编写)。 Here are other APIs binding this dynamically: 以下是其他动态绑定this API的API:

  • Mocha (~120k downloads yesterday) exposes methods for its tests via this . Mocha(昨天下载了约120k)通过this公开了其测试方法。
  • Grunt (~63k downloads yesterday) exposes methods for build tasks via this . Grunt(昨天下载了约63k)通过this公开了构建任务的方法。
  • Backbone (~22k downloads yesterday) defines methods accessing this . 骨干网(昨天下载约22k)定义了访问this方法。
  • Event APIs (like the DOM's) refer to an EventTarget with this . 事件API(如DOM)使用this引用EventTarget
  • Prototypal APIs that are patched or extended refer to instances with this . 修补或扩展的原型API使用this引用实例。

(Stats via http://trends.builtwith.com/javascript/jQuery and https://www.npmjs.com .) (通过http://trends.builtwith.com/javascript/jQueryhttps://www.npmjs.com进行的统计。)

You are likely to require dynamic this bindings already. 你很可能需要动态this绑定了。

A lexical this is sometimes expected, but sometimes not; 有时this是期望的词汇,但有时不是。 just as a dynamic this is sometimes expected, but sometimes not. 只是作为一个动态this有时预期,但有时没有。 Thankfully, there is a better way, which always produces and communicates the expected binding. 值得庆幸的是,有一种更好的方法,它总是产生并传达期望的绑定。

Regarding terse syntax 关于简洁语法

Arrow functions succeeded in providing a "shorter syntactical form" for functions. 箭头功能成功地为功能提供了“较短的语法形式”。 But will these shorter functions make you more successful? 但是,这些较短的功能会使您更成功吗?

Is x => x * x "easier to read" than function (x) { return x * x; } x => x * xfunction (x) { return x * x; }更“容易阅读” function (x) { return x * x; } function (x) { return x * x; } ? function (x) { return x * x; } Maybe it is, because it's more likely to produce a single, short line of code. 也许是这样,因为它更有可能产生单个短代码行。 Accoring to Dyson's The influence of reading speed and line length on the effectiveness of reading from screen , 根据戴森的观点,读取速度和行长对从屏幕读取效果的影响

A medium line length (55 characters per line) appears to support effective reading at normal and fast speeds. 中等的行长(每行55个字符)似乎支持正常和快速的有效阅读。 This produced the highest level of comprehension . 这产生了最高的理解力。 . .

Similar justifications are made for the conditional (ternary) operator, and for single-line if statements. 条件(三元)运算符和单行if语句也有类似的理由。

However, are you really writing the simple mathematical functions advertised in the proposal ? 但是,您是否真的在编写 提案中宣传的简单数学函数? My domains are not mathematical, so my subroutines are rarely so elegant. 我的领域不是数学领域,因此我的子例程很少那么优雅。 Rather, I commonly see arrow functions break a column limit, and wrap to another line due to the editor or style guide, which nullifies "readability" by Dyson's definition. 相反,由于编辑器或样式指南,我通常看到箭头函数打破列限制,并换行到另一行,这使戴森的定义使“可读性”无效。

One might pose, "How about just using the short version for short functions, when possible?" 一个人可能会说:“如果可能的话,仅将简短版本用于简短功能?” But now a stylistic rule contradicts a language constraint: "Try to use the shortest function notation possible, keeping in mind that sometimes only the longest notation will bind this as expected." 但是现在,一条样式规则与语言约束相矛盾:“尝试使用可能的最短函数表示法,请记住,有时只有最长的表示法会按预期约束this 。” Such conflation makes arrows particularly prone to misuse. 这种混合使箭头特别容易被滥用。

There are numerous issues with arrow function syntax: 箭头函数语法有很多问题:

const a = x =>
    doSomething(x);

const b = x =>
    doSomething(x);
    doSomethingElse(x);

Both of these functions are syntactically valid. 这两个函数在语法上均有效。 But doSomethingElse(x); 但是doSomethingElse(x); is not in the body of b , it is just a poorly-indented, top-level statement. 不在b的主体中,它只是一个缩进的顶级语句。

When expanding to the block form, there is no longer an implicit return , which one could forget to restore. 当扩展为块形式时,不再有隐式return ,可能会忘记恢复。 But the expression may only have been intended to produce a side-effect, so who knows if an explicit return will be necessary going forward? 但是,该表达可能只是为了产生副作用,所以谁知道以后是否需要明确的return

const create = () => User.create();

const create = () => {
    let user;
    User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

const create = () => {
    let user;
    return User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

What may be intended as a rest parameter can be parsed as the spread operator: 可以用作休息参数的内容可以解析为散布运算符:

processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest

Assignment can be confused with default arguments: 分配可以与默认参数混淆:

const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens

Blocks look like objects: 块看起来像对象:

(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object

What does this mean? 这是什么意思?

() => {}

Did the author intend to create a no-op, or a function that returns an empty object? 作者是否打算创建无操作或返回空对象的函数? (With this in mind, should we ever place { after => ? Should we restrict ourselves to the expression syntax only? That would further reduce arrows' frequency.) (考虑到这一点,我们是否应该将{放在=> ?我们应该只将表达式语法限制在我们自己吗?这将进一步降低箭头的出现频率。)

=> looks like <= and >= : =>看起来像<=>=

x => 1 ? 2 : 3
x <= 1 ? 2 : 3

if (x => 1) {}
if (x >= 1) {}

To invoke an arrow function expression immediately, one must place () on the outside, yet placing () on the inside is valid and could be intentional. 要立即调用箭头函数表达式,必须将()放在外部,而将()放在内部是有效的,并且可能是有意的。

(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function

Although, if one writes (() => doSomething()()); 虽然,如果有人写(() => doSomething()()); with the intention of writing an immediately-invoked function expression, simply nothing will happen. 为了编写立即调用的函数表达式,根本不会发生任何事情。

It's hard to argue that arrow functions are "more understandable" with all the above cases in mind. 考虑到以上所有情况,很难说箭头功能是“更易理解的”。 One could learn all the special rules required to utilize this syntax. 可以学习使用这种语法所需的所有特殊规则。 Is it really worth it? 是不是真的值得吗?

The syntax of function is unexceptionally generalized. function的语法无一例外地被概括。 To use function exclusively means the language itself prevents one from writing confusing code. 仅使用function意味着语言本身可以防止编写令人困惑的代码。 To write procedures that should be syntactically understood in all cases, I choose function . 为了编写在所有情况下都应在语法上理解的过程,我选择function

Regarding a guideline 关于指南

You request a guideline that needs to be "clear" and "consistent." 您要求的准则必须“清晰”和“一致”。 Using arrow functions will eventually result in syntactically-valid, logically-invalid code, with both function forms intertwined, meaningfully and arbitrarily. 使用箭头函数最终将导致语法上有效,逻辑上无效的代码,而两种函数形式却是有意义且任意地交织在一起的。 Therefore, I offer the following: 因此,我提供以下内容:

Guideline for Function Notation in ES6: ES6中的功能符号指南:

  • Always create procedures with function . 始终创建具有function程序。
  • Always assign this to a variable. 始终分配this一个变量。 Do not use () => {} . 不要使用() => {}

#4楼

Arrow functions were created to simplify function scope and solving the this keyword by making it more simpler. 创建箭头函数是为了简化函数scope并通过使其更简单来解决this关键字。 They utilize the => syntax, which looks like an arrow. 他们使用=>语法,看起来像箭头。

Note: It does not replace the existing functions. 注意:它不会替代现有功能。 If you replace every function syntax with arrow functions, its not going to work in all cases. 如果将所有函数语法替换为箭头函数,则在所有情况下均不能正常使用。

Let's have a look at the existing ES5 syntax, If the this keyword were inside an object's method (a function that belongs to an object), what would it refer to? 让我们看一下现有的ES5语法,如果this关键字位于对象的方法(属于对象的函数)内,它将指向什么?

var Actor = {
  name: 'RajiniKanth',
  getName: function() {
     console.log(this.name);
  }
};
Actor.getName();

The above snippet would refer to an object and print out the name "RajiniKanth" . 上面的代码段将引用一个object并打印出名称"RajiniKanth" Let's explore the below snippet and see what would this point out here. 让我们探索下面的代码片段,看看这里指出了什么。

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

Now what about if the this keyword were inside of method's function ? 现在,如果this关键字位于method's function内部怎么办?

Here this would refer to window object than the inner function as its fallen out of scope . 在这里,它指的是window object不是inner function因为它超出了scope Because this , always references the owner of the function it is in, for this case — since it is now out of scope — the window/global object. 因为this始终引用它所在的函数的所有者,在这种情况下-因为它现在不在范围内-窗口/全局对象。

When it is inside of an object 's method — the function 's owner is the object. 当它在object的方法内部时, function的所有者就是对象。 Thus the this keyword is bound to the object. 因此,此关键字绑定到对象。 Yet when it is inside of a function, either stand alone or within another method, it will always refer to the window/global object. 但是,当它位于函数内部时,无论是独立存在还是在另一种方法中,它将始终引用window/global对象。

var fn = function(){
  alert(this);
}

fn(); // [object Window]

There are ways to solve this problem in our ES5 itself, let us look into that before diving into ES6 arrow functions on how solve it. 有许多方法可以在我们的ES5本身中解决此问题,让我们在深入探讨如何解决它的ES6箭头功能之前先进行研究。

Typically you would, create a variable outside of the method's inner function. 通常,您将在方法的内部函数之外创建一个变量。 Now the 'forEach' method gains access to this and thus the object's properties and their values. 现在, 'forEach'方法可以访问this object's ,从而可以访问object's属性及其值。

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   var _this = this;
   this.movies.forEach(function(movie) {
     alert(_this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

using bind to attach the this keyword that refers to the method to the method's inner function . 使用bind将引用该方法的this关键字附加到该method's inner function

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(_this.name + " has acted in " + movie);
   }).bind(this);
  }
};

Actor.showMovies();

Now with ES6 arrow function, we can deal with lexical scoping issue in a simpler way. 现在有了ES6箭头功能,我们可以以更简单的方式处理lexical scoping问题。

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach((movie) => {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

Arrow functions are more like function statements, except that they bind the this to parent scope . Arrow functions与函数语句更像,只是它们bind this bindparent scope If the arrow function is in top scope , this argument will refer to window/global scope , while an arrow function inside a regular function will have its this argument the same as its outer function. 如果arrow function is in top scope ,则this参数将引用window/global scope ,而常规函数内部的arrow函数将使其this参数与外部函数相同。

With arrow functions this is bound to the enclosing scope at creation time and cannot be changed. 使用arrow功能, this功能在创建时绑定到封闭scope ,并且无法更改。 The new operator, bind, call, and apply have no effect on this. 新的运算符,绑定,调用和应用对此无效。

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

// With a traditional function if we don't control
// the context then can we lose control of `this`.
var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`
  asyncFunction(o, function (param) {
  // We made a mistake of thinking `this` is
  // the instance of `o`.
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? false

In the above example, we lost the control of this. 在上面的示例中,我们失去了对此的控制。 We can solve the above example by using a variable reference of this or using bind . 我们可以通过使用this的变量引用或bind来解决上述示例。 With ES6, it becomes easier in managing the this as its bound to lexical scoping . 随着ES6,它成为管理越容易this作为它的必然lexical scoping

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`.
  //
  // Because this arrow function is created within
  // the scope of `doSomething` it is bound to this
  // lexical scope.
  asyncFunction(o, (param) => {
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? true

When not to Arrow functions 什么时候不使用箭头功能

Inside an object literal. 在对象文字内部。

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  getName: () => {
     alert(this.name);
  }
};

Actor.getName();

Actor.getName is defined with an arrow function, but on invocation it alerts undefined because this.name is undefined as the context remains to window . Actor.getName是使用箭头函数定义的,但是在调用时会发出undefined的警报,因为由于上下文仍保留在window因此this.name尚未undefined

It happens because the arrow function binds the context lexically with the window object ... ie outer scope. 之所以会发生这种情况,是因为arrow函数将上下文与window object (即外部作用域)按词法绑定。 Executing this.name is equivalent to window.name , which is undefined. 执行this.name等同于window.name ,它是未定义的。

Object prototype 对象原型

The same rule applies when defining methods on a prototype object . prototype object上定义方法时,将应用相同的规则。 Instead of using an arrow function for defining sayCatName method, which brings an incorrect context window : 而不是使用箭头函数来定义sayCatName方法,该方法会带来不正确的context window

function Actor(name) {
  this.name = name;
}
Actor.prototype.getName = () => {
  console.log(this === window); // => true
  return this.name;
};
var act = new Actor('RajiniKanth');
act.getName(); // => undefined

Invoking constructors 调用构造函数

this in a construction invocation is the newly created object. this在建筑调用是新创建的对象。 When executing new Fn(), the context of the constructor Fn is a new object: this instanceof Fn === true . 当执行new Fn()时, constructor Fn的上下文是一个新对象: this instanceof Fn === true

this is setup from the enclosing context, ie the outer scope which makes it not assigned to newly created object. this是从封闭上下文(即使其不分配给新创建的对象的外部范围)中设置的。

var Message = (text) => {
  this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');

Callback with dynamic context 具有动态上下文的回调

Arrow function binds the context statically on declaration and is not possible to make it dynamic. 箭头函数在声明时静态绑定context ,无法使其动态化。 Attaching event listeners to DOM elements is a common task in client side programming. 将事件侦听器附加到DOM元素是客户端编程中的常见任务。 An event triggers the handler function with this as the target element. 事件触发以此为目标元素的处理函数。

var button = document.getElementById('myButton');
button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});

this is window in an arrow function that is defined in the global context. this是在全局上下文中定义的箭头函数中的窗口。 When a click event happens, browser tries to invoke the handler function with button context, but arrow function does not change its pre-defined context. 当发生click事件时,浏览器尝试使用按钮上下文调用处理程序函数,但是arrow函数不会更改其预定义的上下文。 this.innerHTML is equivalent to window.innerHTML and has no sense. this.innerHTMLwindow.innerHTML等效,没有任何意义。

You have to apply a function expression, which allows to change this depending on the target element: 您必须应用一个函数表达式,该表达式允许根据目标元素进行更改:

var button = document.getElementById('myButton');
button.addEventListener('click', function() {
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});

When user clicks the button, this in the handler function is button. 当用户单击按钮时,处理程序功能中的该按钮为按钮。 Thus this.innerHTML = 'Clicked button' modifies correctly the button text to reflect clicked status. 因此, this.innerHTML = 'Clicked button'正确修改按钮文本以反映单击状态。

References: https://dmitripavlutin.com/when-not-to-use-arrow-functions-in-javascript/ 参考: https : //dmitripavlutin.com/when-not-to-use-arrow-functions-in-javascript/


#5楼

In addition to the great answers so far, I'd like to present a very different reason why arrow functions are in a certain sense fundamentally better than "ordinary" JavaScript functions. 除了到目前为止的好答案之外,我还要提出一个非常不同的原因,为什么箭头函数在某种意义上要比“普通” JavaScript函数更好。 For the sake of discussion, let's temporarily assume we use a type checker like TypeScript or Facebook's "Flow". 为了便于讨论,我们暂时假设我们使用类型检查器,例如TypeScript或Facebook的“ Flow”。 Consider the following toy module, which is valid ECMAScript 6 code plus Flow type annotations: (I'll include the untyped code, which would realistically result from Babel, at the end of this answer, so it can actually be run.) 考虑以下玩具模块,该模块是有效的ECMAScript 6代码以及Flow类型注释:(我将在此答案的末尾包含未键入的代码,该代码实际上是Babel产生的,因此可以实际运行。)

 export class C { n : number; f1: number => number; f2: number => number; constructor(){ this.n = 42; this.f1 = (x:number) => x + this.n; this.f2 = function (x:number) { return x + this.n;}; } } 

Now see what happens when we use the class C from a different module, like this: 现在看看当我们从另一个模块使用C类时会发生什么,如下所示:

 let o = { f1: new C().f1, f2: new C().f2, n: "foo" }; let n1: number = o.f1(1); // n1 = 43 console.log(n1 === 43); // true let n2: number = o.f2(1); // n2 = "1foo" console.log(n2 === "1foo"); // true, not a string! 

As you can see, the type checker failed here: f2 was supposed to return a number, but it returned a string! 如您所见, 类型检查器在这里失败 :f2应该返回一个数字,但是它返回了一个字符串!

Worse, it seems that no conceivable type checker can handle ordinary (non-arrow) JavaScript functions, because the "this" of f2 does not occur in the argument list of f2, so the required type for "this" could not possibly be added as an annotation to f2. 更糟糕的是,似乎没有一种可能的类型检查器可以处理普通的(非箭头)JavaScript函数,因为f2的“ this”未出现在f2的参数列表中,因此不可能添加“ this”的必需类型。作为f2的注释。

Does this problem also affect people who don't use type checkers? 这个问题还会影响不使用类型检查器的人吗? I think so, because even when we have no static types, we think as if they're there. 我想是这样,因为即使没有静态类型,我们也认为它们在那里。 ("The first parameters must be a number, the second one a string" etc.) A hidden "this"-argument which may or may not be used in the function's body makes our mental bookkeeping harder. (“第一个参数必须是数字,第二个参数必须是字符串”等。)在函数的主体中可能使用或可能不使用的隐藏的“ this”参数使我们的心理记录更加困难。

Here is the runnable untyped version, which would be produced by Babel: 这是可运行的无类型版本,由Babel制作:

 class C { constructor() { this.n = 42; this.f1 = x => x + this.n; this.f2 = function (x) { return x + this.n; }; } } let o = { f1: new C().f1, f2: new C().f2, n: "foo" }; let n1 = o.f1(1); // n1 = 43 console.log(n1 === 43); // true let n2 = o.f2(1); // n2 = "1foo" console.log(n2 === "1foo"); // true, not a string! 


#6楼

我喜欢在那里获得当地所有的时间使用箭头功能this是不需要的,因为箭头功能不结合自己的这一点,参数,超,或new.target

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值