简体   繁体   English

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

[英]When should I use arrow functions in ECMAScript 6?

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 中,任何 function 都可以是匿名的。 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 are 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 and cannot be used when creating prototype不使用new并且不能在创建prototype时使用
  • Fixed this bound to scope at initialisation在初始化时修复this绑定到 scope

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. “他们工作的任何地方”,即 function 不必对this变量不可知,我们也不会创建 object。
  • only "everywhere they are needed", ie event listeners, timeouts, that need to be bound to a certain scope只有“他们需要的任何地方”,即事件监听器,超时,需要绑定到某个 scope
  • with 'short' functions, but not with 'long' functions具有“短”功能,但不具有“长”功能
  • only with functions that do not contain another arrow function仅适用于不包含另一个箭头的功能 function

I am looking for a guideline to selecting the appropriate function notation in the future version of ECMAScript.我正在寻找在未来版本的 ECMAScript 中选择适当的 function 符号的指南。 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.指南需要清晰,以便可以将其传授给团队中的开发人员,并且需要保持一致,这样就不需要不断地从一个 function 符号来回重构到另一个符号。

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) 的上下文中考虑过代码风格并且已经使用过该语言的人。

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) 编写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 ECMAScript Harmony draft class definition is almost as compact:然而,功能相同的2 ECMAScript 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和所有这些函数式编程特性以来,ECMAScript 已经发生了很大变化,这些特性让我们使用了以前会使用for循环的函数。 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 specification . 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.这样很明显可以丢弃返回值。

According to the proposal , arrows aimed "to address and resolve several common pain points of traditional function expressions".根据提案,箭头旨在“解决和解决传统函数表达式的几个常见痛点”。 They intended to improve matters by binding this lexically and offering terse syntax.他们打算通过this词法上绑定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 third 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动态绑定this 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.但问问自己:“调试代码并发现错误的结果是由‘边缘情况’引起的,这是否‘值得’?”我宁愿不仅在大多数时候避免麻烦,而且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:

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

