• es6小记


    1.暂时性死区(temporal dead zone,TDZ

    es6明确规定,若在区块中有let和const命令,则这个区块对这些命令声明的变量从一开始就形成了封闭的区域,只要在声明前使用这些变量就会报错。

    2.箭头函数注意事项

    a.箭头函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象;

    b.箭头函数不能做构造函数;

    c.箭头函数内不存在arguments对象,如果要用可以用rest参数代替;也没有指向外层函数的对应变量super,new.target

    d.不可以使用yield命令,因此箭头函数不能用作Generator函数;

    普通函数中的this指向时可变的,但箭头函数中它是固定的,这种特性有利于封装回调函数,this指向的固定化并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。也因为它没有this,所以不能用作构造函数。

    由于箭头函数没有自己的this,当然就不能用cal()、apply()、bind()这些方法去改变this指向

    3.尾调用优化

    a.尾调用(Tail Call)是函数式编程的一个重要概念,指函数的最后一步是调用别的函数,尾调用不一定在函数的尾部,只要是函数但最后一步操作

    1 function f(x){
    2   if(x>0){
    3     return m(x)
    4   }
    5   return n(x)
    6 }

    以上函数中,m(x),n(x)都是尾调用

    b.尾调用优化,尾调用的特殊调用位置,使其和别的函数调用不同

    函数调用会在内存形成一个“调用帧(call frame)”,保存调用位置和内部变量等信息;若函数A的内部调用了函数B,在A的调用帧上方还会形成一个B的调用帧,等B运行结束,将结果返回到A,B的调用帧才会消失;若B内还调用函数C,

    那就还有一个C的调用帧,以此类推,所有调用帧就形成了“调用栈(call stack)”;

    尾调用由于是函数的最后一步操作,不需要保留外层函数的调用帧,也因为调用位置,内部变量等信息也不会再用到,直接用内层函数的调用帧取代外层函数的即可;

     1 function f(){
     2   let m =1;
     3   let n = 2;
     4   return g(m+n)
     5 }
     6 f();
     7 //等同于
     8 function(){
     9   return g(3);
    10 }
    11 //等同于
    12 g(3);

    上面函数,若g不是尾调用,函数f需要保存内部变量m和n的值,g的调用位置等信息,但因为调用g之后,函数f就结束了,所以执行最后一步,完全可以删除f(x)的调用帧,只保留g(3)的调用帧。

    以上行为叫“尾调用优化(Tail Call Optimization)”,即只保留内层函数的调用帧。若所有的函数都是尾调用,则可以做到每次执行时调用帧只有一项,这将大大节省内存,此为“尾调用优化”的意义;

    只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧;

    4.尾递归

    a.函数尾调用自身称为尾递归,尾递归优化对递归操作意义重大

    递归因为需要同时保存成百上千个调用帧,很耗费内存,且容易发生“栈溢出(stack overflow)”;

    尾递归因为只有一个调用帧,所以永远不会发生“栈溢出”错误;

    以计算Fibonacci数列为例:

    非尾递归的Fibonacci数列实现

     1 //非尾递归的Fibonacci数列实现
     2 function Fibonacci(n){
     3    if(n<=1){return 1};
     4    return Fibonacci(n-1)+Fibonacci(n-2);
     5 }
     6 //尾递归优化的Fibonacci数列实现
     7 function Fibonacci(n, ac1=1, ac2=1){
     8    if(n<=1){return ac2};
     9    return Fibonacci(n-1, ac2, ac1+ac2)
    10 }

    b.递归函数如何改写称尾递归

      修改递归函数,确保最后一步帧调用自身;

    方法就是:把所有用到的内部变量改写称函数的参数;但是传多个参数不太直观,有两种方法,

    方法1,在尾递归函数之外再提供一个正常形式的函数;或者使用柯里化(currying),将多参数函数转换成单参数函数的形式;

     1 function currying(fn, n,l){
     2   return function(m){
     3      return fn.call(this, m, n,l);
     4   }
     5 }
     6 
     7 
     8 //调用
     9 const fibonacci = currying(Fibonacci, 1, 1);
    10 
    11 fibonacci(5);

    方法2:采用ES6的函数默认值

    function Fibonacci(n, ac1=1, ac2=1){
       if(n<=1){return ac2};
       return Fibonacci(n-1, ac2, ac1+ac2)
    }
    
    Fibonacci(5)

    以上因为有默认值所有不用提供此参数值;

     注:尾调用优化只在严格模式下生效,因为严格模式禁用func.arguments,func.caller两个变量,因为尾调用优化发生时,函数的调用栈会改写,这两个变量会失真(两个变量是用来跟踪正常模式下函数的调用栈的);

    5.扩展运算符...

    合并数组/ 与解构赋值结合,生成数组 / 识别32位Unicode字符,正确返回字符串长度[...str].length;

    可以将任何Iterator接口的对象转换成真正的数组:扩展运算符内部调用的是数据解构的Iterator接口,只要具有Iterator接口的对象,都可使用扩展运算符,Map结构,Set结构,Generator函数;

    1 let map = new Map([
    2    [1, 'one'],
    3    [2, 'two'],
    4    [3, 'three'],
    5 ]);
    6 let arr = [...map.keys()];//[1,2,3]

    6.Array.from(obj, func, 参数)

    用于将两类对象转为真正的数组,a:类似数组的对象(array-like object),即必须有length属性的对象;b:可遍历(iterable)对象

     1 let arraylike = {
     2   '0': 'a',
     3   '1': 'b',
     4   '2': 'c',
     5   length:3
     6 };
     7 //ES5写法
     8 var arr1 = [].slice.call(arraylike);//['a', 'b', 'c']
     9 //ES6写法
    10 let arr2 = Array.from(arraylike)

    第二个参数作用类似map方法,用来对每个元素进行处理,将处理后的值放入返回的数组

    1 Array.from(arraylike, x=> x*x);
    2 //等同于
    3 Array.from(arraylike).map(x=> x*x)

    若map函数中用到了this,可传入Array.from第三个参数,用来绑定this;

    Array.from可将字符串转为数组,返回字符串的长度,因为能正确处理Unicode字符

    1 function countSymbols(string){
    2    return Array.from(string).length;
    3 }
  • 相关阅读:
    python——实现三级菜单选择的功能(原创)
    Python之路购物车
    Python基础介绍
    python登陆接口编写
    Oculus Store游戏下载默认路径修改方法
    【转载】关于api-ms-win-crt-runtimel1-1-0.dll缺失的解决方案
    安装Appium
    ASP.NET之MVC 微信公众号授权给第三方平台的技术实现流程一(获取第三方平台access_token)
    C# 利用反射更改父类公开对象
    mysql 根据某个值叠加查询
  • 原文地址:https://www.cnblogs.com/Janejxt/p/12920043.html
Copyright © 2020-2023  润新知