ES6相关知识

191 阅读7分钟

前言

ES6学习资料

规范规定的原因:减少运行时错误,规范编码习惯,执行严格的编码规范;ES6新增的特性也大部分实现了该目的。

1.新增变量声明:let&const

var的特性

  • var存在提升,函数会被提升到作用域的最顶层,变量提升只是声明提升到作用域顶层
  • var存在提升, 所以能在赋值之前获取;let、const因为暂时性死区,不能在声明前使用
  • var声明的变量会挂靠给window,作为window的属性;let、const不会
  • let、const作用基本一致,后者不能再次赋值。

let和const特性

  • 作用域都是块级作用域
  • 都有暂时性死区
  • 都没有变量提升
  • let不能重复声明变量,会报错;const声明常量且声明必须初始化
  • let和const声明的全局变量将逐步与顶层对象的属性脱钩(与window的属性无关)。
  • const只对比对象和数组的内存地址是否改变(如果需要申请常量对象和数组使用冻结对象或数组)
var a = 1; console.log(window.a); //1
let b = 2; console.log(window.b); //undefined

规范规定的原因:减少运行时错误,规范编码习惯,执行严格的编码规范;

暂时性死区:只要改let申明的区域都为let的死区,在其声明的前面使用的该变量都会报错;

(在声明完成之前使用变量都会报错,变量存在与获取时机不同,进入块级作用域变量就存在,只有声明完成后才能获取)

typeof x; // ReferenceError
let x;
typeof undeclared_variable // "undefined"

块级作用域:

var s = 'hello';
for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}
console.log(i); // 5,泄漏为全局变量

新增变量解构赋值

默认值

需要注意:

  • 赋值的默认值,只有变量===undefined的时候,默认值才会生效,变量为null、''、或者其他空值,只要严等不为undefined默认值都不会生效。这样会产生很多bug。
//源码
const {strs = [] } = obj;
const infos = JSON.parse(strs[0]); //如果obj.strs=null或者obj.strs=''等非undefined空值则代码会报错
const {commends = ''} = infos;
//在react项目中编译成源码如下:
_refs2=_context.sent //拿到需要解构的对象
_refs2$strs = _refs2.strs; //拿到需要解构的变量
strs = _refs.strs === void 0 ? [] : _refs2$strs; //只会判断变量是否为void 0,其他空值或者假值都不会被赋默认值
//源码错误优化方案
const {strs = [] } = obj;
const infos = JSON.parse((strs||[])[0]); //也会报错JSON.parse(undefined)会报错,只能解析查看https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON
const {commends = ''} = infos;
//源码优化方案
const {strs = [] } = obj;
let infos = {};
try{
    const infos = JSON.parse((strs)[0]);
}catch(error){
    console.log(error);
}
const {commends = ''} = infos;

//或者this.setState的时候直接写上默认值

借鉴es6中的例子

let [x = 1] = [null];
//x=null 因为null === void 0 为false

新增模板字符串

2. 新增数据类型

3.新增数据结构Map,Set

  1. Map
    • 特征:
      • 存储属性为任意类型的键值对的集合(键值对数组),区别于普通对象只能存储字符串或Symbol类型。
      • 实例对象上有get,set,sjze方法来获取或设置键值,获取个数。
    • 作用:组件复用
//用法 
let map = Map([[a,1],[b,2]]);
console.log(map.get('a'));
map.set('c',3);
console.log(map.size) //3;
//将键值对数组map转换为对象
let map = new Map([['a','one'],['b','two'],['c','three']]); //希望得到{a:'one',b,'two',c:'three'}
let obj = Object.fromEntries(map); //Object.fromEntriesObject.entries的逆操作
  1. Set
    • 特征:数组是存储可重复数据的集合,Set存储不可重复的集合
    • 作用:数组去重,求交集、并集、
  • 数组去重
let arr = [1, 5, 8, 'ac', 1, 'ab', NaN, 5, 'ac', NaN]
let arr2 = Array.from(new Set(arr));
//ES5去重
Array.prototype.unique = function (){
	let arr = [];
	this.forEach(item=>{
		arr.indexOf(item) === -1 ? arr.push(item) : '';
	})
}
  • 交集、差集
