千万别再一直无脑使用ES6的箭头函数了,它虽然很有用但并不是万能的

简介: 相信很多小伙伴自从知道了ES6的箭头函数以后,都疯狂得使用,渐渐的淡忘了普通函数的使用。不过确实,箭头函数看起来比较简洁,用起来也舒服,不过它的出现是为了解决某一部分问题的,并不是用来替代普通函数的,所以我们不能在每一个地方都使用箭头函数

01

箭头函数的基本使用


我们先来看看箭头函数是如何使用的吧


let fn1 = function () {  console.log('我是普通函数')}
let fn2 = () => {  console.log('我是箭头函数')}
fn1()fn2()
//我是普通函数//我是箭头函数

在这个例子中,fn1fn2是完全等价的,这就是箭头函数的基本使用。


有没有感觉箭头函数特别的简洁?因为他只有几个简单的符号,其实,这并不是他最简洁的时候。我们来看一下给函数传参的例子


let fn1 = function(data) {  console.log(data)}
let fn2 = (data) => {  console.log(data)}


如果函数只需要传一个参数,我们可以去掉箭头函数的小括号;若传入多个参数,小括号还是要加上的哦


let fn2 = data => {  console.log(data)}


其实这个例子中,console.log(data)两边的大括号也是可以去掉的。因为在箭头函数中如果函数的代码部分只有一句代码,是可以省去大括号的


let fn2 = data => console.log(data)


当然这还不是最简单的,当我们函数的代码部分只有一句代码,并且是return语句的话,我们可以省略大括号,并且省略return


let fn2 = data => data  //相当于let fn2 = data => return data


但是,如果return的是一个对象,就尽量不要省略大括号和return了,因为大括号会被解析为代码块。我们可以在对象两边加一个小括号,就可以起到return回一个对象的作用了。


//报错let fn2 = data => {ret: data}  //本意是想return {ret: data}
//不报错let fn2 = data => ({ret: data})  //等价于 let fn2 = data => {return {ret: data}}
//报错let fn2 = data => return {ret: data}
//不报错let fn2 = data => {return {ret: data}}


02

箭头函数的作用


首先ES6新增了箭头函数这个东西,一定是为了解决某个问题。我们先来看看原先没有箭头函数的一个例子


let obj = {    name: '张三',    fn: function () {        return function() {            console.log(this.name)  //本意是想打印 '张三'        }    }}obj.fn()()
//打印结果:undefined


在这个例子中,我们本来是想打印 张三的,但最后却是undefined,这是为什么呢?


普通函数中的this的指向是运行时绑定的,就像这个例子中的,先调用了obj.fn,返回了一个嵌套的匿名函数,此时该匿名函数处于全局中,也就是不在obj这个对象内了,因为普通函数的this是运行时绑定的,所以此时的this指向的是全局,如果在浏览器中,就是window对象。


那么我们的本意是什么呢?希望这个this指向的是obj中的name,那么我们可以这么做


let obj = {    name: '张三',    fn: function () {      let _this = this  //在定义是将this赋值给一个变量_this        return function() {            console.log(_this.name)  //调用被赋值的_this去获取obj里的name        }    }}obj.fn()()
//打印结果: 张三


此时我们就可以在嵌套函数中,获取到obj中的name值了,因为在定义时,就把正确的this保存在一个变量中,并给嵌套函数使用。


那么当箭头函数就可以解决这种情况。普通函数的this是运行时绑定,箭头函数的this定义时绑定。我们将上面例子中的嵌套函数用箭头函数代替


let obj = {    name: '张三',    fn: function () {      //此处有个this,该this指向obj,并且被箭头函数所绑定        return () => {            console.log(this.name)  //这里的this是向上寻找,找到function() {}内有一个this,并与之绑定,而这个this指向的就是obj        }    }}obj.fn()()
//打印结果: 张三


这样就轻松地解决了普通函数this随着运行环境的改变而改变的问题了。


03

箭头函数的注意点


  1. 箭头函数内没有this,如果在箭头函数内使用this,会自动往上寻找,直到找到this才停止寻找。


  1. 箭头函数的this是定义时绑定,而不是运行时绑定


  1. 箭头函数内没有arguments对象


  1. 箭头函数不能作为构造函数,原因也是因为它内部没有自己的this


我们来用几个例子验证这几个注意点


(1)例子1


function fn() {  //此处有个this,指向全局对象,被箭头函数所绑定  return () => {    console.log(this.name)    return () => {      console.log(this.name)    }  }}//给全局对象设置一个属性name,值为 李四window.name = '李四'
//调用fn内的第一个箭头函数fn()()             //返回 '李四'//调用fn内第二个箭头函数fn()()()           //返回 '李四'


在这个例子中,函数fn内部有两个嵌套的箭头函数。


第一个箭头函数内调用this,因为箭头函数内没有this,所以向上找,发现函数fn有一个this,于是就与该this绑定。因为该this指向window,所以console.log(this.name)返回了李四


第二个箭头函数内调用this,因为箭头函数内没有this,所以向上找,因为上一级还是箭头函数,仍然没有this,所以继续向上找,同样发现了函数fn中有一个this指向window,与之绑定,最后也返回了李四


(2)例子2


let fn = function (data1, data2) {    return (data3, data4) => {        console.log(arguments)    }}
fn(1, 2)(3, 4)
//返回 [Arguments] {'0': 1, '1': 2}


函数中有一个arguments对象,它的作用是返回一个函数传入的实参的。在这个例子中,我们在箭头函数中打印的arguments,最后返回的却是普通函数传入的实参内容,说明箭头函数内是没有arguments对象的。


(3)例子3


我们来看看平时申明构造函数是如何做的


function My_constructor() {  this.name = '张三'  this.age = 18}


然后再通过关键字new来调用这个构造函数,创建一个新的对象


let new_obj = new My_constructor()


这里简单的给大家说一下,通过new调用构造函数,其实就是先创建一个新的空对象,然后将构造函数内的属性或方法都赋值给这个空对象,再将this指向这个新创建的对象。


所以从这个过程中我们可以看出,需要函数内部有一个this,但是箭头函数没有自己的this啊,所以箭头函数不能作为构造函数。


04

不宜使用箭头函数的场景


在第三部分介绍了箭头函数的注意点以后,我们可以看到,有些时候使用箭头函数是不合适的。


(1)定义类的方法


let obj = {  name: 'Lpyexplore',  get_name: () => {    console.log(this.name)  }}
obj.get_name()
//返回  undefined


我们可以看到在定义类的方法时,我们使用了箭头函数,准备通过this.name获取obj中的name,最后返回了undefined


我们来看一下为何返回undefined,首先obj.get_name是一个箭头函数,内部没有自己的this,所以会向上找,找到了obj,但对象不构成单独的作用域,所以最后就与全局绑定在一起了,但在全局没有定义一个名为name的变量,所以最后this.name返回的就是undefined


(2)绑定动态this


例如我们给一个按钮button绑定一个点击事件,要求点击按钮以后,获取到被点击的这个按钮,进行一些相关操作。这里我们就通过this来获取被点击的按钮对象


<!DOCTYPE html><html><head>    <meta charset="UTF-8">    <title></title></head>
<body>
<button class="btn">按钮</button>
<script>    let btn = document.querySelector('.btn')    btn.onclick = function () {      console.log(this)    }</script>
</body></html>

这里我们给按钮绑定点击事件,使用的是普通函数。我们来看一下,当我们点击了按钮,会打印什么


38ea468056faf132f74c41958753221c.png


我们可以看到,如愿以偿地获取到了被点击的按钮。那么如果使用箭头函数作为点击事件的处理函数呢?


//省略重复代码
btn.onclick = () => {  console.log(this)}


这时我们再来点击一下按钮,看看会打印什么


f368f1fd29cc85eaad5c2c40a039a10c.png

我们可以看到,打印的是全局的window对象,因为箭头函数内没有自己的this,所以往上找,就直接找到了全局的window对象了。所以像这种情况,是不建议使用箭头函数的。


(3)代码复杂


我们都知道,箭头函数看起来很简洁,写起来也非常的方便,但我们有没有发现,使用了箭头函数以后,代码的可读性就变差了。例如这样一个例子


    let fn = data => data


    你第一眼看到这句代码的时候,你能瞬间读懂这句代码的意思吗?我想你肯定会多思考几秒,那如果换成普通函数呢?是不是就能一眼读懂代码的意思了


      let fn = function (data) {  return data}


      说实话,普通函数虽然写法比箭头函数麻烦一点,但是可读性还是很强的。设想一下,如果有一大堆的代码,涉及到很多很多的函数,甚至有很多嵌套函数,如果我们都使用箭头函数,那么这代码阅读起来是不是就非常的困难呢。


      总而言之,如果遇到频繁地使用函数,并且代码特别复杂,我们可以在功能代码简洁代码可读性这三者之间进行权衡,并有选择性地使用箭头函数。


      05

      结束语


      写完这一篇关于箭头函数的文章,我对箭头函数又有了更深刻的印象。同时也告诫自己,不要平时为了方便或者为了装高手,无脑地使用箭头函数,有时可能用法不当造成了一些bug,然后花费大量的时间去解决。

      相关文章
      |
      6月前
      |
      C++ 容器
      【C++11特性篇】一文带小白轻松理解【万能引用(引用折叠)】&【完美转发】
      【C++11特性篇】一文带小白轻松理解【万能引用(引用折叠)】&【完美转发】
      |
      程序员
      相见恨晚的Matlab编程小技巧(2)-代码怎么做到逻辑清晰?——巧用注释符“%“
              本文将以教程的形式详细介绍Matlab中两个常用符号“%”和“%%”的作用。初学者可以通过此文掌握这两个符号的用法,为Matlab编程打下坚实的基础。
      EasyX基础内容(和易错的地方)(二)
      EasyX基础内容(和易错的地方)
      256 0
      EasyX基础内容(和易错的地方)(一)
      注意代码里面的文字,是易错点
      160 0
      |
      存储 Java 编译器
      【重学C/C++系列(四)】:函数体hack过程详解
      首先来说下**什么是hack**? hack字面意思“**非法入侵**”,那么在C/C++中其实就是**使用反汇编查看C/C++代码对应的汇编代码**
      【重学C/C++系列(四)】:函数体hack过程详解
      |
      Unix Apache C++
      给代码写注释时有哪些讲究?
      给代码写注释时有哪些讲究?
      157 0
      给代码写注释时有哪些讲究?
      |
      存储 JavaScript 前端开发
      手撕前端面试题【javascript~文件扩展名、分隔符、单向绑定、判断版本、深浅拷贝、内存泄露等】
      手撕前端面试题【javascript~文件扩展名、分隔符、单向绑定、判断版本、深浅拷贝、内存泄露等】
      246 0
      手撕前端面试题【javascript~文件扩展名、分隔符、单向绑定、判断版本、深浅拷贝、内存泄露等】
      |
      Web App开发 XML JSON
      程序人生 - 开发程序不写代码,而是靠拼图?
      程序人生 - 开发程序不写代码,而是靠拼图?
      220 0
      程序人生 - 开发程序不写代码,而是靠拼图?
      |
      机器学习/深度学习 Python
      小习惯随手记:Python编程时将常引用的基础工具包放在一个头文件中
      小习惯随手记:Python编程时将常引用的基础工具包放在一个头文件中
      小习惯随手记:Python编程时将常引用的基础工具包放在一个头文件中
      |
      Web App开发 JavaScript 前端开发
      B乎问题:通俗的解释下Vite能用来干嘛?是怎么回事?
      最近在B乎看到了这么一个问题,能不能通俗地讲 Vite 到底是用来干嘛的,一开始觉得这个问题没什么意思,因为 Vite 这个话题有太多的人讲了。
      B乎问题:通俗的解释下Vite能用来干嘛?是怎么回事?