ES6入门笔记

变量的解构赋值

  • 数组的解构赋值

    • ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
      例如:
          let[a,b,c] = [1,2,3];
      
      上面代码中,可以从数组中提取值,按照对应位置,对变量赋值。
  • 对象的解构赋值

    • 对象的解构与数组的解构有一个重要的不同,数组的元素是按次序排列的,变量的值由它的位置决定,而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
      let { foo, bar } = { foo: "aaa", bar: "bbb" };  
      foo // "aaa"
      bar // "bbb"
      
  • 解构的多种用途

    • 交换变量的值
      let x = 1;
      let y = 2;
      [x, y] = [y, x];
      
    • 从函数返回多个值
      function example() {
          return [1,2,3];
      }
      let [a,b,c] = example();
      //返回一个对象
      function exm() {
          return {
              foo:1,
              bar:2
          }
      }
      let {a,b} = exm();
      
    • 解构赋值可以方便的将一组参数和变量名对应起来
      //参数是一组有次序的值
      function fn([x, y, z]){ ··· };
      f([1,2,3]);
      //参数是一组无次序的值
      function fn([x, y, z]){ ··· };
      fn([z:3, y:2, x:1]);
      
    • 提取JOSN数据,结构赋值对提取JSON对象的数据,尤其有用
      let jsonData = {
          id: 11,
          status: "OK",
          data: [123,123]
      }
      //data:number 相当于重新给data命名
      let {id, status, data:number} = jsonData;
      
      
    • 函数参数的默认值
      jQuery.ajax = function (url, {
          async = true,
          beforeSend = function () {},
          cache = true,
          complete = function () {},
          crossDomain = false,
          global = true,
          // ... more config
          } = {}) {
      // ... do stuff
      //标记:这里并不是很理解
      };
      

字符串的扩展

  • 字符串的遍历器接口
    • ES6为字符串添加了遍历接口器,可以使字符串被 for...of 遍历
    for(let codePoint of 'foo'){
        console.log(codePoint);
    }
    // "f"
    // "o"
    // "o"
    
  • at()
    • ES5对字符串对象提供 charAt 方法,返回字符串给定位置的字符,该方法不能识别码点大于 0xFFFF的字符
    • ES6提供 string.at() 方法

函数的拓展

  • 函数的默认参数值
    • ES6允许给函数设置默认参数值,直接写在参数后面
    function log(x, y = "world") {
        console.log(x , y);
    }
    
    • 除了简洁,ES6语法还有两个好处:首先,阅读代码的人可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,

尾调用优化

  • 尾调用指某个函数的最后一步调用另一个函数

    • 函数调用会在内存中形成一个‘调用记录’,又称‘调用帧’,保存调用位置和内部变量等信息。尾调用由于是函数的最后一步操作,不需要保留外层函数的调用帧,因为调用位置、内部变量等信息不会再用到了,只要直接使用内层函数逇调用帧,取代外层函数的调用帧就可以了。
  • 尾递归

    • 递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生‘栈溢出’错误。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生‘栈溢出’
      • 尾递归 斐波那契数列
      function Fib2(n, ac1 = 0, ac2 = 1) {
          if( n <= 1) return ac2;
          return Fib2(n-1, ac2, ac1 + ac2);
          //在返回值的时候已经算好了 此时是尾调用 
      }
      
  • 递归函数的改写

    • 把所有用到的内部变量改写成函数的参数
    • 柯里化(currying) 意思是将多参数的函数转换为单参数的形式
    • 递归本质上是一种循环操作,一旦使用递归,最好使用尾递归
  • 严格模式

    • ES6的尾调用优化只在严格模式下开启,正常模式是无效的,因为正常模式下函数内部有两个变量,可以跟踪函数的调用栈
      • - func.arguments: 返回调用时函数的参数
      • - func.caller: 返回调用当前函数的那个函数
    • 尾调用优化是函数的调用栈会改写,因此上面两个变量就会失真。严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效
  • 尾递归优化的实现

    • 尾递归优化只在严格模式下生效,正常模式下可以采用'循环'换掉'递归'
    • 蹦床函数(trampoline)可以将递归执行转为循环执行
    function trampoline(f) {
        while(f && instanceof Function) {
            f = f();
        }
        return f;
    }
    

数组的扩展