(Statistics via http://trends.builtwith.com/javascript/jQuery and https://www.npmjs.com.) (通过http://trends.builtwith.com/javascript/jQuery和 https://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.可能是这样,因为它更有可能产生一行简短的代码。 According 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 .不在b的主体中。 It is just a poorly-indented, top-level statement.它只是一个缩进很差的顶级语句。

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 parentheses

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 () => {} .不要使用() => {}

Arrow functions were created to simplify function scope and solving the this keyword by making it 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, it's not going to work in all cases.如果你用箭头函数替换每个函数语法,它不会在所有情况下都有效。

Let's have a look at the existing ES5 syntax.让我们看看现有的 ES5 语法。 If the this keyword were inside an object's method (a function that belongs to an object), what would it refer to?如果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 .这里 this 指的是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.因此this关键字绑定到对象。 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.我们的 ES5 本身有办法解决这个问题。 Let us look into that before diving into ES6 arrow functions on how solve it.在深入研究 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属性及其值。

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 the ES6 arrow function, we can deal with lexical scoping issue in a simpler way.现在有了 ES6 箭头函数,我们可以用更简单的方式处理词法范围问题。

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 the parent scope .箭头函数更像是函数语句,只不过它们this绑定父作用域 If the arrow function is in the top scope , the this argument will refer to the window/global scope , while an arrow function inside a regular function will have its this argument the same as its outer function.如果箭头函数在顶部作用域中,则this参数将引用窗口/全局作用域,而常规函数内的箭头函数的 this 参数将与其外部函数相同。

With arrow functions this is bound to the enclosing scope at creation time and cannot be changed.使用箭头函数, this在创建时绑定到封闭范围并且不能更改。 The new operator, bind, call, and apply have no effect on this. new 运算符、bind、call 和 apply 对此没有影响。

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.在上面的例子中,我们失去了对 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作为其绑定到词法范围变得更容易。

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 use 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 因为this.name undefined因为上下文仍然是window

It happens because the arrow function binds the context lexically with the window object ... ie, the outer scope.发生这种情况是因为箭头函数在词法上将上下文与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.一个事件以 this 作为目标元素触发处理函数。

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, the browser tries to invoke the handler function with button context, but arrow function does not change its pre-defined context.当点击事件发生时,浏览器会尝试调用带有按钮上下文的处理函数,但箭头函数不会改变其预定义的上下文。 this.innerHTML is equivalent to window.innerHTML and has no sense. this.innerHTML相当于window.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 the button.当用户单击按钮时,处理函数中的this就是按钮。 Thus this.innerHTML = 'Clicked button' correctly modifies the button text to reflect the clicked status.因此this.innerHTML = 'Clicked button'正确修改按钮文本以反映点击状态。

References参考

Arrow functions - most widely used ES6 feature so far ...箭头函数 - 迄今为止使用最广泛的 ES6 特性......

Usage: All ES5 functions should be replaced with ES6 arrow functions except in following scenarios:用法:除以下场景外,所有 ES5 函数都应替换为 ES6 箭头函数:

Arrow functions should not be used:箭功能不应使用:

  1. When we want function hoisting当我们想要函数提升时
    • as arrow functions are anonymous.因为箭头函数是匿名的。
  2. When we want to use this / arguments in a function当我们想在函数中使用this / arguments
    • as arrow functions do not have this / arguments of their own, they depend upon their outer context.由于箭头函数没有自己的this / arguments ,它们依赖于它们的外部上下文。
  3. When we want to use named function当我们想使用命名函数时
    • as arrow functions are anonymous.因为箭头函数是匿名的。
  4. When we want to use function as a constructor当我们想使用函数作为constructor
    • as arrow functions do not have their own this .因为箭头函数没有自己的this
  5. When we want to add function as a property in object literal and use object in it当我们想在对象字面量中添加函数作为属性并在其中使用对象时
    • as we can not access this (which should be object itself).因为我们无法访问this (它应该是对象本身)。

Let us understand some of the variants of arrow functions to understand better:让我们了解一些箭头函数的变体以更好地理解:

Variant 1 : When we want to pass more than one argument to a function and return some value from it.变体 1 :当我们想要将多个参数传递给函数并从中返回一些值时。

ES5 version : ES5 版本

var multiply = function (a, b) {
    return a*b;
};
console.log(multiply(5, 6)); // 30

ES6 version : ES6 版本

var multiplyArrow = (a, b) => a*b;
console.log(multiplyArrow(5, 6)); // 30

Note:笔记:

The function keyword is not required.function不是必需的关键字。 => is required. =>是必需的。 {} are optional, when we do not provide {} return is implicitly added by JavaScript and when we do provide {} we need to add return if we need it. {}是可选的,当我们不提供{} return是由 JavaScript 隐式添加的,当我们提供{}我们需要在需要时添加return

Variant 2 : When we want to pass only one argument to a function and return some value from it.变体2:当我们想通过使用一个参数的函数,并从它返回一定的价值。

ES5 version : ES5 版本

var double = function(a) {
    return a*2;
};
console.log(double(2)); // 4

ES6 version : ES6 版本

var doubleArrow  = a => a*2;
console.log(doubleArrow(2)); // 4

Note:笔记:

When passing only one argument we can omit the parentheses, () .当只传递一个参数时,我们可以省略括号()

Variant 3 : When we do not want to pass any argument to a function and do not want to return any value.变体3:当我们不想传递任何参数的函数,不想返回任何值。

ES5 version : ES5 版本

var sayHello = function() {
    console.log("Hello");
};
sayHello(); // Hello

ES6 version : ES6 版本

var sayHelloArrow = () => {console.log("sayHelloArrow");}
sayHelloArrow(); // sayHelloArrow

Variant 4 : When we want to explicitly return from arrow functions.变体 4 :当我们想要明确地从箭头函数返回时。

ES6 version : ES6 版本

var increment = x => {
  return x + 1;
};
console.log(increment(1)); // 2

Variant 5 : When we want to return an object from arrow functions.变体 5 :当我们想从箭头函数返回一个对象时。

ES6 version : ES6 版本

var returnObject = () => ({a:5});
console.log(returnObject());

Note:笔记:

We need to wrap the object in parentheses, () .我们需要将对象括在括号() Otherwise, JavaScript cannot differentiate between a block and an object.否则,JavaScript 无法区分块和对象。

Variant 6 : Arrow functions do not have arguments (an array like object) of their own.变体 6 :箭头函数没有自己的arguments (类似于对象的数组)。 They depend upon outer context for arguments .它们依赖于arguments外部上下文。

ES6 version : ES6 版本

function foo() {
  var abc = i => arguments[0];
  console.log(abc(1));
};
foo(2); // 2

Note:笔记:

foo is an ES5 function, with an arguments array like object and an argument passed to it is 2 so arguments[0] for foo is 2. foo是一个 ES5 函数,有一个像 object 一样的arguments数组,传递给它的参数是2所以foo arguments[0]是 2。

abc is an ES6 arrow function since it does not have its own arguments . abc是一个ES6箭头功能,因为它没有自己的arguments Hence it prints arguments[0] of foo its outer context instead.因此,它会打印foo arguments[0]其外部上下文。

Variant 7 : Arrow functions do not have this of their own they depend upon outer context for this变体7:箭头的功能没有this属于自己的,他们依靠的外部背景下this

ES5 version : ES5 版本

var obj5 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
        setTimeout(function(){
        console.log(this.greet + ": " +  user); // "this" here is undefined.
        });
     }
};

