zoukankan      html  css  js  c++  java
  • 前端项目中常用es6知识总结 -- 箭头函数及this指向、尾调用优化

    项目开发中一些常用的es6知识,主要是为以后分享小程序开发、node+koa项目开发以及vueSSR(vue服务端渲染)做个前置铺垫。

    项目开发常用es6介绍

    • 1、块级作用域 let const 

    • 2、箭头函数及this指向

    • 3、promise、

    • 4、async await语法
    • 4、模块化 module export和import 

    • 5、解构赋值、字符串模板 

    • ……

    箭头函数

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

    复制代码
    //es5的函数
    var fn = function(num) {
        return num;
    }
    //用箭头函数就可以这样写
    var fn = num => num
    //没有参数可以这么写
    var fn = () => 1
    //参数多的时候可以这样写
    var fn = (num, num1, num2) => num
    复制代码

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

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

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

    复制代码
    var func = (a, b) => {
        name: a,
        age: b
    } //报错
    
    var func = (a, b) => {
        {
            name: a,
            age: b
        }
    } //不报错
    复制代码

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

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

    this指向

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

    复制代码
    var x = 11;
    var obj = {
        x: 22,
        methods: {
            x: 33,
            say: function() {
                console.log(this.x)
            },
            say2: () => {
                console.log(this.x)
            }
        }
    }
    obj.methods.say();//33 普通函数this表示调用次函数时的对象即methods对象
    obj.methods.say2();//11 say2中this所在的外层代码块(向外数一个花括号)是methods对象 但并不是说this就是该对象,而是指methods对象中的this 此时this是window
    复制代码

    再看一个例子:

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

    尾调用优化

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

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

    复制代码
    function f(x) {
        let y = g(x);
        return y
    }//不是尾调用,因为最后一步返回了y,不是调用函数
    
    function f(x) {
        return g(x) + 1
    }//不是尾调用,因为最后一步是加一,不是调用函数
    
    function f(x) {
        g(x)
    }//不是尾调用,因为函数的最后一步是一个默认的return undefined;
    
    function f(x) {
        return g(x)
    }//是尾调用
    复制代码

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

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

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

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

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

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

    复制代码
    //递归 计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 
    function factorial(n) {
        if (n === 1) return 1;
        return n * factorial(n - 1)
    }
    factorial(5) //120
    
    //改成尾递归,只保留一个调用记录,复杂度 O(1) 
    function factorial(n, total) {
        if (n === 1) return total;
        return factorial(n - 1, n * total)
    }
    factorial(5, 1) //120
    复制代码
  • 相关阅读:
    曾经写的一些文章,与技术无关,整理出来怀旧,:)
    在VS.NET2003中使用XHTML的插件HTML TIDY 及 MindManger
    把机器退出了域,造成无法启动 MSSQLSERVER ,晕
    hello php!
    今天又看了一下存储过程
    [转]PAGEII携手极速网爱情电影经典对白
    关键词:2005年,世乒赛.上海,乒乓的胜地
    一本SharePoint方面的书,Special Edition Using Microsoft® SharePoint Portal Server
    keo计划
    关于xp_cmdshell 。。注意安全!
  • 原文地址:https://www.cnblogs.com/lguow/p/10180026.html
Copyright © 2011-2022 走看看