ES6新特性

100 阅读8分钟

ES6

ES6(ECMAScript2015)即ECMAScript的2015年颁布的标准,新标准内容主要是js的语法糖,方便开发者更好的编写js,使开发更加工程性

变量

变量声明

ES5使用var的声明变量,具有3个特点

  1. 可以重复声明
  2. 没有块级作用域
  3. 不能限制修改

因此,ES6为让变量更可控制,完善了声明变量

let

  • 声明的变量名不允许重复声明(同一作用域下)
  • let声明变量支持块级作用域

const

  • 声明常量,不可被修改
  • 常量声明时必须初始化
  • 具备let所有特性

注:

块级作用域:即语法块(花括号内),每次执行括号内,变量都在独立的作用域中

预解析:let,const声明的变量不会被预解析

解构赋值

数组的解构赋值

let [a,b,c] = [1,2,3]; //1,2,3  

一个变量数组 = 一个值数组,以逗号分隔后相互对应项赋值;支持设置默认值;

对象的解构赋值

let {a,b,c} = {a:1,b:2,c:3}; //1,2,3

变量对象 = 变量值对象,不存在声明顺序问题;支持变量新别名对值接收

允许使用默认值

let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
var {x, y = 5} = {x: 1};

其他用法

  let { cos,sin,random } = Math;    //func,func,func;将对象值赋予环境变量 
  let [a,b,c,d,e,f] = "hello";    //h,e,l,l,o,undefined解构字符串
  import { data, request } from 'request' //解构引入模块的成员

函数

参数默认值

function getNum(outNum = 0){//支持对函数形参赋予默认值
  return outNum;
};

箭头函数

写法简洁

let getSum = (a,b) => console.log(a + b);
// 一个参数可省去括号,一行代码可省去花括号

this指向

var obj = {show: () => console.log(this);}
doucment.onclick = obj.show;

箭头函数特点:

  • this指向:是定义时所在的对象,而不是使用时所在的对象。
  • 由于其this特殊性,不允许使用 new 操作符使其提升为构造函数
  • 没有默认的 arguments 对象

字符串

方法

includes(str,index)

判断字符串是否包含参数指定字符串,index为起始检索位

startsWith(str)

判断字符串是否以参数指定字符串开头

endsWith(str)

判断字符串是否以参数指定字符串结束

模板字符串

ES5

var data = {name: 'page',age: '20'};
var html = '<p>姓名:'+ data.name +'</p><p>年龄'+ data.age +'</p>';

ES6

let html = `
    <p>姓名:${data.name}</p>
    <p>年龄:${data.age}</p>`;

拓展运算符

使用 ...

rest参数(剩余参数转成数组)

function test(multiNum,...numbers){//独立参转为数组
  return multiNum * numbers.reduce(function(prev,next,index,arr){
      return prev * next
  })
};

延伸

let arr = [2,2,3];
console.log(test(...arr));//将数组拓展,把数组项转为多个独立参数
let arr2 = [4,4,5];
console.log([...arr,...arr2]); //将两个数组拆分后并至一个数组
// 箭头函数内不支持访问arguments参数对象,可通过拓展运算符实现
let getOPtions = (...arguments) => console.log(arguments);

对象

let data = {person: {name: 'page',age: 20, gender: '男'...}};
let res = {...data};            // 快速浅拷贝
let info = {...data.person};    // 拷贝部分

对象

增强写法

const app = {
  height,
  width,
  render(options){ ... }
}

属性名表达式:对象声明的属性是环境下变量值

es5

var str = 'collected';
var type = {};
type[str] = true;

es6

var str = 'collected';
var type = {[str]: true};

Class类

ES5:创建类使用构造函数,给构造函数prototype添加方法

ES6:class关键字声明类,constructor声明构造函数,内部声明原型方法