let arr1 = [1, 2, 3, 3, 6, 8, 9];
let arr2 = [3, 4, 4, 5, 6, 8];
let a = new Set(arr1);
let b = new Set(arr2);
let newArr = Array.from([...a].filter((item) => b.has(item))); //交集
let newArr2 = Array.from([...a].filter((item) => !b.has(item))); //差集
console.log(newArr, newArr2); //[ 3, 6, 8 ] [ 1, 2, 9 ]
  • 并集
let newArr3 = new Set([...arr1,...arr2])

4.新增表达式

for...of

  • 使用场景
    • 只要实现了Iterator接口都可以使用for....of进行遍历
  • 常见的集合数据结构,Array,Map都有[Symbol.Interator]标识,所以都可以使用for...of进行遍历。但是Object没有

for...of 与 for...in对比?

新增Api

对象api

数组api

新增面向对象class

class Test {
    static create(param){
        console.log('this is Test.create');
        return new Test(param); //返回实例对象
    }
};

const a = new Test.create();

class get方法的属性

class Package {
  // 使用属性方式调用时会动态的调用函数
  get cacheFilePath() {
    return path.resolve(
      this.storePath,
     `_${this.cacheFilePathPrefix}@${this.packageVersion}@${this.packageName}`
    );
  }
  
  // Package是否存在
  async exists() {
    if (this.storePath) {
      await this.prepare();
      return pathExists(this.cacheFilePath);
    } else {
      return pathExists(this.targetPath);
    }
  }
}

class中的static是什么?

是定义class实例对象上的方法

新增异步操作

异步的理解

宿主环境:浏览器环境/Node环境 我们写了一段JS代码时,浏览器或Node环境传递给JS引擎,并且要求他们去执行 所以JS引擎会常驻在内存中,等待宿主环境(我们)把JS代码传递给他去执行 ES3及其之前JS本身没有能力执行异步代码,宿主环境将代码传递给JS引擎,JS引擎就顺次执行下去,这个任务是宿主发起的 ES5之后,JS引入了Promise,不需要宿主环境安排,JS引擎本身也就可以发起任务 宿主发起的任务称之为:宏任务 JS引擎发起的任务称之为:微任务

JS引擎等待宿主环境分配宏任务,在操作系统中,等待的行为都是一个事件循环,所以在Node术语这一部分称之为:事件循环

Promise

function sleep (sec) {
    return new Promise((resolve,reject)=>{setTimeout(resolve,sec)})
};
sleep(2000).then(()=>{console.log('hello')})


async sleep (sec) {
   console.log('hello');
   await sleep(3000);
   console.log('world');
}

实现一个红绿灯,把一个圆形div按照绿色3秒,黄色1秒,红色2秒循环改变背景色

    <style>
      #test {
        border: 20px;
        width: 100px;
        height: 100px;
        background-color: #ccc;
        border-radius: 50%;
      }
    </style>  


<body>
    <div id="test"></div>
    <script>
      const light = document.getElementById( 'test' );
      console.log( light );
      function sleep( sec ) {
        return new Promise( ( resolve, reject ) => {
          setTimeout( resolve, sec );
        } )
      };
      async function shineLight() {
        while ( 1 )
        {
          light.style.backgroundColor = 'green';
          await sleep( 3000 );
          light.style.backgroundColor = 'yellow';
          await sleep( 1000 );
          light.style.backgroundColor = 'red';
          await sleep( 2000 );
        }
      }
      shineLight();
    </script>
  </body>

async await

async function async1() {
  console.log('async1 start')
  await async2() //等async2执行完毕,会阻塞后面的代码,跳出async函数,执行其他代码
  console.log('async1 end') //放入到Promise.then中的代码
}

async function async2() {
  console.log('async2')
}

console.log('script start')
setTimeout(function () {
  console.log('setTimeout')
}, 0)

async1();

