重学ES6+

147 阅读8分钟

前言

我这里为啥子叫 重学,实在是因为在业务代码中还是会经常看到ES5的影子,也不是说用ES5写法不行,只是会有BUG,造成代码量增多,可读性变差而已。

偏偏,有一丢丢代码洁癖,经验呢说长不算长,说短也不短,3-5年经验的同学,着实不想面对这样的代码,重新学习并整理一下ES6新特性,巩固自己,帮助别人,快乐你我他~

ES6+的由来

ES6是ECMAJavaScript指定的第6个标准版本,标准委员会最终决定,标准在每年6月正式发布并作为当年的正式版本,接下来的时间里就在此版本的基础上进行改动,直到下一年6月草案就自然变成新一年的版本,这样一来就无需以前的版本号,只要用年份标记即可。

ECMAscript 2015是在2015年6月发布ES6的第一个版本。以此类推,ECMAscript 2016是ES6的第二个版本、 ECMAscript 2017是ES6的第三个版本。ES6 既是一个历史名词也是一个泛指,含义是5.1版本以后的JavaScript下一代标准,目前涵盖了ES2015ES2016ES2017ES2018ES2019ES2020

所以市面上提到的 ES7、ES8、ES9、ES10、ES11等,实质上都是不规范的概念,从ES1到ES6,每个标准都是花了好几年甚至十多年才制定下来,你一个ES6到ES7,ES7到ES8,才用了一年,按照这样的定义下去,那不是很快就ES20了。用正确的概念来说ES6目前涵盖了ES2015ES2016ES2017ES2018ES2019ES2020。在这里姑且把这些叫作 ES6+ 吧。

正文

1. let / const

let和const是声明变量和常量的命令,在ES5中我们可以使用var来声明,那么为什么会出现let、const,而不继续使用var,他们之间有什么区别呢?

  • 具体区分变量与常量:let声明变量,const声明常量
  • 作用域不同:let和const在代码块中执行,var在全局代码中执行
  • 不可进行重复定义:let和const不允许重复声明,var可重复声明
  • 不存在变量提升:let和const若未定义变量进行使用就会报错,var存在变量提升

2. 解构赋值

  • 对象解构

    const { name, age, height } = {
      name: 'wendy',
      age: 18,
      height: 167
    };
    
    console.log(name, age, height);   // 'wendy', 18, 167
    // 设置默认值
    const { name = 'wendy', age, height } = {
      age: 18,
      height: 167
    };
    // 改变变量名
    const { name, age, height: h } = {
      name: 'wendy',
      age: 18,
      height: 167
    };
    
  • 数组解构

    const arr = [1, 2, 3];
    
    const [a, b, c] = arr;
    console.log(a, b, c);  // 1,2,3
    const [, , d] = arr;
    console.log(d);  // 3
    const [e, ...rest] = arr;
    console.log(e, rest);  // 1, [2, 3]
    

注:

  • 只要等号两边的模式相同,左边的变量就会被赋予对应的值
  • 只要等号右边的值不是对象或者数组,就将其转化为对象
  • 解构不成功时,变量的值等于undefined
  • undefinednull无法转为对象,因此无法进行解构

3. 字符串模板

就是用 `` 将字符串包裹起来。

可以对字符串进行换行和插值,写法上更流畅、简洁。

${}中可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。

// 换行
const str = `wen
dy`;
// 插值
const age = 18;
const str = `wendy: ${age}岁`;
console.log(str); // 'wendy: 18岁'

请摒弃以下写法

const age = 18;
const str = 'wendy: ' + age + '岁';

4. 字符串扩展

  • 字符串遍历:通过for-of遍历字符串

  • 字符串模板

  • repeat():把字符串重复 n 次,并返回 新的字符串

  • Includes():是否存在指定字符串

  • startsWith():是否存在字符串头部指定字符串

  • endsWith():是否存在字符串尾部指定字符串

    const str = 'wendy';
    
    console.log(str.includes('d'));  // true
    console.log(str.startsWith('w'));  // true
    console.log(str.endsWith('n'));  // false
    

5. 数值扩展

  • Number.parseInt():返回转换值的整数部分
  • Number.parseFloat():返回转换值的浮点数部分
  • Number.isNaN():是否为NAN
  • Number.isInteger():是否为整数
  • Math.trunc():返回数值整数部分
  • Math.sign():返回数值类型(整数 1、负数 -1、零 0)

6. 对象扩展

  • 属性名表达式:字面量定义对象时使用[]定义键[key]
  • Object.assign():合并对象(浅拷贝),返回原对象
  • Object.getPrototypeOf():获取对象的原型对象
  • Object.setPrototypeOf():设置对象的原型对象
  • 遍历对象for-in遍历对象自身可继承可枚举属性
  • Object.keys:返回对象属性键组成的数组
  • Object.is():比较俩值是否相等
  • Object.hasOwnProperty():判断对象是否含有指定属性,返回布尔值

