ES6

ECMAScript6.0是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

Posted by page on December 27, 2020

ES6

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

阮一峰ES6教程:https://es6.ruanyifeng.com/

变量

变量声明

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. 类声明不会预解析提升,原因是类的继承必须保证子类在父类之后定义

super关键字

类初始化(constructor)内,调用父类构造函数以实现继承

类中调用,访问类的属性/静态方法

实例方法中调用,访问实例原型的属性/方法

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 i 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 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']);
sets.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

Generator函数

遍历器生成函数,返回遍历器对象;ES6 提供的一种异步编程解决方案;语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态。

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
// 该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象
var hw = helloWorldGenerator(); 
hw.next()
// { value: 'hello', done: false } // done即完成状态
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }

next方法

function* f() {
  for(var i = 0; true; i++) {
    var reset = yield i; // i 将被作为next()返回值,next参数将会作为yield返回值
    if(reset) { i = -1; }
  }
}

var g = f();

g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false }

for…of循环,无需next

function* fibonacci() {
  let [prev, curr] = [0, 1];
  for (;;) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

for (let n of fibonacci()) {
  if (n > 1000) break; // 执行次数由外部决定
  console.log(n);
}
function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);

  for (let propKey of propKeys) {  // 执行次数由内部决定
    yield [propKey, obj[propKey]];
  }
}

let jane = { first: 'Jane', last: 'Doe' };

for (let [key, value] of objectEntries(jane)) {
  console.log(`${key}: ${value}`);
}

// 另一个写法
jane[Symbol.iterator] = objectEntries; // Generator遍历器生成函数生成对象性遍历器
for (let [key, value] of jane) {
  console.log(`${key}: ${value}`);
}

Generator.prototype.throw

var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log('内部捕获', e);
  }
};

var i = g();
i.next();

try {
  i.throw('a'); // 向g的构造器函数内抛出错误,传递值为a
  i.throw('b'); // 继续抛出错误,此时g内部执行完成,向外抛出
} catch (e) {
  console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b

Generator.prototype.return

返回给定的值,并提前终结遍历器函数

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }

Generator应用

状态机:避免声明外部变量去记忆状态就能显示多状态切换

var clock = function* () {
  while (true) {
    console.log('Tick!');
    yield;
    console.log('Tock!');
    yield;
  }
};

回调函数:

// 请求处理逻辑
function* main() {
  var result = yield request("http://some.url");
  var resp = JSON.parse(result);
    console.log(resp.value);
}
// 请求方法
function request(url) {
  makeAjaxCall(url, function(response){
    it.next(response);
  });
}

var it = main();
it.next();
let steps = [step1Func, step2Func, step3Func];

function* iterateSteps(steps){
  for (var i=0; i< steps.length; i++){
    var step = steps[i];
    yield step();
  }
}

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')]);

Decorator装饰器

装饰器(Decorator)用来增强 JavaScript 类(class)功能的函数,支持装饰4种类型值:

  • 类(class)

  • 类的属性

  • 类的方法

  • 属性存储器(accessor)

声明装饰器

type Decorator = (value: Input, context: {
  kind: string;  // 装饰类型:class method getter setter ...
  name: string | symbol;  // 装饰值名称
  access: {  // 访问值的存取器
    get?(): unknown;
    set?(value: unknown): void;
  };
  private?: boolean;  // 是否为私有
  static?: boolean;  // 是否为静态
  addInitializer?(initializer: () => void): void;  // 初始化值时执行一些处理
}) => Output | void;

装饰器参数

value:所装饰的值,可能是 undefined (装饰一个未定义值属性时)

context:上下文对象

类装饰器

function testable(isTestable) {
  return function(target) {
    target.isTestable = isTestable;
  }
}

@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true

@testable(false)
class MyClass {}
MyClass.isTestable // false

方法装饰器

function logged(value, { kind, name }) {
  if (kind === "method") {
    return function (...args) {
      console.log(`starting ${name} with arguments ${args.join(", ")}`);
      const ret = value.call(this, ...args);
      console.log(`ending ${name}`);
      return ret;
    };
  }
}

class C {
  @logged
  m(arg) {}
}

new C().m(1);

存储器装饰器

同方法装饰器,本质对getter/setter方法装饰

属性的装饰

属性装饰器的第一个参数是undefined,即不输入值。用户可以选择让装饰器返回一个初始化函数,当该属性被赋值时,这个初始化函数会自动运行,它会收到属性的初始值,然后返回一个新的初始值

function logged(value, { kind, name }) {
  if (kind === "field") {
    return function (initialValue) {
      console.log(`initializing ${name} with value ${initialValue}`);
      return initialValue;
    };
  }

  // ...
}

class C {
  @logged x = 1;
}

new C();

core-decoraors.js

一个提供常见装饰器的第三方模块,如 readonly