new Promise(function (resolve) {
  console.log('promise1')
  resolve();
}).then(function () {
  console.log('promise2')
})

console.log('script end')

    /*
    同:‘script start’ ‘async1 start’ ‘async2’ promise1 ‘script end’
    宏:‘setTimeout’
    微:‘async1 end’ ‘promise2’
    */
  • 异步的终极解决方案
  • 就是generator加上Promise的语法糖
  • 封装了generator的执行器和返回一个Promise
  • 返回的是一个Promise
  • await后面【表达式】的可以是一个Promise也可以是一个值,但是最终都转换成resolved的Promise;
  • await 下面的代码相当于Promise的then里的代码
  • 区别generator:内置了执行器,而且语义上更好理解,将异步函数以普通同步函数的方式书写。generator需要next函数触发执行

generator

参考:generator

function* foo(){
	yield 'hello';
	yield 'world';
	return 'ending';
}

let t = foo();
t.next(); //{value: 'hello', done: false}
t.next(); //{value: 'world', done: false}
t.next(); //{value: 'ending', done: true}
t.next(); //{value: 'undefined', done: true}
  • generator函数知识点:
    • 是一个所以说generator函数是一个状态机。
    • 调用generator函数只返回遍历器对象(Interator Object),不返回结果
    • 返回的遍历器对象调用next方法,开始执行yield 后面的表达式,返回{value: 'hello', done: false};直到遇到下一个yield或return停止执行。
    • 第二次调用t.next(),从上一次停下来的地方开始执行下一个yield后的表达式'world',返回yield后表达式的结果{value: 'world', done: false};以此类推
    • generator函数是分段执行的,函数是可以暂停执行的函数 (遇到yield就暂停执行);next恢复执行
function* foo(x) {
  let y = 2 * (yield (x + 1));
  let z = yield (y / 3);
  return (x + y + z);
};
let it = foo(5);
console.log('it', it);
console.log(it.next()); //{value: 6, done: false}
console.log(it.next(12)); 
console.log(it.next(13)); 
  1. generator函数数据交换:

面试:ES6中用到了什么?

class:如果没有class基本上使用原型链实现继承,

Promise,异步的内容,EventLoop,proxy

let,var:变量提升

接口超时解决方案

案例1: 轮训查合同

配合接口返回:轮训4次

try{
	setInterval(()=>{

	})
}catch(err){

}
let delayTime = 300;
let orderStatusDelayTime = 360; // 30m * 60s / 5 = 360
let contractHtmlDelayTimer = 4; // 20s / 5 = 4;
let timer: NodeJS.Timeout | null = null;
let orderStatusTimer: NodeJS.Timeout | null = null;
let contractTimer: NodeJS.Timeout | null = null;
let contractHtmlTimer: NodeJS.Timeout | null = null;

const clearTimer = () => {
  if (timer) {
    clearInterval(timer);
    timer = null;
    delayTime = 300;
  }
};

const clearOrderStatusTimer = () => {
  if (orderStatusTimer) {
    clearInterval(orderStatusTimer);
    orderStatusTimer = null;
    orderStatusDelayTime = 360;
  }
};

const clearContractTimer = () => {
  if (contractTimer) {
    clearInterval(contractTimer);
    contractTimer = null;
  }
};