7. 数组扩展

  • 扩展运算符(...):转换数组为用逗号分隔的参数序列([...arr])
  • Array.from():转换数组,返回新数组
  • Array.of():转换一组值为真正数组,返回新数组
  • find():返回第一个符合条件的成员
  • findIndex():返回第一个符合条件的成员索引值
  • fill():根据指定值填充整个数组,返回原数组

8. 函数扩展

  • 给函数形参设置默认值

    // 带默认参数的形参一般放在后面,减少传参导致的错误几率
    function func (name, age = 18) {
    	// TODO: do something  
    };
    
  • 使用...rest形式设置剩余形参,支持无限参数(替换arguments对象)

    // 剩余参数,转化成数组
    const restParams = function(...args) {
        console.log(args.toString()); // 1, 2, 3, 4, 5
    }
    
    restParams(1, 2, 3, 4, 5);
    

9. 箭头函数

  • 简化了函数的写法
    • 无参数:() => {}
    • 单个参数:x => {}
    • 多个参数:(x, y) => {}
    • 解构参数:({x, y}) => {}
  • 没有this机制,this指向固定化,有利于封装回调函数

10. Promise

  • 概念:包含异步操作结果的对象
  • 状态:
    • 进行中:pending
    • 已成功:resolved
    • 已失败:rejected
  • 作用:
    • 对象的状态不受外界影响
    • 解决异步编程中回调嵌套过深的问题
  • 声明:new Promise((resolve, reject) => {})
  • 方法:
    • then():resolved状态rejected状态的回调函数
    • catch():异常回调
    • Promise.all():将多个实例包装成一个新实例,返回全部实例状态变更后的结果数组
    • Promise.race():将多个实例包装成一个新实例,返回全部实例状态优先变更后的结果

11. Async / Await

  • 概念:async函数就是Generator函数的语法糖,async函数的await命令后面 可以是promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。

  • async 函数完全可以看作多个异步操作,包装成的一个Promise对象,而await命令就是内部then命令的语法糖。

  • 实例:

    // 指定时间返回数据
    function timeout(ms) {
      return new Promise((resolve) => {
        setTimeout(resolve, ms);
      });
    }
    
    async function asyncPrint(value, ms) {
      await timeout(ms);
      console.log(value);
    }
    
    asyncPrint('wendy', 6000);
    

12. Module

  • 指令:

    • export:导出模块对外接口
      • 默认导出:export default Person (导入时可指定模块任意名称,无需知晓内部真实名称)
      • 单独导出:export const fetchName = "wendy"
      • 按需导出:export { fetchName, fetchAge }(推荐使用)
      • 改名导出:export { fetchName as getName }
    • import:导入模块内部功能
      • 默认导入:import Common from "common"
      • 整体导入:import * as Common from "common"
      • 按需导入:import { fetchName, fetchAge } from "common"
      • 自执导入:import "common"
      • 复合导入:import Common, { fetchName } from "common"

    • 一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取
    • import命令大括号里的变量名必须与被导入模块对外接口的名称相同

13. Set

  • 概念:一种类似于数组的数据结构,成员值都是唯一且没有重复的值

  • 声明:const set = new Set(arr)

  • 特点:

    • 元素唯一性,不允许重复元素
    • 使用 add 添加重复元素,将会被忽略
  • 方法:

    • add():添加值,返回实例
    • delete():删除值,返回布尔值
    • has():检查值,返回布尔值
    • clear():清除所有成员
    • keys():返回以属性值为遍历器的对象
    • values():返回以属性值为遍历器的对象
    • entries():返回以属性值和属性值为遍历器的对象
    • forEach():使用回调函数遍历每个成员
  • 用途:

    • 数组去重

    • 数据存储

       const arr = [1, 2, 3, 2, 2];
       const set = new Set(arr);
       set.add(1).add(1);
       console.log(set.size);  // 3
       const newArr = Array.from(set);
       console.log(newArr);  // [ 1, 2, 3 ]
      

14. Map

  • 概念:一种类似于对象的数据结构,以 key、value形式存储数据

  • 声明:const map = new Map(obj)

  • 其他与 set 无什么差别

  • 实例:

    const map = new Map();
    map.set('name', 'wendy');
    map.forEach((val, key) => {console.log(key, val)});
    

15. Generator

  • 概念:封装多个内部状态的异步变成解决方案

  • 声明:function* func() {},一般配合 yield 关键字使用

  • 方法:next() 让函数恢复执行且使用不同的语句替换 yield 命令

  • 特性:

    • 惰性执行,调 next 才会往下执行
    • 解决异步回调过深的问题
  • 实例:

    function* crateIdGenerator() {
      let id = 1;
      while(id < 3) yield id++;
    }
    const createId = crateIdGenerator();
    const createId = createIdGenerator();
    console.log(createId.next());  // { value: 1, done: false }
    console.log(createId.next());  // { value: 2, done: false }
    console.log(createId.next());  // { value: undefined, done: true }
    

总结

巴啦啦了一大堆,啥时候用就啥时候来看吧,每个人都在说使用ES6,你倒是用啊,哈哈哈~

emmmm,开玩笑的啦,还是希望能真正了解为什么要使用ES6+吧,努力写出更流畅、更规范、更健壮的代码!