obj5.greetUser("Katty"); //undefined: Katty

Note:笔记:

The callback passed to setTimeout is an ES5 function and it has its own this which is undefined in a use-strict environment.传递给 setTimeout 的回调是一个 ES5 函数,它有自己的thisuse-strict环境中未定义。 Hence we get the output:因此我们得到输出:

undefined: Katty

ES6 version : ES6 版本

var obj6 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
    setTimeout(() => console.log(this.greet + ": " +  user));
      // This here refers to outer context
   }
};

obj6.greetUser("Katty"); // Hi, Welcome: Katty

Note:笔记:

The callback passed to setTimeout is an ES6 arrow function and it does not have its own this , so it takes it from its outer context that is greetUser which has this .传递给回调setTimeout是一个ES6箭头功能,它没有自己的this ,所以它需要从它的外部环境正在greetUser它有this That is obj6 and hence we get the output:那是obj6 ,因此我们得到输出:

Hi, Welcome: Katty

Miscellaneous:各种各样的:

  • We cannot use new with arrow functions.我们不能将new与箭头函数一起使用。
  • Arrow functions do not have prototype property.箭头函数没有prototype属性。
  • We do not have binding of this when an arrow function is invoked through apply or call .当通过applycall调用箭头函数时,我们没有绑定this

I still stand by everything I wrote in my first answer in this thread.我仍然支持在这个线程的第一个答案中写的所有内容。 However, my opinion on code style has developed since then, so I have a new answer to this question that builds on my last one.然而,我对代码风格的看法从那时起就发展了,所以我对这个问题有了一个新的答案,这个答案建立在我上一个的基础上。

Regarding lexical this关于词法this

In my last answer, I deliberately eschewed an underlying belief I hold about this language, as it was not directly related to the argument I was making.在我的最后一个回答中,我故意回避了我对这种语言持有的潜在信念,因为它与我提出的论点没有直接关系。 Nonetheless, without this being explicitly stated, I can understand why many people simply balk at my recommendation to not use arrows, when they find arrows so useful.尽管如此,如果没有明确说明这一点,我可以理解为什么许多人在发现箭头如此有用时,只是拒绝我不使用箭头的建议。

My belief is this: we shouldn't be using this in the first place.我的信念是:我们不应该首先使用this Therefore, if a person deliberately avoids using this in his code, then the “lexical this ” feature of arrows is of little to no value.因此,如果一个人故意避免在他的代码中使用this ,那么箭头的“词法this ”特性就几乎没有价值。 Also, under the premise that this is a bad thing, arrow's treatment of this is less of a “good thing;”此外,前提下, this是一件坏事,箭头的治疗this是更小的“好事”; instead, it's more of a form of damage control for another bad language feature.相反,它更像是另一种糟糕的语言功能的损害控制形式。

I figure that this either does not occur to some people, but even to those to whom it does, they must invariably find themselves working within codebases where this appears a hundred times per file, and a little (or a lot) of damage control is all a reasonable person could hope for.我想,这要么不发生的一些人,但即使是那些谁这样做,他们必须总是发现自己的代码库,其中内工作this似乎每个文件一百倍,并损害控制的一点点(或很多)是一个理性的人所能期望的一切。 So arrows can be good, in a way, when they make a bad situation better.因此,在某种程度上,当箭头使糟糕的情况变得更好时,它们可能是好的。