const clearContractHtmlTimer = () => {
  if (contractHtmlTimer) {
    clearInterval(contractHtmlTimer);
    contractHtmlTimer = null;
    contractHtmlDelayTimer = 4;
  }
};


    /**
     * 轮询-获取合约草稿html
     */
    async contractResultQuery(dispatch: Dispatch, getState: Function) {
      try {
        const { paymentParam } = getState();
        const { amount } = paymentParam;
        const { result: { renderTraceId } } = await contractPreview({
          rechargeOrderAmount: (amount * 100).toString(),
        });

        if (!contractHtmlTimer && contractHtmlDelayTimer) {
          contractHtmlTimer = setInterval(async () => {
            const { modalIsClosed } = getState();
            if (modalIsClosed) {
              clearContractHtmlTimer();
              return;
            }
            contractHtmlDelayTimer -= 1;
            try {
              dispatch({
                type: 'setPreviewLoading',
                payload: true,
              });
              const { result: { renderStatus, html } } = await contractResultQuery({
                renderTraceId,
              });
              if (renderStatus === 'success') {
                clearContractHtmlTimer();
                dispatch({
                  type: 'setState',
                  payload: {
                    contractHtml: html.replace(/width: 800px;/g, ''),
                  },
                });
                dispatch({
                  type: 'setPreviewLoading',
                  payload: false,
                });
              }
              // 超时
              if (contractHtmlDelayTimer < 0 && renderStatus === 'waiting') {
                dispatch({
                  type: 'setState',
                  payload: {
                    previewTimeout: true,
                  },
                });
              }
            } catch (e) {
              e.message && message.error(e.message);
              console.log(e);
            }
            if (contractHtmlDelayTimer < 0) {
              clearContractHtmlTimer();
            }
          }, 5000);
        }
      } catch (e) {
        e.message && message.error(e.message);
        console.log(e);
      }
    },
<div className="recharge-content-container">
            {previewLoading ? (
              <div style={{ padding: '100px 0 30px 0' }} >
                {previewTimeout ? (
                  <>
                    <Icon style={{ fontSize: 48, color: '#ff5800' }} type="warning" className="loading-icon" />
                    <div className="info">
                      合约生成超时,请<a onClick={this.handleRetry}>重试</a>
                    </div>
                  </>
                ) : (
                    <>
                      <Icon type="loading" className="loading-icon" />
                      <div className="info">合约生成中...</div>
                      <div className="sub-info">大概需要5-10s,请耐心等待</div>
                    </>
                  )}

              </div>
            )
              : <div className="contract-container" dangerouslySetInnerHTML={{ __html: contractHtml }} />
            }
          </div>

在这里插入图片描述 在这里插入图片描述 弊端:其中一个接口报错,则一直是loading状态,跳出了setInterval

案例2:轮训查充值结果

let delayTime = 300;
    async queryPaymentResult(dispatch: Dispatch, getState: () => ModelState, { payload }: Action) {
      if (delayTime && !timer) {
        timer = setInterval(async () => {
          delayTime -= 5;
          const { payModalShow } = getState();
          if (!payModalShow) {
            clearTimer();
            return;
          }
          try {
            // 轮询
            const data = await queryPaymentResult(payload);
            await dispatch({
              type: 'setState',
              payload: {
                payResult: data.data,
              },
            });
            const { status } = data.data || {};
            if (status === 'INTIAL' || status === 'PAYING') {
              // await dispatch({
              //   type: 'queryPaymentResult',
              //   payload,
              // });
            } else {
              // 成功停止轮询
              clearTimer();
              // // 支付成功后,刷新账户信息
              // await dispatch({
              //   type: 'setState',
              //   payload: {
              //     rechargeModalShow: false,
              //   },
              // });
              // const { paymentParam } = getState();
              // await dispatch({
              //   type: 'getAssetAccountMReq',
              //   payload: {
              //     assetType: paymentParam.assetType,
              //   },
              // });
              // // 刷新列表
              // await dispatch({
              //   type: 'refresh',
              // });
            }
          } catch (err) {
            message.info(err.message);
          }

          if (delayTime <= 0) {
            clearTimer();
            await dispatch({
              type: 'setState',
              payload: {
                payResult: {
                  status: 'EXPIRED',
                },
              },
            });
          }
        }, 5000);
      }
    },

EventLoop

浏览器的EventLoop 执行同步代码, 同步代码执行完后,执行栈为空,检查是否有异步代码需要执行, 执行所有微任务, 然后下一轮EventLoop,执行宏任务中的异步代码。 Node中的EventLoop

JS是如何运行的?

JS是单线程运行的,线程和进程,执行栈,EventLoop,微任务,宏任务,JS是没有异步的概念,是浏览器给了JS执行异步程序的条件。哪

浏览器/Node的EventLoop的区别。JS的垃圾回收