扩展运算符

  • 扩展运算符(spread)是三个点(...),它好比rest参数的逆运算,将一个数组转换为逗号分隔的参数序列
    • 复制数组
      • 数组是复杂数据类型,直接复制只是复制了指向底层数据的指针,而不是克隆一个新的数组,扩展运算符提供了复制数组的简写
      const a1 = [1,2];
      const a2 = [...a1]; //写法1
      const [...a2] = a1; //写法2
      
    • 数组合并
      const a1 = ['a', 'b'];
      const a2 = ['c'];
      const a3 = ['d', 'e'];
      //ES5的合并数组
      a1.concat(a2, a3);
      //ES6的合并数组
      console.log([...a1, ...a2, ...a3]);
      
      • 这两种方法都是浅拷贝
    • 字符串
      • 扩展运算符可以将字符串转为真正的数组
      [...'hello'] //['h','e','l','l','o']
      //这种写法一个重要的好处是可以正确识别 Unicode字符
      
    • Map 和 Set 结构,Generator 函数
      • 扩展运算符内部调用的是数据结构Iterator接口,只要有Iterator接口的对象,都可以使用扩展运算符
    • Array.from()
      • Array.from 用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象(包括ES6新增的数据结构 Set 和 Map)
      let arrayLike = {
          '0': 'a',
          '1': 'b',
          '2': 'c',
          length: 3
      };
      //ES5的写法
      var arr1 = [].slice.call(arrayLike);
      //ES6
      let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
      
      • 实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,以及函数内部的 arguents 对象。 Array.from都可以将他们转换为真正的数组

Promise

回调函数的问题

  • 回调函数本身没有问题,他的问题出现在多个回调嵌套。假设执行完某个异步函数后在执行另一个函数,出现多次后会发生多重嵌套。这种情况称为'回调地狱'(callback hell)
  • Promise就是为了解决这种问题而提出的。采用Promise连续读取多个文件
    let readFile = require('fs-readfile-promise');
    
    readFile(filea)
    .then(function(data){
        console.log(data.toString());
    })
    .then(function() {
        return readFile(fileB);
    })
    .then(function(data){
        console.log(data.toString());
    })
    .catch(err => console.log(err))
    

Generator 函数的概念

  • Generator 函数是协程在 ES6 的实现,最大的特点是可以交出函数的执行权(即暂停执行)
    function* gen(x) {
        var y = yield x + 2;
        return y;
    }
    //它不同于不同函数,是可以暂停执行的,所以函数名之前要加星号,以示区别
    
    • 整个 Generator 函数就是一个封装的异步任务,或者说异步任务的容器。一步操作需要暂停的地方,都用yield语句注明

Promise 对象

  • Promise 的含义
    • Promise 就是一个容器,存储着某个未来才会结束的事件(通常是一个异步操作)的结果。
    • Promise 对象有以下两个特点
      • (1) 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态: pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个窗台。
      • (2) 一旦状态改变,就不会在变,任何时候都可以得到这个结果。
  • 基本用法
    • ES6规定,Promise 对象是一个构造函数,用来生成Promise实例
    • Promise 构造函数接收一个函数为参数,该函数的两个参数分别是 resolvereject , reslove 函数在异步操作成功时调用,并将异步操作的结果作为参数传递出去;reject函数的作用是在一部操作失败是调用,并将异步操作报出的错误作为参数传递出去
    • Promise实例生成之后可以用then方法分别指定 resolved状态和rejected状态的回调函数
    promise.then(function(value){
        //success
    }, function(error) {
        //failure
    })
    
  • Promise.prototype.then()
    • Promise 实例具有 then 方法,then方法是定义在原型Promise.prototype上的。它的作用是作为 Promise 实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
    • then 方法返回的是一个新的Promise实例(此时返回的实例不是原来的Promise 实例),因此可以采用链式写法,即then方法后面再调用另一个then方法
    getJSON('/post.json').then(function(json) {
        return json.post;
    }).then(function(post){
        //...
    });
    
    • 第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数就会等待这个新的Promise对象状态发生变化。如果变为resolved就执行下一个then的第一个参数,箭头函数的写法是:
    getJSON('post/.json').then(
        post => getJSON(params); 
    ).then(
        //第一个参数是成功后的回调函数
        comments => console.log("resolved:", comments),
        //第二个参数是失败调用的回调函数
        err => console.log("rejected", err)
        //此时函数返回的是一个对象,还可以继续 then
    ).then(
        成功调用的函数 => console.log(resolved),
        失败调用的函数(可选) => console.log(rejected)
    );
    

你可能感兴趣的:(ES6入门笔记)