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

IT技术 javascript lambda ecmascript-6 ecmascript-harmony arrow-functions
2021-01-30 00:11:42

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

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

  • 不要用工作new创建时,不能使用prototype
  • this在初始化时固定绑定到范围

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

  • “在他们工作的任何地方”,即在任何地方,函数都不必与this变量无关,我们也没有创建对象。
  • 只有“需要它们的地方”,即需要绑定到某个范围的事件侦听器、超时
  • 具有“短”功能,但不具有“长”功能
  • 仅适用于不包含其他箭头函数的函数

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

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

6个回答

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

  • 使用function在全球范围和Object.prototype性质。
  • 使用class的对象构造。
  • =>在其他地方使用

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

  1. 范围安全:当一致地使用箭头函数时,保证一切都thisObject与根相同如果即使是单个标准函数回调与一堆箭头函数混合在一起,范围也有可能变得混乱。
  2. 紧凑性:箭头函数更易于读写。(这可能看起来很自以为是,所以我将进一步举几个例子。)
  3. 清晰:当几乎所有东西都是一个箭头函数时,任何正则function立即突出来定义范围。开发人员可以随时查找下一个更高的function语句,看看它thisObject什么

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

  1. 指示不应访问thisObject.
  2. window对象(全局范围)是最好的明确处理。
  3. 许多Object.prototype定义都存在于全局范围内(thinkString.prototype.truncate等),并且这些定义通常必须是类型function的。始终function在全局范围内使用有助于避免错误。
  4. 全局作用域中的许多函数是旧式类定义的对象构造函数。
  5. 函数可以命名为1这有两个好处:(1) 编写起来function foo(){}不像const foo = () => {}——特别是在其他函数调用之外。(2) 函数名称显示在堆栈跟踪中。虽然命名每个内部回调会很乏味,但命名所有公共函数可能是一个好主意。
  6. 函数声明被提升,(意味着它们可以在声明之前被访问),这是静态实用函数中的一个有用属性。

对象构造器

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

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

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

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

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

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

我希望最终不鼓励使用前一种符号。对象构造函数符号可能仍然被一些人用于简单的匿名对象工厂,其中对象是通过编程生成的,但不是其他很多。

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

箭头函数的可读性

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

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

以这段(特别令人困惑的)代码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;
        })
}

具有常规功能的同一段代码:

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为任何真正应该是类方法或类的东西保留关键字。


笔记

  1. ES6 规范中推迟命名箭头函数它们可能仍会在未来版本中添加。
  2. 根据草案规范,只要类不使用关键字“类声明/表达式创建一个构造函数/原型对与函数声明完全相同”extend一个细微的区别是类声明是常量,而函数声明不是。
  3. 关于单语句箭头函数中的块的注意事项:我喜欢在单独调用箭头函数用于副作用(例如,赋值)的任何地方使用块。这样很明显可以丢弃返回值。
我实际上认为在示例 3 中,常规函数更具可读性。即使是非程序员也可以预测正在发生的事情。使用箭头,您需要确切地知道它们如何工作才能理解该示例。也许更多的换行符会帮助箭头示例,但我不知道。只是我的 2 美分,但箭头让我感到畏缩(但我还没有使用它们,所以我可能很快就会被转换。)
2021-03-11 00:11:42
@Spencer 这是一个公平的观点。根据我自己的经验,=>随着时间的推移最终会变得更好。我怀疑非程序员对这两个示例会有非常不同的感受。如果您正在编写 ES2016 代码,您通常也不会最终使用这么多箭头函数。在这个例子中,使用 async/await 和数组推导式,你最终会在reduce()调用中只有一个箭头函数
2021-03-11 00:11:42
另一个你想使用的时间function是当你不想this被束缚时,对吧?我最常见的场景是事件,您可能想要this引用触发事件的对象(通常是 DOM 节点)。
2021-03-20 00:11:42
我完全同意 Spencer 的观点,即在该示例中,常规函数的可读性要高得多。
2021-03-30 00:11:42
很好的答案,谢谢!我个人也尽可能在全局范围内使用箭头。这让我几乎没有“功能”。对我来说,代码中的“函数”意味着需要突出并仔细考虑的特殊情况。
2021-04-05 00:11:42

根据提案,箭头旨在“解决和解决传统函数表达式的几个常见痛点”。他们打算通过this词法绑定和提供简洁的语法来改进问题

然而,

  • 不能在this词汇上始终如一地绑定
  • 箭头函数语法微妙而含糊不清