class dropdown{
  static defaults = { options : true }  // 静态属性/方法
  loaded = "loaded"  // 声明实例默认属性(公开字段声明)
  constructor ($ele,options){  // 构造函数
    this.$ele = $ele;
    this.options = options;
  }
  // 原型方法
  show(){
    this.$ele.showHide("show");
  }
  hide(){
    this.$ele.showHide("hide");
  }
  get value(){
    return "value"
  }
  set value(val){
    this._value = val;
  }
  // 私有属性/方法
  #privateAttr = "value";
  #getPrivateAttr(){
    return this.#privateAttr;
  }
};

class继承

class Dog extends Animals {
  constructor(name,type){
    super(name);  //调用父类构造函数
  }
  // 原型方法自动继承父类原型
};

class类的特点:

  1. 内部定义的所有方法不可枚举,如constructor,getName,getColor
  2. 类必须使用new调用,不能如es5中构造函数可单独调用
  3. 类声明不会预解析提升,原因是类的继承必须保证子类在父类之后定义。

Promise

Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。可以理解为承诺将异步返回结果传递给关联处理程序,实现编写类似同步的代码进行异步操作;

状态

  1. pending 待定的初始状态

  2. fullfilled 操作成功

  3. rejected 操作失败

resolve与reject

const preloadImage = function (path) {
  const image = new Image();
  image.onload  = resolve;    
  return new Promise(function (resolve, reject) {
    image.onerror = reject;
    image.src = path;
  });
};
preloadImage('/images/eg.png').then(
    () => console.log('success'),   // 作为参数resolve回调函数
    () => console.log('failed')     // 作为参数reject回调函数
)

Primise.all

注意

  1. Promise内部会在创建时立马执行,resolve/reject并不会阻塞代码执行
  2. resolve/reject回调仅会在循环队列,即不会马上执行

手写Promise

 异步操作回调 => Promise实例记录 => 执行对应then回调
 class Ipromise {
   constructor(asycback){
     this.status = 'pending'; // 初始状态
     this.value = undefined;   // fullfilled结果
     this.result = undefined;  // rejected结果
     this.fullfilledCallbacks = [];  // fullfilled状态下回调集合
     this.rejectedCallbacks = [];    // rejected状态下回调集合
     this.resolve = this.resolve.bind(this);
     this.reject = this.reject.bind(this);
     try{
       asycback(this.resolve,this.reject); // 代理执行,完成后更新状态
     }catch(e){this.reject(e)}
   }

   reject(result){
     if(this.status === 'pending'){
       this.status = 'rejected';
       this.result = result;
       this.rejectedCallbacks.forEach(function(callback){
         callback(result);
       })
     }
   }
   resolve(store){
     if(this.status === 'pending'){
       this.status = 'fullfilled';
       this.value = store;
       this.fullfilledCallbacks.forEach(function(callback){
         callback(store);
       })
     }
   }
   then(fullfilled,rejected){
     switch(this.status){
       case 'pending' :
         this.fullfilledCallbacks.push(fullfilled);
         this.rejectedCallbacks.push(rejected);
         break;
       case 'fullfilled': fullfilled(this.store);
         break;
       case 'rejected': rejected(this.result);
         break;
     }
   }
 }

  new Ipromise(function(resolve,reject){
    setTimeout(function(){
      resolve(2)
    },2000);
  }).then(
    function(data){console.log(data)},
    function(error){console.log(error)},
  )

数组

Array.from(list,mapFn)

  • 将类数组(Nodelist等)与可遍历对象转为Array类型
  • mapFn为转数组前对数组项的处理

Array.of(item...)

  • 将一组值转化为数组
  • es5我们常用[].slice.call(arguments)

Array.find/findIndex(func,func的this指向对象)

  • 返回满足查找条件的第一个索引项/索引
  • 未找到返回undefined/-1

for快速遍历

循环索引值

  for(let index in this.list){
    console.log(i + ': ' + this.list[i]);
  };

循环数组项

  for(let item of this.list){
      console.log('数组项: ' + item)
  };

兼容ES6

es6大部分新特性需要IE10+,兼容低版本浏览器时可使用babel编译为es5代码

Babel

初始化npm

npm i init