Even if it is easier to write code with this with arrows than without them, the rules for using arrows remain very complex (see: current thread).即使是更容易编写代码与this用箭头比没有他们,使用箭头规则仍然很复杂(参见:当前线程)。 Thus, guidelines are neither “clear” nor “consistent,” as you've requested.因此,正如您所要求的那样,指南既不“清晰”也不“一致”。 Even if programmers know about arrows' ambiguities, I think they shrug and accept them anyway, because the value of lexical this overshadows them.即使程序员知道箭含糊不清,我认为他们耸耸肩,反正接受他们,因为词汇值this黯然失色他们。

All this is a preface to the following realization: if one does not use this , then the ambiguity about this that arrows normally cause becomes irrelevant.所有这些都是以下认识的序言:如果不使用this ,那么箭头通常引起的关于this的歧义就变得无关紧要了。 Arrows become more neutral in this context.在这种情况下,箭头变得更加中性。

Regarding terse syntax关于简洁的语法

When I wrote my first answer, I was of the opinion that even slavish adherence to best practices was a worthwhile price to pay if it meant I could produce more perfect code.当我写下我的第一个答案时,我认为即使是对最佳实践的盲目遵守也是值得付出的代价,如果这意味着我可以生成更完美的代码。 But I eventually came to realize that terseness can serve as a form of abstraction that can improve code quality, too — enough so to justify straying from best practices sometimes.但我最终意识到,简洁也可以作为一种抽象形式,可以提高代码质量——足以证明有时偏离最佳实践是合理的。

In other words: dammit, I want one-liner functions, too!换句话说:该死的,我也想要单行函数!

Regarding a guideline关于指南

With the possibility of this -neutral arrow functions, and terseness being worth pursuit, I offer the following more lenient guideline:由于this中性箭头函数的可能性,以及值得追求的简洁性,我提供以下更宽松的指导方针:

Guideline for Function Notation in ES6: ES6 中的函数符号指南:

  • Don't use this .不要用this
  • Use function declarations for functions you'd call by name (because they're hoisted).对按名称调用的函数使用函数声明(因为它们被提升)。
  • Use arrow functions for callbacks (because they tend to be terser).对回调使用箭头函数(因为它们往往更简洁)。

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!

我更喜欢在不需要访问本地this任何时候使用箭头函数,因为箭头函数不绑定自己的this 、参数、 super 或 new.target

Arrow functions or lambdas , were introduced in ES 6. Apart from its elegance in minimal syntax, the most notable functional difference is scoping of this inside an arrow function箭头函数或lambdas是在 ES 6 中引入的。 除了它在最小语法中的优雅之外,最显着的功能差异是箭头函数内部的this作用域

In regular function expressions, the this keyword is bound to different values based on the context in which it is called.正则函数表达式中, this关键字根据调用它的上下文绑定到不同的值。

In arrow functions , this is lexically bound, which means it closes over this from the scope in which the arrow function was defined (parent-scope), and does not change no matter where and how it is invoked / called.箭头函数中this词法绑定的,这意味着它从定义箭头函数的范围(父范围)关闭this ,并且无论在何处以及如何调用/调用它都不会改变。

Limitations of arrow functions as methods on an object箭头函数作为对象方法的局限性

// this = global Window
let objA = {
  id: 10,
  name: "Simar",
  print () { // same as print: function()
    console.log(`[${this.id} -> ${this.name}]`);
  }
}

objA.print(); // logs: [10 -> Simar]

objA = {
  id: 10,
  name: "Simar",
  print: () => {
    // Closes over this lexically (global Window)
    console.log(`[${this.id} -> ${this.name}]`);
  }
};

objA.print(); // logs: [undefined -> undefined]

In the case of objA.print() when print() method defined using regular function , it worked by resolving this properly to objA for method invocation, but failed when defined as an arrow => function.objA.print()的情况下,当使用常规function定义print()方法时,它通过将this正确解析为objA进行方法调用来工作,但在定义为箭头=>函数时失败。 It is because this in a regular function when invoked as a method on an object ( objA ), is the object itself.这是因为this在常规函数中作为对象 ( objA ) 上的方法调用时,是对象本身。

However, in case of an arrow function, this gets lexically bound to the the this of the enclosing scope where it was defined (global / Window in our case) and stays it stays same during its invocation as a method on objA .然而,在箭头函数的情况下, this在词法上绑定到它被定义的封闭范围的this (在我们的例子中是全局/窗口),并在它作为objA上的方法调用期间保持不变。

