当前位置: 首页 > Web前端 > HTML

【翻译】JS箭头函数三重问:为什么用,怎么用,什么时候用

时间:2023-04-02 15:53:07 HTML

现代JS中最值得期待的特性就是箭头函数,用=>标示。箭头函数有两个主要优点:一个是非常简洁的语法,另一个是直观的范围和this绑定。由于这些优点,箭头函数优于其他形式的函数声明。例如,流行的airbnbeslint配置库强制使用JavaScript箭头函数来创建匿名函数。然而,就像世界上的一切事物一样,箭头函数有一些优点,也有一些“缺点”,这需要在使用时进行一些权衡。学习如何取舍是用好箭头函数的关键。在这篇文章中,我们将回顾箭头函数的工作原理,然后深入探讨箭头函数如何在实际代码中改进我们的代码,以及一些不推荐使用箭头函数的情况。什么是箭头函数JS的箭头函数大概就像python中的lambda(python中定义匿名函数的关键字)和ruby中的blocks(类似于闭包)。这些匿名函数有其特殊的语法:首先接收一定数量的参数,然后在定义它们的函数范围内或最近的范围内执行它们。接下来我们将详细探讨这些。箭头函数的语法箭头函数有一个通用的结构,但也有很多可以简化的特殊情况。核心结构如下:(argument1,argument2,...argumentN)=>{//functionbody}括号里是一系列的参数,后面跟着一个箭头符号=>,最后是函数体。这与传统函数非常相似,只是我们省略了function关键字并在参数后添加了=>。而且,这里还有很多情况让箭头函数结构更加简洁。第一,如果函数体是单个表达式,可以省略花括号,直接将表达式写在一行中,表达式的结果将由函数直接返回。例如:constadd=(a,b)=>a+b;其次,如果这是单个参数,你也可以省略参数部分的括号。例如:constgetFirst=array=>array[0];如您所见,这看起来更简洁,我们稍后会解释更多功能。高级语法如果您了解这些高级语法,将会非常有用。首先,如果你试图在一行中写一个函数,但是返回值是一个对象内容,你打算这样写:(name,description)=>{name:name,description:description};问题是这种语法会造成歧义,你会误以为你写的是函数的函数体。如果要返回单个对象,请将对象括在括号中:(name,description)=>({name:name,description:description});封闭上下文范围不像其他形式的函数,箭头函数没有自己的执行上下文。实际上,这意味着代码中的this和arguments都是从它们的父函数继承的。例如,比较下面的箭头函数和传统函数的区别:控制台日志(参数);};},createArrowFunction:function(){return()=>{console.log(this.name);控制台日志(参数);};}};我们有一个对象有两个方法,每个方法都返回一个匿名函数。区别在于第一种方法使用传统的函数表达式,而后者使用箭头函数表达式。如果我们使用相同的参数运行它,我们会得到两个不同的结果。constanon=test.createAnonFunction('hello','world');//返回匿名函数constarrow=test.createArrowFunction('hello','world');anon();//undefined//{}//this->windowarrow();//testobject//object{'0':'hello','1':'world'}//this->test第一个匿名函数有自己的上下文(指向nottestobject),调用时没有引用this.name属性,(注意:this现在指向window),也没有创建时调用的参数。另一方面,箭头函数与创建它的函数具有相同的上下文,使其可以访问参数和对象。箭头函数改进你的代码传统lambda函数的主要用例之一,即使用函数进行数组遍历,现在使用JavaScript箭头函数实现。例如,如果你有一个包含值的数组,并且你想映射每个项目,强烈建议使用箭头函数:constwords=['hello','WORLD','Whatever'];constdowncasedWords=words.map(word=>word.toLowerCase());一个极其常见的例子是返回一个对象的某个值:constnames=objects.map(object=>object.name);同样,当使用forEach代替传统的for循环时,箭头函数实际上会直观地将this从父级保留下来this.examples.forEach(example=>{this.runExample(example);});Promise和Promisechain在写异步程序的时候,箭头函数也会让代码更加直观和简洁。Promise使编写异步程序变得更容易。虽然你很乐意使用async/await,但你也需要很好地理解promises,因为这是它们的基础。使用promises,您仍然需要在代码执行完成后定义一个回调函数。这是箭头函数的理想位置,尤其是当生成的函数是有状态的并且同时想要引用对象中的某些内容时。this.doSomethingAsync().then((result)=>{this.storeResult(result);});对象转换箭头函数的另一个常见且非常有用的用途是封装对象转换。例如,在Vue.js中,有一种常见的模式是使用mapState将Vuex存储的各个部分直接包含到Vue组件中。这涉及定义一组用于将原始对象转换为完整输出的映射器,这在组件问题中确实是必需的。对于这一系列简单的转换,使用箭头函数是最合适的。例如:exportdefault{computed:{...mapState({results:state=>state.results,users:state=>state.users,});}}什么时候不应该使用箭头函数箭头函数有很多场景是不推荐的,在这种情况下不仅没有帮助,反而会造成不必要的麻烦。首先是对象中的方法。这里有一个函数上下文的例子,对我们的理解很有帮助。曾经有一个流行趋势是使用类语法和箭头函数为其自动绑定方法。比如:事件方法可以用,但是还是绑定在class类中。它看起来像下面的例子:classCounter{counter=0;handleClick=()=>{this.counter++;}}在这个方法中,如果被点击事件函数调用,不在Counter的上下文中,仍然可以访问实例的数据,这个方法的缺点是不言而喻的。这种方法确实为绑定函数提供了一种快捷方式,但是函数的表达形式多种多样,非常不直观。如果你试图在原型中使用这种对象,不利于测试,同时会引发很多问题。相反,建议使用常规的绑定方式,必要时可以在实例构造函数中绑定:classCounter{counter=0;handleClick(){this.counter++;}constructor(){this.handleClick=this.handleClick.bind(this);}}深度调用使用箭头函数的另一种让人头疼的问题是,你使用很多函数的组合来调用,尤其是函数的深度调用。道理很简单,和匿名函数一样,stacktrace很复杂。如果您的功能仅低于一级,而不是深入迭代,这不是什么大问题。但是如果你将函数定义为箭头函数,并在它们之间来回调用,你在调试bug的时候就会被代码弄糊涂,甚至会得到如下错误信息:{anonymous}(){anonymous}(){anonymous}(){anonymous}(){anonymous}()//anonymous具有动态上下文的匿名函数还有一种最容易混淆的情况是箭头函数会让你感到困惑,那就是当this是动态绑定的时候。如果你在以下几种情况下使用箭头函数,那么this的动态绑定将无法正常工作,你也会对为什么代码没有按预期工作感到困惑,并且会给你后面的人带来麻烦。一些典型的例子:事件的调用函数,在jquery中this指向当前的target属性,在vue中多数时候this指向当前选中的元素,methods和computed中的this指向vue组件。当然,你也可以在上述情况下谨慎使用箭头函数。但尤其是在jquery和vue的情况下,这通常会干扰正常的功能,并使您感到困惑,为什么看起来像别人的代码的代码却不起作用。总结箭头函数是JS语言的一个非常特殊的属性,在很多情况下使得代码更加不可预测。但是,与其他语言功能一样,它们也有自己的优点和缺点。因此,我们应该只将它作为一个工具来使用,而不是简单地将它们全部替换为箭头函数。