前言
规范规定的原因:减少运行时错误,规范编码习惯,执行严格的编码规范;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
- 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.fromEntries是Object.entries的逆操作
- 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));
- 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的垃圾回收