There are advantages of an arrow-functions over regular functions in method(s) of an object, but only when this is expected to be fixed and bound at the time of definition.有超过常规功能的箭头功能优点在方法的对象的(一个或多个),仅当this预期是固定的,并在定义时的约束。

/* this = global | Window (enclosing scope) */

let objB = {
  id: 20,
  name: "Paul",
  print () { // Same as print: function()
    setTimeout( function() {
      // Invoked async, not bound to objB
      console.log(`[${this.id} -> ${this.name}]`);
    }, 1)
  }
};

objB.print(); // Logs: [undefined -> undefined]'

objB = {
  id: 20,
  name: "Paul",
  print () { // Same as print: function()
    setTimeout( () => {
      // Closes over bind to this from objB.print()
      console.log(`[${this.id} -> ${this.name}]`);
    }, 1)
  }
};

objB.print(); // Logs: [20 -> Paul]

In the case of objB.print() where the print() method is defined as function that invokes console.log( [${this.id} -> {this.name}] ) asynchronously as a call-back on setTimeout , this resolved correctly to objB when an arrow function was used as call-back, but failed when the call-back was defined as as regular function.objB.print()的情况下, print()方法被定义为调用console.log( [${this.id} -> {this.name}] )函数作为setTimeout上的回调,当箭头函数用作objB时, this正确解析为objB但当回调定义为常规函数时失败。

