关于javascript:我什么时候应该在ECMAScript 6中使用Arrow功能?

When should I use Arrow functions in ECMAScript 6?

这个问题是针对那些在即将到来的EcmaScript6(Harmony)环境中考虑过代码风格并且已经使用过该语言的人。

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

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

  • 不要和new合作
  • 固定的this在初始化时绑定到作用域

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

  • "它们在任何地方工作",也就是说,在任何地方,函数都不必对this变量不可知,我们也不创建对象。
  • 只有"它们需要的任何地方",即事件侦听器、超时,需要绑定到特定范围
  • 具有"短"功能,但不具有"长"功能
  • 仅与不包含其他箭头函数的函数一起使用

我要寻找的是在未来版本的ecmascript中选择适当的函数符号的指南。该指导方针需要清晰,以便能够向团队中的开发人员传授,并且保持一致,这样就不需要在一个函数表示法和另一个函数表示法之间来回不断地重构。


不久前,我们的团队将其所有代码(中型AngularJS应用程序)迁移到使用tracerbabel编译的javascript中。我现在对ES6及更高版本的函数使用以下经验法则:好的。

  • 在全局范围内使用function和用于Object.prototype属性。
  • 对象构造函数使用class
  • 在其他地方使用=>

为什么几乎在任何地方都使用箭头函数?好的。

  • 范围安全:如果始终使用arrow函数,则保证所有内容都使用与根相同的thisObject。如果一个标准函数回调与一组箭头函数混合在一起,那么范围就有可能变得混乱。
  • 紧凑性:箭头函数更容易读写。(这似乎是固执己见的,所以我将进一步举几个例子)。
  • 清晰:当几乎所有东西都是箭头函数时,任何规则的function都会立即突出定义范围。开发人员总是可以查看下一个更高的function语句,以查看thisObject是什么。
  • 为什么总是在全局范围或模块范围内使用常规函数?好的。

  • 表示不应访问thisObject的函数。
  • window对象(全局范围)最好是明确地解决。
  • 许多Object.prototype定义都存在于全球范围内(如String.prototype.truncate等),而且这些定义通常都必须是function类型。在全局范围内持续使用function有助于避免错误。
  • 全局范围中的许多函数都是旧样式类定义的对象构造函数。
  • 函数可以命名为1。这有两个好处:(1)与const foo = () => {}相比,写入function foo(){}并不难,尤其是在其他函数调用之外。(2)函数名显示在堆栈跟踪中。虽然命名每个内部回调都很繁琐,但命名所有公共函数可能是一个好主意。
  • 函数声明被提升(意味着可以在声明之前访问它们),这是静态实用程序函数中的一个有用属性。
  • 对象构造函数好的。

    试图实例化arrow函数时引发异常:好的。

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

    因此,与箭头函数相比,函数的一个主要优点是函数可以作为对象构造函数使用:好的。

    1
    2
    3
    function Person(name) {
        this.name = name;
    }

    但是,功能上相同的2 ES Harmony草稿类定义几乎是紧凑的:好的。

    1
    2
    3
    4
    5
    class Person {
        constructor(name) {
            this.name = name;
        }
    }

    我预计使用前一个符号最终会受到阻碍。对象构造函数符号可能仍然被一些简单的匿名对象工厂所使用,这些工厂中的对象是以编程方式生成的,但对于其他工厂则不是如此。好的。

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

    箭头函数的可读性好的。

    坚持常规函数的最好理由可能是箭头函数的可读性不如常规函数。如果您的代码一开始不起作用,那么箭头函数可能看起来不必要,并且如果箭头函数不被一致使用,它们看起来很难看。好的。

    ECMAScript在ECMAScript 5.1给我们提供了函数Array.forEachArray.map和所有这些函数编程功能之后发生了很大的变化,这些功能编程功能使我们使用了以前使用for循环的函数。异步JavaScript已经有了很大的发展。ES6还将发送一个Promise对象,这意味着更多的匿名函数。函数式编程没有回头路。在函数式JavaScript中,箭头函数比常规函数更可取。好的。

    例如,这段代码(特别令人困惑)3:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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;
            })
    }

    具有常规函数的同一段代码:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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));
    }

    虽然任何一个箭头函数都可以被一个标准函数替换,但是这样做的好处很小。哪个版本更可读?我会说第一个。好的。

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

    笔记好的。

  • ES6规范中的命名箭头函数已被延迟。它们可能仍会被添加到将来的版本中。
  • 根据规范草案"类声明/表达式创建构造函数-函数/原型对与函数声明完全相同",只要类不使用extend关键字。一个小的区别是类声明是常量,而函数声明不是。
  • 注意单语句箭头函数中的块:我喜欢在仅为副作用(例如赋值)调用箭头函数的任何地方使用块。这样很明显,返回值可以被丢弃。
  • 好啊。


    根据该提案,arrows的目标是"解决和解决传统Function Expression"的几个共同问题,他们打算通过在词汇上绑定this,并提供简洁的语法来改善问题。好的。

    然而,好的。

    • 不能在词汇上一致地绑定this
    • 箭头函数语法微妙且不明确

    因此,arrow函数为混淆和错误创造了机会,应该从javascript程序员的词汇表中排除,只替换为function。好的。

    关于词汇this。好的。

    this有问题:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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函数旨在解决需要在回调中访问this的属性的问题。有几种方法可以做到这一点:可以将this赋给变量,使用bind或使用Array聚合方法上的第三个参数。然而,箭头似乎是最简单的解决方法,因此可以这样重构该方法:好的。

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

    但是,考虑代码是否使用了jquery之类的库,其方法专门绑定this。现在,有两个this值需要处理:好的。

    1
    2
    3
    4
    5
    6
    7
    Book.prototype.render = function () {
        var book = this;
        this.$pages.each(function (index) {
            var $page = $(this);
            book.draw(book.currentPage + index, $page);
        });
    };

    为了使each能够动态地约束this,我们必须使用function。这里不能使用箭头函数。好的。

    处理多个this值也会让人困惑,因为很难知道作者在谈论哪个this:好的。

    1
    2
    3
    4
    5
    function Reader() {
        this.book.on('change', function () {
            this.reformat();
        });
    }

    作者真的打算给Book.prototype.reformat打电话吗?或者他忘了约束以东二十一〔1〕并打算打电话给以东二十一〔19〕?如果我们将处理程序更改为arrow函数,我们同样会怀疑作者是否希望使用动态this,但是选择了一个箭头,因为它适合一行:好的。

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

    人们可能会摆出这样的姿势:"箭头有时可能是错误的功能,这是例外吗?也许,如果我们很少需要动态的this值,那么大多数时候仍然可以使用箭头。"好的。

    但是问问自己:"调试代码并发现错误的结果是由"边缘情况"引起的,这是值得的吗?"我宁愿避免麻烦,不只是大部分时间,而是100%的时间。好的。

    有一种更好的方法:总是使用function(因此this总是可以动态绑定),并且总是通过变量引用this。变量是词法变量,具有许多名称。将EDOCX1[1]分配给变量将使您的意图明确:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    function Reader() {
        var reader = this;
        reader.book.on('change', function () {
            var book = this;
            book.reformat();
            reader.reformat();
        });
    }

    此外,总是将this分配给一个变量(即使只有一个this或没有其他函数)可以确保即使在代码更改后,人们的意图仍然清晰。好的。

    另外,动态this也不例外。jquery在超过5000万个网站上使用(截至2016年2月撰写时)。以下是动态绑定this的其他API:好的。

    • Mocha(昨天下载量约12万)通过this公开了其测试方法。
    • Grunt(昨天下载了约63K)通过this公开了构建任务的方法。
    • 主干网(昨天下载了约22K)定义了访问this的方法。
    • 事件API(类似于DOM)指的是带有thisEventTarget
    • 修补或扩展的原型API指的是使用this的实例。

    (通过http://trends.builtwith.com/javascript/jquery和https://www.npmjs.com进行统计。)好的。

    您可能已经需要动态this绑定。好的。

    词汇this有时是预期的,但有时不是;正如动态this有时是预期的,但有时不是。谢天谢地,有一种更好的方法,它总是产生并传递预期的绑定。好的。

    关于简明扼要的语法好的。

    arrow函数成功地为函数提供了"更短的语法形式"。但是这些较短的功能会让你更成功吗?好的。

    x => x * xfunction (x) { return x * x; }更容易阅读吗?也许是这样,因为它更可能产生一个简短的代码行。根据Dyson的阅读速度和行长对屏幕阅读效果的影响,好的。

    A medium line length (55 characters per line) appears to support effective reading at normal and fast speeds. This produced the highest level of comprehension . . .

    Ok.

    对条件(三元)运算符和单行if语句进行了类似的证明。好的。

    然而,你真的在写提案中宣传的简单数学函数吗?我的域不是数学的,所以我的子程序很少如此优雅。相反,我通常会看到箭头函数打破了列限制,并由于编辑器或样式指南而换行,这会使Dyson定义的"可读性"无效。好的。

    有人可能会摆出这样的姿势:"如果可能的话,只使用简短的函数怎么样?"但是现在,一个风格规则与语言约束相矛盾:"尽量使用最短的函数符号,记住有时只有最长的符号才能像预期的那样绑定this"。这种合并使得箭头特别容易被滥用。好的。

    箭头函数语法有许多问题:好的。

    1
    2
    3
    4
    5
    6
    const a = x =>
        doSomething(x);

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

    这两个函数在语法上都是有效的。但是doSomethingElse(x);不在b的主体中,它只是一个缩进很差的顶级声明。好的。

    当扩展到块形式时,就不再有一个隐式的return,人们可以忘记恢复它。但是,这个表达可能只是为了产生副作用,所以谁知道将来是否需要一个明确的return?好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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);
    };

    可以将要用作rest参数的内容解析为spread运算符:好的。

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

    赋值可能与默认参数混淆:好的。

    1
    2
    3
    4
    5
    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

    块看起来像对象:好的。

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

    这是什么意思?好的。

    1
    () => {}

    作者是否打算创建一个no op或返回空对象的函数?(考虑到这一点,我们是否应该将{放在=>之后?我们应该只局限于表达式语法吗?这将进一步降低箭头的频率。)好的。

    =><=>=相似:好的。

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

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

    要立即调用箭头函数表达式,必须将()放在外部,而将()放在内部是有效的,并且可能是有意的。好的。

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

    但是,如果编写(() => doSomething()());的目的是编写一个立即调用的函数表达式,那么什么也不会发生。好的。

    考虑到上述所有情况,很难说arrow函数"更容易理解"。我们可以学习使用这种语法所需的所有特殊规则。它真的值得吗?好的。

    function的语法毫无例外地是广义的。仅使用function意味着语言本身可以防止编写混淆的代码。为了编写在所有情况下都应该被语法理解的过程,我选择function。好的。

    关于指导方针好的。

    您需要一个"清晰"和"一致"的指导原则。使用箭头函数最终将导致语法上有效、逻辑上无效的代码,两种函数形式交织在一起,有意义且任意。因此,我提出以下建议:好的。ES6中的功能符号指南:

    • 始终使用function创建过程。
    • 始终将this赋给变量。不要使用() => {}

    好啊。


    创建箭头函数是为了简化函数scope,并通过使其更简单来求解this关键字。它们使用=>语法,看起来像箭头。好的。

    注意:它不会取代现有的功能。如果用箭头函数替换每个函数语法,它就不会在所有情况下都起作用。好的。

    让我们看一下现有的ES5语法,如果this关键字在对象方法(属于对象的函数)内,它会指什么?好的。

    1
    2
    3
    4
    5
    6
    7
    var Actor = {
      name: 'RajiniKanth',
      getName: function() {
         console.log(this.name);
      }
    };
    Actor.getName();

    上述代码段将引用object,并打印出名称"RajiniKanth"。让我们研究下面的代码片段,看看这里会指出什么。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var Actor = {
      name: 'RajiniKanth',
      movies: ['Kabali', 'Sivaji', 'Baba'],
      showMovies: function() {
       this.movies.forEach(function(movie) {
         alert(this.name +" has acted in" + movie);
       });
      }
    };

    Actor.showMovies();

    那么,如果this关键字在method’s function的内部呢?好的。

    这里指的是window object而不是inner function,因为它是从scope掉出来的。因为this总是引用它所在函数的所有者,对于这种情况?-?既然现在已经超出范围了?-?窗口/全局对象。好的。

    当它在object方法的内部时?-?对象是function的所有者。因此,这个关键字被绑定到对象。然而,当它位于一个函数内部时,无论是独立的还是在另一个方法中,它总是引用window/global对象。好的。

    1
    2
    3
    4
    5
    var fn = function(){
      alert(this);
    }

    fn(); // [object Window]

    在我们的ES5中有很多方法可以解决这个问题,让我们先研究一下这个问题,然后再深入讨论ES6箭头函数如何解决这个问题。好的。

    通常,您会在方法的内部函数之外创建一个变量。现在,‘forEach’方法获得了this的访问权,从而获得了object’s的属性及其值。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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();

    使用bind将引用方法的this关键字附加到method’s inner function上。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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();

    现在使用ES6箭头函数,我们可以更简单地处理lexical scoping问题。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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更像是函数语句,只是它们bind把this改为parent scope。如果arrow function is in top scopethis参数将引用window/global scope,而正则函数内的箭头函数将具有与其外部函数相同的参数。好的。

    对于arrow函数,this在创建时绑定到封闭的scope上,不能更改。新的运算符bind、call和apply对此没有影响。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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

    在上面的例子中,我们失去了对这个的控制。我们可以通过使用this的变量引用或使用bind来解决上述示例。有了ES6,管理this变得更容易,因为它与lexical scoping是绑定的。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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

    当不到箭头功能时

    在对象文本中。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var Actor = {
      name: 'RajiniKanth',
      movies: ['Kabali', 'Sivaji', 'Baba'],
      getName: () => {
         alert(this.name);
      }
    };

    Actor.getName();

    Actor.getName是用arrow函数定义的,但在调用时,它会发出未定义的警报,因为this.nameundefined,因为上下文仍保留在window中。好的。

    发生这种情况的原因是arrow函数在词汇上与window object绑定上下文…即外部范围。执行this.name相当于window.name未定义。好的。

    prototype对象>

    方法applies when the same rule defining在prototype object在线。instead of using an arrow函数defining saycatname method for which brings context window:放任,安>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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新建对象invocation is the created。当执行新的FN(),is the context of the constructor Fnthis instanceof Fn === true在对象。>

    thisis from the setup enclosing context,which makes the outer 1手动指定not created to新建/对象。>

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

    动态语境与回调>

    the function binds箭头contextis not possible在线静态类型和宣言,让动态。attaching事件listeners DOM元素的共同任务是在客户端程序设计。安事件处理函数和触发器the this as the目标元素。>

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

    thisis that is an arrow函数窗口中定义的上下文中的全球。单击的事件发生时,浏览器tries to the button invoke处理函数与函数context does not change,but its箭头预定义的上下文。this.innerHTMLis has to window.innerHTML感和不等价。>

    You have to apply to which a function expression change,this on the allows depending元目标:>

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

    当用户点击the button,this is the button在处理函数。thus this.innerHTML = 'Clicked button'button text to the modifies正确clicked现状的反思。>

    references。rainsoft.io HTTPS:///when not -使用-箭头- / -函数的JavaScript>

    好吧。


    Arrow functions - most widely used ES6 feature so far ...

    用法:除以下情况外,所有ES5函数都应替换为ES6箭头函数:

    不应使用箭头函数:

  • 当我们需要功能提升时
    • 因为箭头函数是匿名的。
  • 当我们想在函数中使用this/arguments
    • 由于arrow函数本身没有this/arguments,因此它们依赖于它们的外部上下文。
  • 当我们想使用命名函数时
    • 因为箭头函数是匿名的。
  • 当我们想使用函数作为constructor
    • 因为arrow函数没有自己的this
  • 当我们想在对象文本中添加函数作为属性并在其中使用对象时
    • 因为我们不能访问this(应该是对象本身)。
  • 让我们了解箭头函数的一些变体,以便更好地了解:

    变量1:当我们想向一个函数传递多个参数并从中返回一些值时。

    ES5版本:

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

    ES6版本:

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

    注:不需要function关键字。需要=>{}是可选的,当我们不提供{}时,return是由javascript隐式添加的,当我们提供{}时,如果需要,我们需要添加return

    变量2:当我们只想向一个函数传递一个参数并从中返回一些值时。

    ES5版本:

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

    ES6版本:

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

    注:当只传递一个参数时,我们可以省略括号()

    变量3:当我们不想向函数传递任何参数并且不想返回任何值时。

    ES5版本:

    1
    2
    3
    4
    var sayHello = function() {
        console.log("Hello");
    };
    sayHello(); //Hello

    ES6版本:

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

    变量4:当我们想要从箭头函数显式返回时。

    ES6版本:

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

    变量5:当我们想要从箭头函数返回一个对象时。

    ES6版本:

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

    注:我们需要将对象括在括号()中,否则javascript无法区分块和对象。

    变体6:arrow函数本身没有arguments(类似数组的对象),它们依赖于arguments的外部上下文。

    ES6版本:

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

    注:foo是一个es5函数,有一个arguments数组样的对象,传递给它的参数是2所以arguments[0]对于foo是2。

    abc是一个es6箭头函数,因为它没有自己的arguments,因此它打印fooarguments[0],而不是外部上下文。

    变体7:arrow函数本身没有this,它们依赖于this的外部上下文。

    ES5版本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var obj5 = {
      greet:"Hi, Welcome",
      greetUser : function(user) {
            setTimeout(function(){
            console.log(this.greet +":" +  user); //"this" here is undefined.
            });
         }
    };

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

    注:传递给setTimeout的回调是一个ES5函数,它有自己的this,在use-strict环境中没有定义,因此我们得到输出:

    1
    undefined: Katty

    ES6版本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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

    注:传递给setTimeout的回调是一个es6 arrow函数,它没有自己的this,所以它是从外部上下文获取的,即greetUser,其中thisobj6,因此我们得到输出:

    1
    Hi, Welcome: Katty

    其他:我们不能将new与arrow函数一起使用。arrow函数没有prototype属性。当通过applycall调用arrow函数时,我们没有this的绑定。


    除了迄今为止的伟大答案,我还想提出一个非常不同的原因,为什么箭头函数在某种意义上比"普通"的javascript函数要好。为了便于讨论,让我们暂时假设我们使用类型检查器,如typescript或facebook的"流"。考虑下面的toy模块,它是有效的ecmascript 6代码加上流类型注释:(我将包括非类型化的代码,这实际上是babel的结果,在这个答案的最后,所以它可以实际运行。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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;};
      }
    }

    现在看看当我们使用来自不同模块的类C时会发生什么,如下所示:

    1
    2
    3
    4
    5
    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!

    如您所见,类型检查器在这里失败了:F2应该返回一个数字,但它返回了一个字符串!

    更糟糕的是,似乎没有一个可想象的类型检查器可以处理普通(非箭头)的javascript函数,因为f2的"this"不会出现在f2的参数列表中,因此"this"所需的类型不可能添加为f2的注释。

    这个问题也会影响不使用类型检查程序的人吗?我想是的,因为即使我们没有静态类型,我们也会认为它们就在那里。(第一个参数必须是数字,第二个参数必须是字符串等)隐藏的"this"参数可能会在函数体中使用,也可能不会在函数体中使用,这使得我们的心理簿记更加困难。

    下面是可运行的非类型化版本,它将由babel生成:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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的任何时候使用arrow函数,因为arrow函数不绑定自己的this、arguments、super或new.target。


    函数或从箭头(6),在ES。在除了其优雅的语法功能差最小,最著名的是一个函数的范围内this之箭

    In regular function expressions, the this keyword is bound to different values based on the context in which it is called.

    Ok.

    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.

    Ok.

    限制的方法,在一个箭头的函数对象 ;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 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]

    在的情况下,采用常规法objA.print()print()定义function ;恰当处理,它是由this到解决方法调用失败的时候,但objAarrow=>定义一个函数。这是因为当被this在调节功能的方法,在一个对象(objA)是对象本身。然而,在的情况下一个箭头的函数,得到的thislexically绑定到它的覆盖范围this(全球/窗口的定义是在我们的案例中,它stays stays)和相同的方法objA调用它的在线。

    在一个箭头的优势函数的正则函数法(S)的一个对象,但只有当预计是固定this&;束缚在时间的定义。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    /* 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]

    在的情况下定义的方法是在print()objB.print()函数是invokes console.log([ this.id } { { }】→this.name )asynchronously回调的在线setTimeout ;this分辨正确,当一对箭头objB回调函数是用来当回调,但失败的定义是规则空气的功能。这是因为中箭=>函数对setTimeout(()=>..)闭上lexically从母体thisIE调用),它objB.print()定义。。。。。。。。。。。。。。在其他的话,=>函数通过在箭头的方向objBsetTimeout(()==>...奥曲肽对其this因为在调用objB.print()this)是objB本身。

    我们可以很容易地使用,使Function.prototype.bind(),在回调函数中定义的规则,由它来this的正确结合。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const objB = {
     id: 20,
     name:"Singh",
     print () { // same as print: function()
      setTimeout( (function() {
        console.log(`[${this.id} -> ${this.name}]`);
      }).bind(this), 1)
     }
    }
    objB.print() // logs: [20 -> Singh]

    然而,在箭头的函数和不方便吃的错误倾向是异步回调的情况,我们知道在哪里this在时间的函数定义的,它应该得到和束缚。

    限制:这需要在箭头的函数的变化,在invocations

    我们需要它的时候,this函数调用可以在时间的改变,我们不能用箭头的功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /* this = global | Window (enclosing scope) */

    function print() {
       console.log(`[${this.id} -> {this.name}]`);
    }
    const obj1 = {
     id: 10,
     name:"Simar",
     print // 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]

    上面的任何一个函数都不能与箭头函数const print = () => { console.log([$this.id->this.name]);}一起使用,因为this不能更改,并且将继续绑定到定义它的封闭范围(全局/窗口)的this。在所有这些示例中,我们使用不同的对象(obj1obj2逐个调用相同的函数,这两个对象都是在声明print()函数之后创建的。好的。

    这些都是人为的例子,但让我们考虑一些更现实的例子。如果我们必须编写类似于在arrays上工作的方法的reduce()方法,我们再次不能将其定义为lambda,因为它需要从调用上下文(即调用它的数组)推断this。好的。

    因此,不能将constructor函数定义为箭头函数,因为构造函数函数的this在声明时不能设置。每次使用new关键字调用构造函数函数时,都会创建一个新对象,然后将其绑定到该特定调用。好的。

    同样,当框架或系统接受一个回调函数以后用动态上下文this调用时,我们不能再使用arrow函数,因为this可能需要随着每次调用而更改。这种情况通常出现在DOM事件处理程序中。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    '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');
    });

    这也是Angular2+和Vue.js等框架希望模板组件绑定方法是常规函数/方法的原因,因为它们的调用由绑定函数的框架管理。(Angular使用zone.js管理异步上下文以调用视图模板绑定函数)。好的。

    另一方面,在react中,当我们希望将组件的方法作为事件处理程序(例如)传递时,我们应该将handleOnchanage = (event)=> {this.props.onInputChange(event.target.value);}定义为箭头函数,因为对于每个调用,我们希望它与为呈现的dom元素生成JSX的组件的实例相同。好的。

    这篇文章也可以在我的媒体出版物上浏览。如果你喜欢这部作品,或者有什么意见和建议,请拍手或在媒体上留言。好的。好啊。


    简单来说,

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

    另一个实例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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();

    答:控制台会打印20个。< BR>

    原因是,每当执行一个函数时,都会创建它自己的堆栈,在本例中,ex函数是用new运算符执行的,因此会创建一个上下文;当执行inner时,尽管存在一个本地上下文,但js会创建一个新的堆栈并执行inner函数global context

    因此,如果我们希望inner函数具有一个本地上下文,即ex,那么我们需要将上下文绑定到内部函数。

    箭头解决了这个问题,他们不使用global context,而是使用local context(如果存在)。在given example,中,取new ex()this

    因此,在所有绑定是显式箭头的情况下,都可以通过默认值来解决问题。


    我仍然支持我在这条线索的第一个答案中所写的一切。然而,从那时起,我对代码样式的看法就发展起来了,所以我对这个问题有了一个新的答案,它建立在我上一个问题的基础上。

    关于词汇this

    在我最后的回答中,我故意回避了我对这门语言持有的一种潜在的信念,因为它与我所提出的论点没有直接关系。尽管如此,如果没有明确说明这一点,我可以理解为什么许多人在我建议不使用箭头时,会拒绝使用箭头,因为他们发现箭头非常有用。

    我的信念是:我们不应该首先使用this。因此,如果一个人故意避免在代码中使用this,那么箭头的"词汇this"特性就没有价值。另外,在this是一个坏东西的前提下,arrow对this的处理并不是一个"好东西";相反,它更像是另一个坏语言特性的损害控制形式。

    我认为这两种情况都不会发生在某些人身上,但即使是对他们来说,他们也一定会发现自己在代码库中工作,在代码库中,每个文件中的this出现了100次,而一点点(或大量)的损害控制是一个合理的人所希望的。所以箭头在某种程度上可以是好的,当它们使坏的情况好转时。

    即使使用带箭头的this编写代码比不带箭头更容易,使用箭头的规则仍然非常复杂(请参见:当前线程)。因此,正如您所要求的,指导方针既不是"明确的",也不是"一致的"。即使程序员知道箭头的含糊不清,我认为他们还是会耸耸肩接受它们,因为词汇this的值掩盖了它们。

    所有这些都是以下实现的序言:如果不使用this,那么箭头通常导致的this的模糊性就变得不相关了。在此上下文中,箭头变得更加中性。

    关于简明扼要的语法

    当我写第一个答案的时候,我认为即使是对最佳实践的盲目坚持也是值得付出的代价,如果这意味着我可以生成更完美的代码的话。但我最终意识到,简洁也可以作为一种抽象形式来提高代码质量,这足以证明有时偏离最佳实践是合理的。

    换句话说:该死,我也想要一个线性函数!

    关于指导方针

    鉴于this中性箭头功能的可能性,以及简洁性值得追求,我提供了以下更为宽松的指导方针:

    ES6中的功能符号指南:

    • 不要使用this
    • 将函数声明用于按名称调用的函数(因为它们被提升)。
    • 对回调使用箭头函数(因为它们往往比较简洁)。