当前位置: 首页 > news >正文

箭头函数转化为普通函数_理解 JavaScript 箭头函数

引言

ES6 出现了箭头函数,我们在现代 JavaScript 的开发中经常使用箭头函数,它令我们的代码看上去更加简洁。

e0e95f4bb73aa22b82551b9b3dd9bff6.png

思考

在开始正题前,我们先思考一段代码,然后在文章的最后,我们还会回到这里看看我们的代码:

let team = {  work: 'my-team',  langs: ['JS','PHP','JAVA'],  show() {    this.langs.forEach(function(lang) {      console.log(this.work + ' ' + lang);    })  }}team.show();

先看一下结果吧,这段代码在严格模式下报错:TypeError: Cannot read property "work" of undefined ,非严格模式会输出:

undefined JS

undefined PHP

undefined JAVA

因为在严格模式下,这段代码的 this 不指向 window 对象,而是 undefined ,调用 undefined.work 显然是错误的,在非严格模式下可以正常执行,因为 this 指向了 window 对象,访问一个对象不存在的属性,值就是 undefined 。

箭头函数

有一种非常简单和简洁的语法创建函数,它通常比函数表达式更好,我们来看一看它的真面目:

let arrow = (arg1, arg2, ...argN) => expression;

这儿创建了一个箭头函数,它接收参数 arg1 ... argN ,然后使用这些参数对右侧的表达式求值,并返回结果。

再来看一下函数表达式是如何定义的:

let exp = function(arg1, arg2, ...argN) {  return expression;}

我们来看一个具体的箭头函数的例子:

let arrow = (str1, str2) => str1 + ' ' + str2;console.log(arrow('hello','world')); // hello world

正如您所见,(str1, str2) => str1 + ' ' + str2 表示一个接收 str1 ,str2 两个参数的函数。在执行时,它计算表达式 str1 + ' ' + str2 的值,然后将结果直接返回 (没有写 return 关键字)。

与函数表达式相比,箭头函数没有 function 和 return 关键字。

如果只有一个参数,可以省略参数的括号,使箭头函数表现得更 “精巧” ,就像这样:

let arrow = str1 => str1 + ' world';console.log(arrow('hello')); // hello world

如果没有参数,括号就必须得存在了,例如:

let arrow = () => 'hello world';console.log(arrow());

箭头函数可以像表达式一样使用。

我们来看一个例子,这是一个动态创建的函数:

let lan = 'js';let work = (lan === 'js') ?   () => console.log('code in js') : () => console.log('code in HTML and CSS');work(); // code in js

到目前为止,我们的箭头函数都只有一行代码,并且不用写 return 关键字就可以直接返回 '=>' 右侧的值,这看起来非常简洁。

多行代码的箭头函数

有时候,我们的函数中可能会需要一些更复杂的功能,有可能需要多行语句,这时候,我们应该在 '=>' 右侧用 '{}' 将我们的函数体扩起来,并且在需要返回值时,显式的在函数体内写上 return 关键字,如果没有 return 关键字,函数将默认返回 undefined 。

就像下面这样:

let arrow = (str1, str2) => {  let result = str1 + ' ' + str2;  return result;}console.log(arrow('hello', 'world'));

箭头函数中的 this

在函数内部有个 ThisBindingStatus 属性,它标识了函数内部 this 绑定情况的,即是否绑定好了本地的 this 值,如果这个属性值是 lexical ,代表这是个箭头函数,没有 this 值。

简单定义一下箭头函数中的 this :箭头函数是和父级上下文绑定在一起的。简单来讲,如果箭头函数内出现了 this ,则它指向包含箭头函数的 “那个容器” 所在范围的 this 值。以下几点可能需要特别注意:

如果箭头函数的容器还是箭头函数:

由于箭头函数没有 this 的概念,所以对于箭头函数的嵌套情形,内部箭头函数的 this 指向,直接看最外部箭头函数的 this 指向。即不管嵌套多少层,都共享最外层的 this 指向。外部箭头函数的指向,参考上面的定义,如下面的代码:

// 共享 say 的 this 指向// 包含 say 的 “那个容器”是 obj// “那个容器”所在的范围是全局var name = 'ooo';let o = {  name: 'hello',  say: () => {    let f = () => {      console.log(this.name);    }    f();  }}o.say(); // ooo// 共享 f 的 this 指向// 包含 f 的 “那个容器”是 say// “那个容器”所在的范围 objvar name = 'ooo';let o = {  name: 'hello',  say: function() {    let f = () => {      let innerF = () => {        console.log(this.name);       }      innerF();    }    f();  }}o.say(); // hello

如果箭头函数在一个构造函数中:

  • 箭头函数直接作为构造函数的属性或箭头函数包含在作为构造函数属性的普通函数内时,箭头函数中的 this 都指向构造函数的 this 值。
function Per(){  this.name = 'kylin';  this.nor = function () {    console.log(this.name);  }  this.say = () => {    console.log(this.name)  }  this.arrowSay = () => {    let s = () => {      console.log(this.name)    }    s();  }  this.funSay = function() {    let s = () => {      console.log(this.name)    }    s();  }}let p = new Per();p.say(); // kylinp.arrowSay(); // kylinp.funSay(); // kylinvar p2 = p.say;p2(); // kylin 箭头函数本来就没 this ,this 一直就是 Per 的var p3 = p.nor;p3(); // global this 在引用时丢失了

如果箭头函数出现在回调、setTimeout 、forEach 等功能性函数调用中:

  • 箭头函数的 this 直接指向包含这些功能性函数的 “那个容器” 的 this 值。注意箭头函数嵌套情形,如果使用它们的 “那个容器” 是箭头函数,还是没有 this 值,还要根据上面的定义去找。

function 声明的普通函数:

  • 如果是一个普通的函数声明,那么函数的 this 在非严格模式下指向 window 对象,严格模式下 this 值为 undefined 。箭头函数中的 this 则不受严格模式影响,因为它本身就没 this 。

我们再看一些示例,熟悉一下箭头函数:

// 用 var 可以让 str 绑定到 window 上// 这样看结果清晰些var str = 'world';let arrow = () => {  let str = 'hello';  console.log(this.str);}arrow();// 输出 world // 包含箭头函数的 “那个容器” 是全局// “那个容器”所在范围的 this 是 window

别急,还有一段代码:

// 这个例子箭头函数在一个普通函数中var str = 'world'; function say() {  let str = 'hello';  let f = () => {    // 输出 world     // 包含箭头函数的 “那个容器” 是 say 函数    // “那个容器” 是一个普通函数    //  普通函数 this 是 window    console.log(this.str);  }  f();}say();

或者下面这个例子:

// 这个例子箭头函数在对象的函数属性中var str = 'world';let obj = {  str: 'hello',  say() {    let hi = () => {      console.log(this.str)    }    hi();  }}// hello 包含箭头函数的 “那个容器” 是 say 函数// “那个容器” 所属范围是 obj ,obj 对象的 this 是// 它自身,所以箭头函数的 this 指向 objobj.say();

还有这个例子:

// 箭头函数不在对象的函数属性中// 直接就是对象的属性var str = 'world';let obj = {  str: 'hello',  say: () => {    console.log(this.str);  }}// 输出 world // 包含箭头函数的 “那个容器” 是 obj 对象// “那个容器”所在的范围是全局,全局的 this 是 window ,// 所以箭头函数的 this 指向 windowobj.say();

再看一个例子了:

// 箭头函数不在对象的函数属性中var name = 'world';function outer() {  let name = 'hello';  function inner() {    let say = () => {      console.log(this.name)    }    say();  }  inner();}// 输出 world // 包含箭头函数的 “那个容器” 是 inner 函数// “那个容器” 所在的范围是 outer 函数 ,// 由于 outer 是普通函数,所以 this 指向 window// 所以箭头函数的 this 指向 windowouter();

结合上面的规则,最后思考一个例子:

var name = 'world'function Person() {  this.name = 'hello'  this.say = () => {    console.log('p.say: ' + this.name);  }  this.sayFun = function () {    let f = () => {      console.log('constructor: ' + this.name);    }    return f;  }  setTimeout(() => {    this.name += ' person'  }, 1000)  this.o = {    name: 'obj',    fun: () => {      console.log('o.fun:' + this.name)    },    funArrowIn: () => {      let f = () => console.log('o.funArrowIn:' + this.name);      f();    },    funTest: function() {      let print = () => {        console.log('o.funTest: ' + this.name)      }      print();    },    funcSetTimeoutArrow: () => {      setTimeout(() => {        console.log('o.funcSetTimeoutArrow: ' + this.name);      }, 3000)    },    funcSetTimeout: function() {      setTimeout(() => {        console.log('o.funcSetTimeout: ' + this.name)      }, 1000)    }  }}let p = new Person();p.say(); // p.say: hellop.sayFun()(); // constructor: hellop.o.fun(); // o.fun:hellop.o.funTest(); // o.funTest: objp.o.funArrowIn(); // o.funArrowIn:hellop.o.funcSetTimeoutArrow(); // o.funcSetTimeoutArrow: hello personp.o.funcSetTimeout(); // o.funcSetTimeout: objsetTimeout(()=>{ console.log(p.name)}, 3000) // hello person

现在,你应该了解箭头函数的 this 了,我们回到上面正文部分的例子,今天不打算讲解普通函数中的 this ,我们直接将代码中的普通函数改成箭头函数看看结果:

let team = {  work: 'my-team',  langs: ['JS','PHP','JAVA'],  show () {    this.langs.forEach((lang) => console.log(this.work + ' ' + lang));  }}team.show();// my-team JS// my-team PHP// my-team JAVA

可以正常输出了,依据前面的定义,箭头函数定义在了一个名叫 show 的函数定义中,这个函数是 team 对象的属性,所以 this 就指向这个 “show 函数的容器” team 。或者可以说,show 函数的 this 是 team ,包含 forEach 的 “容器” 是 show 函数,所以 this 就是 show 函数的 this 。

总结

箭头函数对于只有一行代码的程序来说非常方便。它们有两种形态:

  • 右侧是表达式:'=>' 右边是表达式,计算表达式的值,不用 return 直接自动返回。
  • 右侧使用大括号:使用 (…args) => { 函数体 } 的形式,括号允许我们在函数中编写多个语句,但是我们需要一个显式的 return 来返回一些东西,如果没有显式的 return ,函数默认返回 undefined 。

箭头函数还有几方面需要注意:

  • 没有 this 和 super 关键字。
  • 没有 arguments 对象。
  • 不能使用 new 实例化,因为它没 this 。
  • 尽量在 setTimeout(func) 、arr.forEach(func) 以及你自定义的回调函数中使用箭头函数,因为箭头函数可以避免烦人的 this 指向问题。

文章中的图片来源于网络,若有侵权行为,请在后台与我联系。

相关文章:

  • python抖音涨粉代码_python制作抖音代码舞
  • python中pow_pow在python中指的是什么意思
  • 80端口被占用 nt kernel iis_IIS维护分享
  • 对多用户分时系统最重要_新建网站如何做网络推广?最有效方法是什么?
  • c++ 如何将输入的内容输出到文本文件 要建立文本文件嘛_利用FSO对象读取文本文件的信息...
  • 简单实现x的n次方pta_TF2.0实现DeepFM并部署
  • 基于python的图像处理的毕业论文_基于Python的人脸识别系统研究.docx
  • python图形编程复选按钮和单选按钮详细说明_python GUI库图形界面开发之PyQt5复选框控件QCheckBox详细使用方法与实例...
  • 用python做一张图片_用Python实现将一张图片分成9宫格的示例
  • @data注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • python做运动控制_【仿真】基于mPython掌控地月运动系统
  • 悟空问答python反爬_悟_刘德华_高音质在线试听_悟歌词|歌曲下载_酷狗音乐
  • redis 登录_redis如何做亿级用户登录日活统计?(内含资料)
  • span js增加display_小猿圈前端编写JS五子棋游戏
  • python读取的数据怎么变成数据框数据_利用python进行数据分析
  • JavaScript的使用你知道几种?(上)
  • mysql 5.6 原生Online DDL解析
  • mysql中InnoDB引擎中页的概念
  • Python 反序列化安全问题(二)
  • python3 使用 asyncio 代替线程
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 聊聊flink的TableFactory
  • 区块链共识机制优缺点对比都是什么
  • 如何胜任知名企业的商业数据分析师?
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 异步
  • 怎么将电脑中的声音录制成WAV格式
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • 浅谈sql中的in与not in,exists与not exists的区别
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • # 飞书APP集成平台-数字化落地
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (转)程序员技术练级攻略
  • .NET 4.0中的泛型协变和反变
  • .NET Micro Framework初体验
  • .net 微服务 服务保护 自动重试 Polly
  • .NET大文件上传知识整理
  • .net反混淆脱壳工具de4dot的使用
  • .NET简谈设计模式之(单件模式)
  • .NET是什么
  • .net中我喜欢的两种验证码
  • @WebService和@WebMethod注解的用法
  • [] 与 [[]], -gt 与 > 的比较
  • [3D游戏开发实践] Cocos Cyberpunk 源码解读-高中低端机性能适配策略