安装babel编译器,bebel cli命令工具,babel编译相关参数

npm i @babel/core @babel/cli @babel/preset-env

新建.babelrcl配置文件

{
 "presets": [
   "@babel/env",
   "@babel/preset-react"
 ],
 "plugins": []
}

执行编译输出命令

babel src -d dist   // 将src目录的js编译至dist目录下

polyfill

问题

Babel默认只转换新的JavaScript语法(语法糖),并不会转换API(如:Symbol、Promise),以及全局对象上的方法,如Object.assign

解决:自动补丁

安装

npm install --save @babel/polyfill

项目根目录创建babel.config.json

{
"presets": [
 [
   "@babel/env",
   {
     "targets": {
       "edge": "17",
       "firefox": "60",
       "chrome": "67",
       "safari": "11.1",
     },
     "useBuiltIns": "usage",
     "corejs": "3.6.5",
   }
 ]
]
}

Symbol类型

symbol是一种基本数据类型。Symbol()函数会返回symbol类型的值,一个symbol值能作为对象属性的标识符。这意味着对象属性类型包括字符串属性和Symbol标识的属性两种;

创建

let id = Symbol();
let info = Symbol('info');  //参数仅是对该symbol的描述

使用

let obj = {
  [id]: 2020, //使用symbol标识唯一属性,声明需以方括号包裹
  id: '2020',  //普通的字符串属性'id'
};
访问symbol属性,必须通过symbol访问
console.log(obj[id],obj.id);    //2020,'2020'

Object.getOwnPropertySymbols(obj)

  • 返回symbol属性组成的数组,用于遍历symbol属性

Set对象

与Array相同,Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

let s = new Set();
let sets = new Set([0,{},[1,2],'a str']);
s.add(0);
sets.has(-1); //flase
sets.size(); //length
set.delete(0);

数组去重

const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]);

支持foreach遍历

Map对象

Map对象保存键值对,允许任何值(对象或者原始值) 都可以作为一个键或一个值(映射),并且能够记住键的原始插入顺序。

let myMap = new Map([[2,'two'],[3,'three']]);
myMap.set(0, "zero");
myMap.set(1, "one");
for (let [key, value] of myMap) {
  console.log(key + " = " + value);
}

async/await函数

以同步写法编写异步处理,比Promise更简洁,与Promise密切相关

返回Promsie

async function foo() {
 return 1;                // 等同于return Promise.resolve(1)
}.then(res => console.log(res));

await

async function foo() { // await终止函数内执行,后续代码被放在then回调
 await 1;         // Promise.resolve(1).then(
 await 2;         //      reuturn Promise.resolve(2)
}                   // )

Proxy代理器

Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

创建

const p = new Proxy(target, handler)    //返回一个代理器
// target为代理的对象实例,handle对象即所有代理操作
// 对代理器的操作自动handler

Module模块

相对已有的CommonJS、AMD模块化规范,新增ES6 Module(按引用传递)

export

基本使用

export var num = 0;
export const Temp = 'xx';
export function func() {};

// 等同于
export { num, Temp, func }

// 继承
export * from './module1.js';
export * from './module2.js';

export default

// func模块 
// export default唯一性, 但支持与具名导出并存
export default function func() {}

// 引用
import func from './func.js';

as关键字

const value = 'xxx';
export {
    value as outName 
    ...
}
export { default as AppMain } from './AppMain'; 
// 或
export { default } from './xxx.js';

import

基本使用

import './script.js';
import { num } from './xxx.js';

// 注意:num应该作为只读,由于按引用传值,修改是危险操作
num++; // warn dangerous

as关键字

import { vaule as A } from './xxx.js';
import * as api from './api.js';

import default

import module1, { childModule1 } from "./main.js";

运行时加载

// 与require相像
import('./xx.js');
if(true) import('./xx.js');
import(getModulePath(params));

// promise
import('./xx.js').then(({export1, export2}) => console.log('imported!'));
new Promise.all([import('./module1.js'), import('./module2.js')]);