前端开发es6知识2 箭头函数及this指向、尾调用优化

二叶草 2020年2月10日14:40:58IT专区评论阅读模式

项目开发常用es6介绍

  • 1、块级作用域 let const
  • 2、箭头函数及this指向
  • 3、promise、async await语法
  • 4、模块化 module export和import
  • 5、解构赋值
  • ……

箭头函数

顾名思义 用箭头 “ => ” 定义函数

  1. //es5的函数
  2. var fn = function(num){
  3.   return num;
  4. }
  5. //用箭头函数就可以这样写
  6. var fn = num => num
  7. //没有参数可以这么写
  8. var fn = () => 1
  9. //参数多的时候可以这样写
  10. var fn = (num,num1,num2) => num

如果箭头函数的代码块部分有多条语句,就要使用大括号将它们括起来,并且使用return返回。

  1. var func = (a,b)=>{
  2.   ...  //此处省略多行
  3.   return b;
  4. }

由于花括号{ }被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上花括号,否则会报错。

  1. var func = (a,b)=>{name:a,age:b}  //报错
  2. var func = (a,b)=>{{name:a,age:b}}  //不报错

所以由上述示例可以看出箭头函数的一个作用是简化代码。

箭头函数还有一个更主要的作用:解决this的指向问题

this指向

先说一下普通函数中的this,普通函数中的this表示调用此函数时的对象。而箭头函数是没有自己的this的,箭头函数里面的this会继承自外部的this。或者用代码块的概念解释会更加直观:箭头函数中的this就是外层代码块的this。举一个例子:

  1. var x = 11;

var obj = {

  1.     x: 22,
  2.     methods: {
  3.        x: 33,
  4.        say: function () { console.log(this.x) },
  5.        say2: () => { console.log(this.x) }
  6.       }
  7. }
  8. obj.methods.say();//33 普通函数this表示调用次函数时的对象即methods对象
  9. obj.methods.say2();//11 将一个{}看成一个代码块,
  10. say2中this所在的外层代码块(向外数一个花括号)是methods对象
  11. 但并不是说this就是该对象,而是指methods对象中的this
  12. 此时this是window

再看一个例子:

  1. var btn = document.getElementById('btn');
  2.  btn.onclick = function () {
  3.    setTimeout(function () {
  4.       console.log(this)  //普通函数中的this指的是window
  5.    }, 100)
  6.    setTimeout(() => {
  7.       console.log(this)  //箭头函数中的this在定义的时候就已经绑定了,
  8. 即继承自this当前所在代码块的外层代码块中的this
  9. 而外层代码块是一个普通函数
  10. this自然就指向调用该函数时的对象
  11. (id为btn的button按钮)
  12.    }, 100)
  13.  }

尾调用优化

首先来说一下什么是尾调用:尾调用指某个函数的最后一步是调用另一个函数。

请小心这里有一个深坑“最后一步”,举几个例子:

  1. function f(x){
  2.  let y = g(x);
  3.  return y;
  4. }
  5. //不是尾调用,因为最后一步返回了y,不是调用函数
  6. function f(x){
  7.  return g(x) + 1;
  8. }
  9. //不是尾调用,因为最后一步是加一,不是调用函数
  10. function f(x){
  11.  g(x);
  12. }
  13. //不是尾调用,因为函数的最后一步是一个默认的return undefined;
  14. function f(x){
  15.  return g(x);
  16. }
  17. //是尾调用

很多同学会疑问,为什么要做尾调用优化呢?这是一个事关内存优化的功能。

函数调用会在内存形成一个“调用记录”,又称“调用帧”。每形成一个调用帧就会占用一定的内存,假如有一个函数A里面调用了函数B,函数B里面又调用了函数C,以此类推。

在不是尾调用的情况下,因为js是单线程同步的,所以只有当函数B执行完毕才会执行函数A的return语句(函数没写return会有一个默认的return undefiend;),此时函数B的调用帧才会消失。但事实上,只有当函数B执行到return语句时才会执行完毕,而在函数B执行return之前会先调用函数C,所以当这种函数调用越来越多的时候,形成的调用帧也会越来越多占用的内存就会越来越大。俗话说精满自溢,内存也会有溢出的时候是吧,这就不行了!所以要用尾调用!

尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。

比如在执行递归的时候就可以利用尾调用达到优化内存的目的。函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。

  1. //递归 计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n)
  2. function factorial(n) {
  3.  if (n === 1) return 1;
  4.  return n * factorial(n - 1);
  5. }
  6. factorial(5) // 120
  7. //改成尾递归,只保留一个调用记录,复杂度 O(1)
  8. function factorial(n, total) {
  9.  if (n === 1) return total;
  10.  return factorial(n - 1, n * total);
  11. }
  12. factorial(5, 1) // 120

本文来源于:前端开发es6知识2 箭头函数及this指向、尾调用优化-变化吧门户
特别声明:以上文章内容仅代表作者本人观点,不代表变化吧门户观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。

  • 赞助本站
  • 微信扫一扫
  • weinxin
  • 加入Q群
  • QQ扫一扫
  • weinxin
二叶草

发表评论