It is because the arrow => function, passed to setTimeout(()=>..) , closed over this lexically from its parent, ie, invocation of objB.print() which defined it.这是因为传递给setTimeout(()=>..)的箭头=>函数在词法上从其父级关闭了this ,即调用定义它的objB.print() In other words, the arrow => function passed in to to setTimeout(()==>... bound to objB as its this because the invocation of objB.print() this was objB itself.换句话说,箭头=>函数传入到setTimeout(()==>...绑定到objB作为它的this因为objB.print() this的调用是objB本身。

We could easily use Function.prototype.bind() to make the call-back defined as a regular function work, by binding it to the correct this .我们可以很容易地使用Function.prototype.bind()使定义为常规函数的回调工作,通过将其绑定到正确的this

const objB = {
  id: 20,
  name: "Singh",
  print () { // The same as print: function()
    setTimeout( (function() {
      console.log(`[${this.id} -> ${this.name}]`);
    }).bind(this), 1)
  }
}

objB.print() // logs: [20 -> Singh]

However, arrow functions come in handy and are less error prone for the case of async call-backs where we know the this at the time of the functions definition to which it gets and should be bound.然而,箭头函数派上用场,并且在异步回调的情况下不太容易出错,在这种情况下,我们知道this在它获取并应该绑定到的函数定义时。

Limitation of Arrow-Functions where this needs to change across invocations箭功能的局限性,其中this需要跨调用更改

Anytime, we need a function whose this can be changed at the time of invocation, we can't use arrow functions.任何时候,我们都需要一个可以在调用时更改this的函数,我们不能使用箭头函数。

/* this = global | Window (enclosing scope) */

function print() {
  console.log(`[${this.id} -> {this.name}]`);
}

const obj1 = {
  id: 10,
  name: "Simar",
  print // The same as print: print
};

obj.print(); // Logs: [10 -> Simar]

const obj2 = {
  id: 20,
  name: "Paul",
};

printObj2 = obj2.bind(obj2);
printObj2(); // Logs: [20 -> Paul]
print.call(obj2); // logs: [20 -> Paul]

None of the above will work with arrow function const print = () => { console.log( [${this.id} -> {this.name}] );} as this can't be changed and will stay bound to the this of the enclosing scope where it was defined (global / Window).以上都不适用于箭头函数const print = () => { console.log( [${this.id} -> {this.name}] );}因为this无法更改并且将保持绑定到定义它的封闭范围的this (全局/窗口)。

In all these examples, we invoked the same function with different objects ( obj1 and obj2 ) one after the another, both of which were created after the print() function was declared.在所有这些示例中,我们使用不同的对象( obj1obj2 )一个接一个地调用相同的函数,这两个对象都是在声明print()函数之后创建的。

These were contrived examples, but let's think about some more real life examples.这些都是人为的例子,但让我们考虑一些更真实的例子。 If we had to write our reduce() method similar to one that works on arrays , we again can't define it as a lambda, because it needs to infer this from the invocation context, ie, the array on which it was invoked.如果我们必须编写类似于处理arrays reduce()方法,我们又不能将其定义为 lambda,因为它需要从调用上下文(即调用它的数组)中推断出this

For this reason, constructor functions can never be defined as arrow functions, as this for a constructor function can not be set at the time of its declaration.出于这个原因,构造函数永远不能定义为箭头函数,因为不能在声明时设置构造函数的this Every time a constructor function is invoked with the new keyword, a new object is created which then gets bound to that particular invocation.每次使用new关键字调用构造函数时,都会创建一个新对象,然后将其绑定到该特定调用。

Also when when frameworks or systems accept a callback function(s) to be invoked later with dynamic context this , we can't use arrow functions as again this may need to change with every invocation.此外,当框架或系统接受稍后使用动态上下文this调用的回调函数时,我们不能再次使用箭头函数,因为this可能需要随每次调用而更改。 This situation commonly arises with DOM event handlers.这种情况通常出现在 DOM 事件处理程序中。

'use strict'
var button = document.getElementById('button');

button.addEventListener('click', function {
  // web-api invokes with this bound to current-target in DOM
  this.classList.toggle('on');
});

var button = document.getElementById('button');

button.addEventListener('click', () => {
  // TypeError; 'use strict' -> no global this
  this.classList.toggle('on');
});

This is also the reason why in frameworks like Angular 2+ and Vue.js expect the template-component binding methods to be regular function / methods as this for their invocation is managed by the frameworks for the binding functions.这也是为什么像角2+Vue.js框架期望为模板组件绑定的方法是定时功能/方法的原因this为他们的调用由为绑定功能的框架进行管理。 (Angular uses Zone.js to manage an async context for invocations of view-template binding functions.) (Angular 使用 Zone.js 来管理视图模板绑定函数调用的异步上下文。)

On the other hand, in React , when we want pass a component's method as an event-handler, for example, <input onChange={this.handleOnchange} /> , we should define handleOnchanage = (event)=> {this.props.onInputChange(event.target.value);} as an arrow function as for every invocation.另一方面,在React 中,当我们想要将组件的方法作为事件处理程序传递时,例如<input onChange={this.handleOnchange} /> ,我们应该定义handleOnchanage = (event)=> {this.props.onInputChange(event.target.value);}作为每次调用的箭头函数。 We want this to be the same instance of the component that produced the JSX for the rendered DOM element.我们希望它与为渲染的 DOM 元素生成 JSX 的组件实例相同。


This article is also available in my Medium publication.这篇文章也可以在我的 Medium出版物中找到。 If you like the article, or have any comments and suggestions, please clap or leave comments on Medium .如果你喜欢这篇文章,或者有什么意见和建议,请在Medium鼓掌留言

In a simple way,简单来说,

var a = 20; function a() {this.a = 10; console.log(a);}
//20, since the context here is window.

Another instance:另一个例子:

var a = 20;
function ex(){
    this.a = 10;
    function inner(){
        console.log(this.a); // Can you guess the output of this line?
    }
    inner();
}
var test = new ex();

Ans: The console would print 20.答:控制台会打印 20。

The reason being whenever a function is executed its own stack is created, in this example the ex function is executed with the new operator so a context will be created, and when inner is executed it JavaScript would create a new stack and execute the inner function in a global context though there is a local context.原因是每当一个函数被执行时,它自己的堆栈就会被创建,在这个例子中, ex函数是用new操作符执行的,因此将创建一个上下文,当执行inner时,JavaScript 将创建一个新堆栈并执行inner函数尽管存在局部上下文,但在global context中。

So, if we want the inner function to have a local context, which is ex , then we need to bind the context to the inner function.因此,如果我们希望inner函数具有局部上下文,即ex ,那么我们需要将上下文绑定到内部函数。

Arrows solve this problem.箭头解决了这个问题。 Instead of taking the Global context , they take the local context if any exist.它们不采用Global context ,而是采用local context如果存在)。 In the *given example, it will take new ex() as this .在*给定的示例中,它将采用new ex()作为this

So, in all cases where binding is explicit, arrows solve the problem by defaults.因此,在所有绑定是显式的情况下,默认情况下箭头可以解决问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM