ES6
箭头函数
结构:
() => 1
v => v+1
(a,b) => a+b
() => {
alert("foo");
}
e => {
if (e == 0){
return 0;
}
return 1000/e;
}
箭头函数和普通函数的区别:
- 箭头函数是匿名函数,不能作为构造函数,不能使用
NEW
- 箭头函数不绑定
arguments
,取而代之用rest参数…解决 - 箭头函数没有原型属性
- 箭头函数不能当做
Generator
函数,不能使用yield
关键字 - 箭头函数的
this
永远指向其上下文的this
,任何方法都改变不了其指向,如call() , bind() , apply()
。普通函数的this指向调用它的那个对象
模板字符串
var a = `123 ${first} ${last}.`
解构赋值
获取数组中的值
//从数组中获取值并赋值到变量中,变量的顺序与数组中对象顺序对应
var foo = ['one', 'two', 'three', 'four']
var [one, two, three] = foo
console.log(one) //one
console.log(two) //two
console.log(three) //three
//如果你要忽略某些值,你可以按照下面的写法获取你想要的值
var [first, , , last] = foo
console.log(first) //one
console.log(last) //four
//你也可以这样写
var a, b;
[a, b] = [1, 2]
console.log(a) //1
console.log(b) //2
//如果没有从数组中的获取到值,你可以为变量设置一个默认值
var a, b;
[a = 5, b = 7] = [1]
console.log(a) //1
console.log(b) //7
//通过解构赋值可以方便的交换两个变量的值
var a = 1
var b = 3
;[a, b] = [b, a]
console.log(a) //3
console.log(b) //1
获取对象中的值
const student = {
name: 'Ming',
age: '18',
city: 'Shanghai',
}
const { name, age, city } = student
console.log(name) //Ming
console.log(age) //18
console.log(city) //Shanghai
扩展运算符
//函数调用
function sum(x, y, z) {
return x + y + z
}
const numbers = [1, 2, 3]
console.log(sum.apply(null, numbers)) //6
console.log(sum(...numbers)) //6
//构造函数
const stuendts = ['Jine', 'Tom']
const persons = ['Tony', ...stuendts, 'Aaron', 'Anna']
console.log(persons) //["Tony", "Jine", "Tom", "Aaron", "Anna"]
//数组拷贝
var arr = [1, 2, 3]
var arr2 = [...arr,4]
arr2.push(5)
console.log(arr2) //[1, 2, 3, 4, 5]
//连接多个数组
var arr1 = [0, 1, 2]
var arr2 = [3, 4, 5]
var arr3 = [...arr1, ...arr2]
//等同于
var arr4 = arr1.concat(arr2)
//对象
var obj1 = { foo: 'bar', x: 42 }
var obj2 = { foo: 'baz', y: 13 }
var clonedObj = { ...obj1 }
var mergedObj = { ...obj1, ...obj2 }
console.log(clonedObj) //{foo: "bar", x: 42}
console.log(mergedObj) //{foo: "baz", x: 42, y: 13}
对象属性简写
let name = 'Ming',
age = '18',
city = 'Shanghai'
let student = {
name,
age,
city,
}
console.log(student) //{name: "Ming", age: "18", city: "Shanghai"}
promise(重点说一下)
理解:
解决异步和回调地狱的一种方案
从语法上说Promise
是一个构造函数
从功能上说Promise
对象用来封装一个异步操作并可以获取其结果
三种状态:
pending
:等待状态,比如正在进行网络请求,或者定时器没有到时间。
fulfill
:满足状态,当我们主动回调了resolve
时,就处于该状态,并且会回调.then()
reject
:拒绝状态,当我们主动回调了reject
时,就处于该状态,并且会回调.then()
或者.catch()
两种状态改变:
pending
变为 fulfilled
pending
变为 rejected
流程:
基本使用:
//1.创建一个新的promise对象
const p = new Promise((resolve, reject) => { //执行器函数是同步回调!
console.log('执行 executor') //立刻执行
//2.执行异步操作
setTimeout(() => {
const time = Date.now()
//3.1 成功,调用resolve(value)
if( time % 2 === 0 ){
resolve('成功的数据,time=' + time)
} else {
//3.2 失败,调用reject(reason)
reject('失败的数据,time=' + time)
}
}, 1000)
})
console.log('new Promise()之后') //先输出 '执行 exceutor'
p.then(
value => { // onfulfilled函数,自动接收成功的value
console.log('成功的回调', value)
},
reason => { // onrejected函数,自动接收失败的reason
console.log('失败的回调', reason)
}
)
API
new Promise( (resolve, reject) => {
setTimeout( () => {
resolve('成功') //resolve就像是一个传递数据的运输机
}, 1000 )
}).then(
value => {
console.log('onResolved()1', value)
}
).catch(
reason => {
console.log('onRejected()1', reason)
}
)
const p1 = new Promise((resolve, reject) => {
resolve(1)
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
// p1.then( value => {console.log(value)} )
// p2.then( value => {console.log(value)} )
// p3.catch( reason => {console.log(reason)} )
//const pAll = Promise.all([p1,p2,p3])
const pAll = Promise.all([p1,p2])
pAll.then(
values => {
console.log('all onResolved()', values)
},
reason => {
console.log('all onRejected()', reason)
}
)
const pRace = Promise.race([p1,p2,p3])
pRace.then(
value => {
console.log('race onResolved()', value)
},
reason => {
console.log('race onResolved()', reason)
}
)
一个promise指定多个成功/失败回调函数, 都会调用吗?
当promise改变为对应状态时都会调用
改变promise状态和指定回调函数谁先谁后?
- 都有可能,正常情况下先指定回调在改变状态,也可以先改变状态在指定回调
- 如何先改状态再指定回调?
在执行器中直接调用resolve()/reject()
延迟更长时间才调用then()
- 什么时候才能得到数据?
如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
promise异常传透?
当使用 promise 的 then
链式调用时, 可以在最后指定失败的回调(.catch()
),前面任何操作出了异常, 都会传到最后失败的回调中处理。
中断promise链?
当使用promise的then
链式调用时, 在中间中断, 不再调用后面的回调函数
办法: 在回调函数中返回一个pending
状态的promise对象
promise和async|await的区别?
async / await
就是Promise
的语法糖promise
错误可以通过catch
来捕捉,建议尾部捕获错误, async/await既可以用.then
又可以用try-catch
捕捉
let 与 const
let、const、var的区别?
- var是
ES5
提出的,let和const是ES6
提出的 const
与let
都是块级作用域,var
定义的变量为函数级作用域const
声明的是常量,必须赋值,不能使用null
占位,声明后不能再修改,如果声明的是复合类型数据,可以修改其属性var
允许重复声明变量,后一个变量会覆盖前一个变量。let和const
在同一作用域不允许重复声明变量var
声明的变量存在变量提升,值为undefined
。let和const
不存在变量提升
ES7
数组.includes()
用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false
let arr = ['react', 'angular', 'vue']
//使用 includes() 验证数组中是否存在某个元素
if (arr.includes('react')) {
console.log('react存在') //react存在
}
//使用 indexOf()验证数组中是否存在某个元素,需要根据返回值是否为 -1来判断
if (arr.indexOf('react') !== -1) {
console.log('react存在') //react存在
}
ES8
async/await
await
后面接一个会return new promise
的函数并执行它
await
只能放在async
函数里
async函数会返回一个promise
,并且Promise对象的状态值是resolved
await 命令后面的 Promise 对象,运行结果可能是 rejected
,所以最好把 await 命令放在 try...catch
代码块中。
用法:
function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
}
function step2(m, n) {
console.log(`step2 with ${m} and ${n}`);
return takeLongTime(m + n);
}
function step3(k, m, n) {
console.log(`step3 with ${k}, ${m} and ${n}`);
return takeLongTime(k + m + n);
}
//使用async/await
async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time1, time2);
const result = await step3(time1, time2, time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");
}
doIt();
// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 800 = 300 + 500
// step3 with 1800 = 300 + 500 + 1000
// result is 2000
// doIt: 2907.387ms
//使用promise
function doIt() {
console.time("doIt");
const time1 = 300;
step1(time1)
.then(time2 => {
return step2(time1, time2)
.then(time3 => [time1, time2, time3]);
})
.then(times => {
const [time1, time2, time3] = times;
return step3(time1, time2, time3);
})
.then(result => {
console.log(`result is ${result}`);
console.timeEnd("doIt");
});
}
doIt();
ES9
Object.values()
Object.values()
是一个与 Object.keys()
类似的新函数,但返回的是 Object 自身属性的所有值,不包括继承的值
const obj = { a: 1, b: 2, c: 3 }
//不使用 Object.values()
const vals = Object.keys(obj).map((key) => obj[key])
console.log(vals) //[1, 2, 3]
//使用 Object.values()
const values = Object.values(obj1)
console.log(values)//[1, 2, 3]
Object.entries()
Object.entries()
函数返回一个给定对象自身可枚举属性的键值对的数组
//不使用Object.entries()
Object.keys(obj).forEach((key) => {
console.log('key:' + key + ' value:' + obj[key])
})
//使用Object.entries()
for (let [key, value] of Object.entries(obj)) {
console.log(`key: ${key} value:${value}`)
}
// key: a value:1
// key: b value:2
// key: c value:3
Rest/Spread 属性
为对象解构提供了和数组一样的 Rest
参数()和展开操作符,只适用于每个对象的顶层,如果对象中嵌套对象则无法适用
const myObject = {
a: 1,
b: 2,
c: 3,
}
const { a, ...x } = myObject
// x = { b: 2, c: 3 }
ES10
Array的 flat()方法和 flatMap()方法
flat()和 flatMap()本质上就是是归纳(reduce
) 与 合并(concat
)的操作。
var arr1 = [1, 2, [3, 4, [5, 6]]]
arr1.flat() //[1, 2, 3, 4, [5, 6]]
arr1.flat(3) //[1, 2, 3, 4, 5, 6]
var arr4 = [1, 2, , 4, 5]
arr4.flat() //[1, 2, 4, 5]
var arr1 = [1, 2, 3, 4]
arr1.map((x) => [x * 2]) //[[2],[4],[6],[8]]
// 只会将 flatMap 中的函数返回的数组 “压平” 一层
arr1.flatMap((x) => [x * 2]) //[2,4,6,8]
arr1.flatMap((x) => [[x * 2]]) //[[2],[4],[6],[8]]
String的 trimStart()方法和 trimEnd()方法
分别去除字符串首尾空白字符
Object.fromEntries()
Object.fromEntries()
是 Object.entries()
的反转
Object.fromEntries()
函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。这个迭代参数应该是一个能够实现 @iterator
方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类似数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值
//通过 Object.fromEntries, 可以将 Map 转化为 Object
const map = new Map([
['foo', 'bar'],
['baz', 42],
])
const obj = Object.fromEntries(map)
console.log(obj) //{foo: "bar", baz: 42}
//通过 Object.fromEntries, 可以将 Array 转化为 Object
const arr = [
['0', 'a'],
['1', 'b'],
['2', 'c'],
]
const obj = Object.fromEntries(arr)
console.log(obj) //{0: "a", 1: "b", 2: "c"}
新的基本数据类型 BigInt
JavaScript 中 Number
类型只能安全的表示-(2^53-1
)至 2^53-1
范的值
var bigNumRet = 9007199254740993n + 9007199254740993n // -> -> 18014398509481986n
bigNumRet.toString() // -> '18014398509481986'
现在的基本数据类型加上BigInt一共有七种基本数据类型,分别是:String、Number、Boolean、Null、Undefined、Symbol、BigInt
注意: 尽可能避免通过调用函数 BigInt 方式来实例化超大整型。因为参数的字面量实际也是 Number 类型的一次实例化,超出安全范围的数字,可能会引起精度丢失
Symbol
symbol
是一种基本数据类型,表示独一无二的值。Symbol()
函数会返回 symbol
类型的值,该类型具有静态属性和静态方法。
每个从 Symbol()
返回的 symbol
值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数据类型仅有的目的
const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');
console.log(typeof symbol1); // "symbol"
console.log(symbol3.toString()); // "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo')); // false
ES11
Promise.allSettled
我们需要一种机制,如果并发任务中,无论一个任务正常或者异常,都会返回对应的的状态(fulfilled
或者 rejected
)与结果(业务 value
或者 拒因 reason
),在 then
里面通过 filter
来过滤出想要的业务逻辑结果,这就能最大限度的保障业务当前状态的可访问性,而 Promise.allSettled
就是解决这问题的。
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"
Promise 中的三兄弟 .all(), .race(), .allSettled()
Promise.all(iterable)
方法返回一个 Promise 实例,此实例在iterable
参数内所有的promise
都“完成(resolved
)”或参数中不包含promise
时回调完成(resolve
);如果参数中promise
有一个失败(rejected
),此实例回调失败(reject
),失败原因的是第一个失败promise
的结果Promise.race(iterable)
方法返回一个 promise,一旦迭代器中的某个promise
解决或拒绝,返回的promise
就会解决或拒绝。Promise.allSettled()
方法返回一个promise,该promise
在所有给定的promise
已被解析或被拒绝后解析,并且每个对象都描述每个promise
的结果
可选链
可选链中的 ? 表示如果问号左边表达式有值, 就会继续查询问号后面的字段
//这是一种丑陋但又不得不做的前置校验,否则很容易命中 Uncaught TypeError: Cannot read property... 这种错误,这极有可能让你整个应用挂掉。
var age = user && user.info && user.info.getAge && user.info.getAge()
//改写
var age = user?.info?.getAge?.()
空值合并运算符
当我们查询某个属性时,经常会遇到,如果没有该属性就会设置一个默认的值
var level = (user && user.level) || '暂无等级'
//在 JS 中,空字符串、0 等,当进行逻辑操作符判断时,会自动转化为 false,所以要用if去判断类型,
var level;
if (typeof user.level === 'number') {
level = user.level;
} else if (!user.level) {
level = '暂无等级';
} else {
level = user.level;
}
//改写
var level = user.data?.level ?? '暂无等级' //暂无等级
dynamic-import
现代前端打包资源越来越大,打包成几 M 的 JS 资源已成常态,而往往前端应用初始化时根本不需要全量加载逻辑资源,为了首屏渲染速度更快,很多时候都是按需加载,比如懒加载图片等。而这些按需执行逻辑资源都体现在某一个事件回调中去加载
el.onclick = () => {
import(`/path/current-logic.js`)
.then((module) => {
module.doSomthing()
}).catch((err) => {
// load error;
})
}
ES12
replaceAll
返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉
const str = 'hello world';
str.replaceAll('l', ''); // "heo word"
Promise.any
Promise.any()
接收一个Promise
可迭代对象,只要其中的一个 promise
成功,就返回那个已经成功的 promise
。如果可迭代对象中没有一个 promise
成功(即所有的 promises
都失败/拒绝),就返回一个失败的 promise
和AggregateError
类型的实例,它是 Error
的一个子类,用于把单一的错误集合在一起。本质上,这个方法和Promise.all()
是相反的。
const pErr = new Promise((resolve, reject) => {
reject("总是失败");
});
const pSlow = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "最终完成");
});
const pFast = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "很快完成");
});
Promise.any([pErr, pSlow, pFast]).then((value) => {
console.log(value);
// pFast fulfils first
})
// 输出: "很快完成"
注: Promise.any()
方法依然是实验性的,尚未被所有的浏览器完全支持
逻辑运算符和赋值表达式
a ||= b
//等价于
a = a || (a = b)
a &&= b
//等价于
a = a && (a = b)
a ??= b
//等价于
a = a ?? (a = b)
数字分隔符
const money = 1_000_000_000;
//等价于
const money = 1000000000;
1_000_000_000 === 1000000000; // true