因此,箭头函数创造了混淆和错误的机会,应该从 JavaScript 程序员的词汇表中排除,用function完全代替

关于词汇 this

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);
};

箭头函数旨在解决我们需要访问this回调内部属性的问题已经有几种方法可以做到这一点:可以分配this给一个变量,使用bind,或使用Array聚合方法上可用的第三个参数然而箭头似乎是最简单的解决方法,因此该方法可以像这样重构:

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

但是,请考虑代码是否使用了像 jQuery 这样的库,其方法是this专门绑定的现在,有两个this值需要处理:

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

我们必须使用function以便动态each绑定this我们不能在这里使用箭头函数。

处理多个this值也可能令人困惑,因为很难知道this作者在谈论哪个

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

作者真的打算打电话Book.prototype.reformat吗?还是他忘记绑定了this,打算打电话Reader.prototype.reformat如果我们将处理程序更改为箭头函数,我们同样会怀疑作者是否想要 dynamic this,但选择了一个箭头,因为它适合一行:

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

一个人可能会提出:“箭头有时可能是错误的函数使用是不是很特殊?也许如果我们很少需要动态this值,那么大多数时间使用箭头仍然可以。”

但问问自己:“调试代码并发现错误的结果是由‘边缘情况’引起的,这是否‘值得’?”我宁愿不仅在大多数时候避免麻烦,而且100% 的时间。

有一个更好的方法:始终使用function(因此this可以始终动态绑定),并且始终this通过变量引用变量是词法的,有很多名字。分配this给变量将使您的意图明确:

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.
  • Backbone(昨天的下载量约为 22k)定义了访问this.
  • 事件 API(如 DOM)指的是EventTargetwith this
  • 修补或扩展的原型API 引用带有this.

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

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

词法this有时是预期的,但有时不是;正如动态this有时是预期的,但有时不是。值得庆幸的是,有一种更好的方法,它总是产生和传达预期的绑定。

关于简洁的语法

箭头函数成功地为函数提供了“更短的句法形式”。但是这些较短的函数会让你更成功吗?

x => x * x“容易阅读”比function (x) { return x * x; }也许是这样,因为它更有可能产生一行简短的代码。根据戴森的《阅读速度和行长对屏幕阅读效率的影响》

中等行长(每行 55 个字符)似乎支持以正常和快速速度进行有效阅读。这产生了最高层次的修真。. .

条件(三元)运算符和单行if语句也有类似的理由

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

有人可能会提出,“如果可能,只使用简短版本的简短功能怎么样?”。但现在一个文体规则与语言约束相矛盾:“尽量使用最短的函数符号,记住有时只有最长的符号才能this按预期绑定。” 这种混淆使得箭头特别容易被误用。

箭头函数语法有很多问题:

const a = x =>
    doSomething(x);

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

这两个函数在语法上都是有效的。而是doSomethingElse(x);不在的体内b这只是一个缩进很差的顶级语句。

当扩展到块形式时,不再有一个隐含的return,人们可能会忘记恢复。但是这个表达式可能只是为了产生副作用,所以谁知道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);
};

可以将用作剩余参数的内容解析为扩展运算符:

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

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

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

块看起来像对象:

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

这是什么意思?

() => {}

作者是打算创建一个空操作,还是一个返回空对象的函数?(考虑到这一点,我们是否应该放在{之后=>?我们是否应该仅将自己限制在表达式语法上?这将进一步降低箭头的频率。)

=>看起来像<=>=

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

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

要立即调用箭头函数表达式,必须放在()外面,但放在()里面是有效的并且可能是有意的。

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

虽然,如果有人(() => doSomething()());打算写一个立即调用的函数表达式,那么什么都不会发生。

考虑到上述所有情况,很难说箭头函数“更容易理解”。一个可以学会利用这种语法要求所有的特殊规则。是不是真的值得吗?

的语法function非常普遍。function专门使用意味着语言本身可以防止人们编写令人困惑的代码。为了编写在所有情况下都应该在语法上理解的过程,我选择function.

关于指南

您要求一份需要“清晰”和“一致”的指南。使用箭头函数最终将导致语法有效、逻辑无效的代码,两种函数形式交织在一起,有意义且任意。因此,我提供以下内容:

ES6 中的函数符号指南:

  • 始终使用function.
  • 始终分配this给变量。不要使用() => {}.
不是箭头函数,而是thisJavascript 的问题。不应被隐式绑定,而this应作为显式参数传递。
2021-03-15 00:11:42
“有多种做事方式为工作场所和语言社区中的争论和分歧创造了不必要的载体。如果语言语法不允许我们做出错误的选择,那就更好了。” 太同意了 写得不错!我认为箭头函数实际上是一种退步。在另一个主题上,我希望我的同事不要再尝试将 JavaScript 转换为带有一系列 .prototype 定义的 C#。那真令人恶心。我应该匿名链接你的帖子:)
2021-03-16 00:11:42
始终使用函数(因此它始终可以动态绑定),并且始终通过变量引用它。 “。我不能同意更多!
2021-03-25 00:11:42
有趣的写一个函数式程序员对 JavaScript 的看法。我不确定我是否同意私有变量的论点。IMO 很少有人真正需要它们;那些这样做的人可能还需要其他合同功能,并且无论如何都会选择像 TypeScript 这样的语言扩展。我当然可以看到 aself而不是 this的吸引力您陈述的箭头函数陷阱也都是有效的,并且与其他可以不使用大括号的语句相同的标准在这里也绝对适用;否则,我认为根据你的论点,人们也可以到处提倡箭头函数。
2021-03-30 00:11:42
写得很好!虽然我不同意你的大部分观点,但考虑相反的观点很重要。
2021-04-02 00:11:42

创建箭头函数是为了简化函数scopethis通过使其更简单来解决关键字。他们使用=>看起来像箭头语法。

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

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

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

上面的代码片段将引用 anobject并打印出 name "RajiniKanth"让我们探索下面的片段,看看这会指出什么。

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呢?

在这里 this 将比 指window objectinner function是它的脱落scope因为this, 总是引用它所在函数的所有者,在这种情况下 - 因为它现在超出范围 - 窗口/全局对象。

当它在 anobject的方法中时——function的所有者是对象。因此this关键字绑定到对象。然而,当它在一个函数内部时,无论是独立的还是在另一个方法中,它都将始终引用该window/global对象。

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

fn(); // [object Window]

我们的 ES5 本身有办法解决这个问题。在深入研究 ES6 箭头函数之前,让我们先研究一下如何解决它。

通常,您会在方法的内部函数之外创建一个变量。现在的‘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();

使用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();

现在有了 ES6 箭头函数,我们可以用更简单的方式处理词法范围问题。

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

Actor.showMovies();

箭头的功能更像是函数语句,只是它们结合父母范围如果箭头函数在顶部作用域中,则this参数将引用窗口/全局作用域,而常规函数内的箭头函数的 this 参数将与其外部函数相同。

使用箭头函数在创建时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

在上面的例子中,我们失去了对 this 的控制。我们可以通过使用this的变量引用来解决上面的例子bind使用 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

何时不使用箭头函数

在对象字面量内。

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

Actor.getName();

Actor.getName与箭头函数来定义,但在调用它提示未定义,因为this.nameundefined作为上下文仍然window

发生这种情况是因为箭头函数在词汇上将上下文与window object... 即外部作用域绑定在一起执行this.name相当于window.name,这是未定义的。

对象原型

在 上定义方法时,同样的规则适用prototype object而不是使用箭头函数来定义 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

调用构造函数

this在构造调用中是新创建的对象。执行 new Fn() 时, 的上下文constructor Fn是一个新对象:this instanceof Fn === true

this 是从封闭上下文中设置的,即外部作用域,这使得它不会分配给新创建的对象。

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

具有动态上下文的回调

箭头函数context在声明时静态绑定,无法使其动态化。将事件侦听器附加到 DOM 元素是客户端编程中的一项常见任务。一个事件以 this 作为目标元素触发处理函数。

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

this是在全局上下文中定义的箭头函数中的窗口。当点击事件发生时,浏览器尝试调用带有按钮上下文的处理函数,但箭头函数不会改变其预定义的上下文。this.innerHTML相当于window.innerHTML并且没有意义。

您必须应用一个函数表达式,它允许根据目标元素进行更改:

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

当用户点击按钮时,this处理函数中就是按钮。因此this.innerHTML = 'Clicked button'正确修改按钮文本以反映单击状态。

参考

应该 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();
2021-03-11 00:11:42
好吧,我必须承认,“最好的在于中间”赞成声明,箭头函数不会涵盖任何可能的函数用例。它们实际上只是为了解决一部分常见问题而设计的。完全切换到它们将是一种矫枉过正。
2021-03-25 00:11:42
“使用绑定将引用该方法的 this 关键字附加到方法的内部函数”行之后的代码。里面有bug。你测试过你的其他例子吗?
2021-03-30 00:11:42
@DmitriPavlutin:检查我更新的帖子,它收集了很多东西......也许我应该发布一个参考。
2021-04-04 00:11:42
一个using bind to attach the this keyword that refers to the method to the method’s inner function.有语法错误。
2021-04-09 00:11:42

箭头函数 - 迄今为止使用最广泛的 ES6 特性......

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

箭功能应该 不会 被使用:

  1. 当我们想要函数提升时
    • 因为箭头函数是匿名的。
  2. 当我们想在函数中使用this/arguments
    • 由于箭头函数没有自己的this/ arguments,它们依赖于它们的外部上下文。
  3. 当我们想使用命名函数时
    • 因为箭头函数是匿名的。
  4. 当我们想使用函数作为 constructor
    • 因为箭头函数没有自己的this.
  5. 当我们想在对象字面量中添加函数作为属性并在其中使用对象时
    • 因为我们无法访问this(应该是对象本身)。

让我们了解一些箭头函数的变体以更好地理解:

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

ES5 版本

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

ES6 版本

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

笔记:

function是关键字不是必需的。 =>是必须的。 {}是可选的,当我们不提供{} return时由 JavaScript 隐式添加,当我们提供时,{}我们需要在需要时添加return

变体2:当我们想通过一个参数传递给函数,并从它返回一定的value。

ES5 版本

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

ES6 版本

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

笔记:

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

变体3:当我们希望传递任何参数传递给函数,做希望返回任何值。

ES5 版本

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

ES6 版本

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

变体 4:当我们想要明确地从箭头函数返回时。

ES6 版本

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

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

ES6 版本

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

笔记:

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

变体 6:箭头函数没有自己的arguments(类似数组的对象)。它们取决于 的外部上下文arguments

ES6 版本

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

笔记:

foo是一个 ES5 函数,有一个arguments类似对象数组和一个传递给它的参数2arguments[0]因为foo是 2。

abc是一个ES6箭头功能,因为它并没有拥有自己的arguments因此它打印arguments[0]foo其外上下文来代替。

变体7:箭头功能并没有拥有this自己的它们依赖于外部语境this

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

笔记:

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

undefined: Katty

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

笔记:

传递给回调setTimeout是一个ES6箭头功能,它并没有拥有自己的this,所以需要从它的外部环境正在greetUser拥有this也就是说obj6,因此我们得到输出:

Hi, Welcome: Katty

各种各样的:

  • 我们不能使用new箭头函数。
  • 箭功能并不能prototype属性。
  • 我们没有绑定this何时通过apply调用箭头函数call

我仍然支持在这个线程的第一个答案写的所有内容然而,我对代码风格的看法从那时起就发展了,所以我对这个问题有了一个新的答案,这个答案建立在我上一个的基础上。

关于词汇 this

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

我的信念是:我们不应该首先使用this因此,如果一个人this在他的代码中刻意避免使用,那么this箭头的“词法”特征几乎没有value。而且,在this坏事的前提下,箭的治疗this也算不上什么“好事”;相反,它更像是另一种糟糕的语言功能的损害控制形式。

我认为有些人不会发生这种情况,但即使是那些会发生这种情况的人,他们也必须发现自己在this每个文件出现一百次的代码库中工作,并且一点(或很多)损害控制就是全部一个通情达理的人可以希望。因此,在某种程度上,当箭头使糟糕的情况变得更好时,它们可能是好的。

即使this使用箭头编写代码比没有它们更容易,使用箭头的规则仍然非常复杂(请参阅:当前线程)。因此,正如您所要求的那样,指导方针既不“明确”也不“一致”。即使程序员知道箭头的歧义,我认为他们仍然耸耸肩并接受它们,因为词法的value使this它们黯然失色。

所有这些都是以下认识的序言:如果不使用this,那么this通常由箭头引起的歧义就变得无关紧要了。在这种情况下,箭头变得更加中性。

关于简洁的语法

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

换句话说:该死的,我也想要单行函数!

关于指南

考虑到this-neutral 箭头函数的可能性,以及值得追求的简洁性,我提供以下更宽松的指导方针:

ES6 中的函数符号指南:

  • 不要使用this.
  • 对按名称调用的函数使用函数声明(因为它们被提升)。
  • 对回调使用箭头函数(因为它们往往更简洁)。
100% 同意底部的“ES6 函数表示法指南”部分 - 特别是提升和内联回调函数。很好的答案!
2021-03-25 00:11:42