先来张Vue的风暴图,感受一下抽象的席卷~
ES(6-12)全版本语法
ES6
es6是js的规格,js是es6的实现
1. 新的声明方式:let
变量
- 不属于顶层对象window
- 不允许重复声明
- 不存在变量提升
- 暂时性死区
- 块级作用域
1. 不属于顶层对象window
//声明的是变量,具有作用域
var a = 5
console.log(windeow.a) //可以输出
//没有var,是一个对象
b = 6
console.log(windeow.b) //可以输出
let的出现是为了弥补var将变量挂在window上的缺陷
static文件夹下的文件是原封不动地上传到浏览器
而src文件夹下的文件会经过webpack打包,会规避一些问题
2. 不允许重复声明
var可以多次重复声明(最后一次声明会覆盖前面的声明),而let不能(会报错)
可以避免重复命名
3. 不存在变量提升
console.log(a)
var a = 5
//相当于
var a
console.log(a)
a = 5
而let不存在变量提升
4. 暂时性死区
var a = 5
if(true){
a = 6
let a
}
//会报错,a没有进行声明,在if{}里是一个暂时性死区
5. 块级作用域
for(var i=0;i<3;i++){
console.log(i) //输出0 1 2
}
console.log(i) //只会输出3,因为var不存在块级作用域,i=3时不满足条件则结束循环,跳出循环之后被外部的console.log输出
//将var改成let,则外部的console报错
块级作用域使得代码更加安全
- 允许在块级作用域内声明函数
- 函数声明类似于
var,即会提升到全局作用域或函数作用域的头部 - 同时,函数声明还会提升到所在的块级作用域的头部
参考:zhuanlan.zhihu.com/p/100856823
2. 新的声明方式:const
常量,不能被重新赋值
const a
a = 5 //报错,应const a = 5
对于引用类型,const不能改变其引用地址,但是可以改变堆内存中的值
const obj = {
name:'yl',
age:11
}
//这里添加一行 Object.freeze(obj),后面的就无法改变(但只能冻结第一层,如果多层嵌套需要obj.name)
obj.sex = 'G' //obj中会添加这一值(堆内存可以改变,栈不能改变)
- 不属于顶层对象window
- 不允许重复声明
- 不存在变量提升
- 暂时性死区
- 块级作用域
区别:
var let const 函数级作用域 块级作用域 块级作用域 变量提升 不存在变量提升 不存在变量提升 值可更改 值可更改 值不可更改
let VS const
默认情况下优先使用const,如果需要被改变再考虑let
let 变量 const 常量
3. ==解构赋值(常用)==
- 按照一定模式,从数组和对象中提取值,对变量进行赋值
- 数组解构
- 对象解构
- 字符串解构
- 应用
默认参数的使用(当没有传这个值的时候,默认赋该值)
等号左右两边的结构一样即可
const [a,b,c,d = 5] = [1,2,[3,4]] //输出[1,2,[3,4],5]
const [a,b,c,d = 5] = [1,2,[3,4],6] //输出[1,2,[3,4],6]
//即如果右边有值则为右边的值,否则输出左边赋的默认值;如果右边没有值,左边也没有默认值,则underfined
-
数组通过索引进行配对(按顺序解构)
-
对象通过键名进行配对(变量必须和属性同名)
-
解构也适用于嵌套结构的对象(要使用一样的结构)
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined //不同名,取不到
嵌套赋值:
let obj = {};
let arr = [];
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
obj // {prop:123}
arr // [true]
- 字符串的解构和数组相似
function foo([a,b,c]) {
console.log(a,b,c)
}
let arr = [1,2,3]
foo(arr)
对于json
let json = '{"a":"hello","b":"world"}' let {a,b} = JSON.parse(json) //将json格式输出成对象,再进行解构赋值
使用了别名之后,真正被赋值的是后者
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
注意:
// 错误的写法
let x;
{x} = {x: 1};
// 正确的写法
let x;
({x} = {x: 1}); //将解构赋值语句放在一个原括号里
-
数值和布尔值的解构赋值:
会先转换为对象
解构赋值的规则:
只要等号右边的值不是对象或数组,就先转换为对象
undefined和null无法转为对象,故无法进行解构赋值 -
函数的参数也可以使用解构赋值
用途:
-
交换变量的值
let x = 1; let y = 2; [x, y] = [y, x]; -
从函数返回多个值
// 返回一个数组 function example() { return [1, 2, 3]; } let [a, b, c] = example(); // 返回一个对象 function example() { return { foo: 1, bar: 2 }; } let { foo, bar } = example(); -
函数参数的定义
// 参数是一组有次序的值 function f([x, y, z]) { ... } f([1, 2, 3]); // 参数是一组无次序的值 function f({x, y, z}) { ... } f({z: 3, y: 2, x: 1}); -
提取JSON数据
-
函数参数的默认值
-
遍历Map结构
-
输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
4. 数组的各种遍历方式
ES5中的数组遍历方式
- for循环
- forEach():没有返回值,只是针对每个元素调用func
- map():返回新的Array,每个元素为调用func的结果
- filter():返回符合func条件的元素数组
- some():返回布尔,判断是否有元素符合func条件
- every():返回布尔,判断每个元素是否符合func条件
- reduce():接收一个函数作为累加器
- for in ???
arr = [1,2,3]
//for
for(let i = 0; i<arr.length;i++) {
//....
}
//forEach(不支持break continue)
arr.forEach(function(elem,index,array){
//.....
})
//map
let result = arr.map(function(value){
value += 1
return value
})
console.log(arr,result) //map循环之后会生成新的数组,不会去更改之前的arr
//filter(过滤)
let result = arr.filter(function(value){
return value == 2
})
console.log(arr,result) //会生成一个新的数组,这个新的数组只会保存满足条件的值
//some
let result = arr.some(function(value){
return value == 4
})
console.log(arr,result) //返回的是一个布尔值,因为arr中没有4,所以返回false(只要找到一个满足条件的值就会返回true)
//every
let result = arr.every(function(value){
return value == 2
})
console.log(arr,result) //所有元素都满足条件时才会返回true
//reduce
//0初始值 prev上一个处理的元素 cur当前处理的元素 index当前处理元素的索引 array原数组
let sum = arr.reduce(function(prev,cur,index,array){
return prev + cur
},0) //得到的就是求和的结果
//reduce可以实现求max min 去重等
//去重
let res = arr.reduce(function(prev,cur){
prev.indexOf(cur) == -1 && prev.push(cur)
return prev
},[])
//for in xx
//这种方法遍历数组会将arr上的所有东西遍历出来(包括原型上的方法)
for(let index in arr){
//....
}
ES6中数组遍历方法
- find():返回第一个通过测试的元素
- findIndex():返回的值为该通过第一个元素的索引
- for of
- values()
- keys()
- entries()
arr = [1,2,3,2,4]
//find
let res = arr.find(function(value){
return value == 2
})
console.log(arr,res) //res返回的2为arr的第一个2
//findIndex
let res = arr.findIndex(function(value){
return value == 2
})
console.log(arr,res) //res返回的是为arr的第一个2的索引
//for of
for(let item of arr){
console.log(item)
}
//for(let item of arr.values()){} 和上面的效果一样
//arr.values() 为内容
//arr.keys() 为索引
//arr.entries() 为两者都输出
for(let [index,item] of arr.entries()){
console.log(index,item)
}
5. 数组的扩展
-
类数组/伪数组
有长度,但不能使用数组的方法
-
Array.from()
-
Array.of()
-
copyWithin()
-
fill()
-
includes()
es5中,可以通过slice方法将伪数组转换成数组
let arr = Array.prototype.slice.call(divs3)
arr.push(123) //此时已经转换成了真正的数组,使用数组方法不会报错
es6中:
//Array.from() 将其转换为数组
Array.from(arrayLike)
//Array.of()
let arr = Array.of(1,2)
let arr = Array.of(3)
//let arr = new Array(3) 这个返回的是3个空白,并不是数组[3]。这种方法会随着传入的参数个数不同而得到不同的数组
//copyWithin()替换元素
let arr = [1,2,3,4,5]
console.log(arr.copyWithin(1,3)) //从第一个位置开始读取,再读取下标为3的数组,(因为没有第三个参数,所有默认到结尾),于是就用4,5来替换2,3
//fill()填充
//1.
let arr = new Array(3).fill(7) //数组长度为3,用7进行填充,于是得到[7,7,7]
//2.
let arr = [1,2,3,4,5]
arr.fill('yl',1,3) //从下标为1开始替换,直到下标为3(不包括) 得到[1,'yl','yl',4,5]
arr.fill(0) //全部被替换成0
//includes()是否包含
NAN == NAN 不相等
6. 函数的参数
- 参数的默认值
- 与解构赋值结合
- length属性
- 作用域
- 函数的name属性
1. 参数的默认值
//es5
function foo(x,y){
y = y || 'world' //判断参数是否存在,但存在问题
console.log(x,y)
}
foo('hello',0) //如果不传y值,则打印'world';而0由于是false,所以打印出来的是world
//es6
function foo(x, y = 'world'){
console.log(x,y)
}
foo('hello',0) //此时打印出来的是hello,0
//函数内部的参数已经默认声明过了,使用const或let再次声明会报错
//函数内部的参数不能重名 eg.foo(x,x,y)报错
function foo(x = 5){
//这里不能再声明x
}
foo()
//参数的默认值放最后面
function foo(x,z,y=5){}
2. 与解构赋值结合
function foo({x,y = 5}){
console.log(x,y)
}
foo({}) //打印出 underfined 5(x没有赋值),这里符合解构赋值
//foo() 报错,结构要一样才可以
与默认值一同使用
function ajax(url,{
body = '',
method = 'GET',
headers = {}
} = {}){ //如果不传入第二个参数,则默认值为空值
console.log(method)
}
ajax('http://ww.imooc.com',{
method: 'POST'
}) //POST
3. length属性
返回没有指定默认值的个数
4. 作用域
let x = 1
function foo(x,y=x){ //()中形成了一个作用域,故y取到的值为这个作用域里面的x值
console.log(y) //2
}
foo(2)
let x = 1
function foo(y=x){
let x = 2
console.log(y) //1
}
foo()
//没有传入参数,此时y会沿着作用域链**往外**找,找到全局变量中有一个x的值,然后赋值得到
//如果没有声明全局变量,则返回的是underfined
5. 函数的name属性
(new Function).name //输出anonymous
7. 拓展运算符 与 rest参数
-
...
-
扩展运算符:把数组或者类数组展开成用逗号隔开的值
-
rest参数:把逗号隔开的值组合成一个数组
互逆操作
如果...放在等号左边或是形参上,则rest参数
如果...放在等号右边或是实参上,则扩展运算符
function foo(a,b,c){
console.log(a,b,c)
}
let arr = [1,2,3]
foo(..arr)
//如果使用foo(arr)需要使用解构赋值,而使用拓展运算符则会将arr变成1,2,3
//合并数组
let arr1 = [1,2,3]
let arr2 = [4,5,6]
//es5
Array.prototype.push.apply(arr2,arr2) //在原型上进行push apply
//es6
arr1.push(...arr2) //...可以打散arr2,再通过push加上去
//打散字符串
let str = 'hello'
var arr = [...str] //得到["h","e","l","l","o"]
//es5
function foo(x,y,z) {
let sum = 0
Array.prototype.forEach.call(arguments,function(item){ //arguments返回的是伪数组
sum += item
})
return sum
}
console.log(foo(1,2)) //3
console.log(foo(1,2,3)) //6
//使用es6中Array.from转换数组
//Array.from(arguments).forEach(function(item){})
//使用reset参数(对于不确定参数) 参数要放在最后
function foo(...args) {
console.log(args)
let sum = 0
args.forEach(function(item){
sum += item
})
return sum
}
//reset提取剩余的参数
function foo(x,...args) {
console.log(x) //1
console.log(args) //[2,3,4,5]
}
foo(1,2,3,4,5)
//同样适用于解构赋值中
let [x,...y] = [1,2,3]
console.log(x) //1
console.log(y) //[2,3]
8. 箭头函数
-
this指向定义时所在的对象,而不是调用时所在的对象
箭头函数里没有this,会往外一层去找this
-
不可以当作构造函数
-
不可以使用arguments对象
箭头函数的写法:箭头左边是参数,右边是方法体
let sum = (x,y) => {
return x + y
}
//可以简写成 let sum = (x,y) => x + y (方法体只有一行代码)
//es5中构造函数
function People(name,age){
console.log(this)
this.name = name
this.age = age
}
let p1 = new People('yl',11)
let foo = (..args) => {
//console.log(arguments) 浏览器会报错
//可以使用reset参数进行输出
console.log(args)
}
foo(1,2,3)
9. 对象的扩展
-
属性简洁表示法
-
属性名表达式
-
Object.is() 即===
-
拓展运算符 与 Object.assign()
-
in
-
对象的遍历方式
1. 属性简洁表示法 属性名表达式
let name = 'yl'
let age = 11
let s = 'school'
let obj = {
name,
age,
[s]:'gdut' //如果想要使用变量,则加上[]
study(){ //es6为对象提供了一种简写的方式,如果使用箭头函数会报错,this指代的是window
console.log(this.name + 'studying')
}
}
2. Object.is()
obj1 == obj2 //false
obj存储的是一个引用地址,每一个obj都会进行一次new Object(),在堆内存中进行存储,所以哪怕两个对象内容一模一样,在堆内存中的位置也是不一样的,故返回false
同样 Object.is(obj1 == obj2) //false
let obj1 = obj2
Object.is(obj1 == obj2) //true
3. 拓展运算符 与 Object.assign()
let x = {
a: 3
b: 4
}
let y = {..x}
console.log(y) //{a:3,b:4}
//Object.assign()
let x = {
a: 3, //后面的值会覆盖前面的,所以a:3
b: 4
}
let y = {
c:5,
a:6
}
Object.assign(y,x)
console.log(y) //{a:6,b:4,c:5}
4. in
判断对象中是否存在
如果是数组:
console.log(3 in arr) //下标为3是否存在
5. 对象的遍历方式
//1
for (let key in obj){
console.log(key,obj[key])
}
//2
Object.keys(obj).forEach(key => {
console.log(key,obj[key])
})
//3
Object.getOwnPropertyNames(obj).forEach(key =>{
console.log(key,obj[key])
})
//4
Reflect.ownKeys(obj).forEach(key => {
console.log(key,obj[key])
})
10. 深拷贝与浅拷贝
1. 浅拷贝
let Foo = {
a: 3,
b: 4
}
let newFoo = Foo
newFoo.a = 5
//使用object.assign()
let Foo = {
a: 3,
b: 4
}
// let newFoo = Foo
Object.assign(newFoo, Foo)
newFoo.a = 5
改变内容,都会改变(因为改变的是引用地址)
2. 深拷贝
-
JSON方式
JSON.parse() 将JSON字符串转换成JavaScript对象
JSON.stringify() 将JavaScript对象转换成JSON字符串
let Foo = { a: { c:1 }, b: 4 } let str = JSON.stringify(Foo) let newFoo = JSON.parse(str) newFoo.a.c = 5 -
递归
let checkType = data => { return Object.prototype.toString.call(data).slice(8, -1) } let deepClone = target => { let targetType = checkType(target) let result // 初始化操作 if (targetType === 'Object') { result = {} } else if (targetType === 'Array') { result = [] } else { // 都不是的话证明是基本数据类型,基本数据 // 类型只会有一个值,所以直接返回这个值就可以了 return target } // target不是基本类型,进入遍历 for (let i in target) { let value = target[i] let valueType = checkType(value) if (valueType === 'Object' || valueType === 'Array') { result[i] = deepClone(value) // 递归 } else { // 是基本类型直接赋值 result[i] = value } } return result }
不过深拷贝还是建议看Condrail的,好像是叫这个,掘金搜深拷贝就行
11. 面向过程与面向对象
面向过程:强调实现需求的步骤
面向对象:对象的属性、方法
JavaScript是一种基于对象的语言
类是对象的模板,定义了同一组对象共有的属性和方法
12. ES5中的类与继承
组合式继承
function Animal(name) {
this.name = name;
}
Animal.prototype.showName = function () {
console.log('名字为' + this.name);
}
//子类
function Dog(name,color) {
Animal.call(this,name); //继承父类的属性,**但不继承父类的方法**
this.color = color;
}
Dog.prototype = new Animal(); //组合继承,既能继承属性又能继承方法
Dog.prototype.constuctor = Dog;
lett d = new Dog('wangcai','white');
console.log(d1);
继承的其他东西可以看我另一个文章介绍了6种继承的方法
13. ES6中的类与继承
1. class是语法糖
class People {
constructor(name,age) {
this.name = name;
this.age = age;
}
showName(){
console.log(this.name);
}
}
let p1 = new People('yl',11);
console.log(p1);
2. 继承 extends
class Coder extends People {
constructor(name,age,company){
super(name,age);
this.company = company;
}
showCompany(){
console.log(this.company);
}
}
3. Setters&Getters
class Animal {
constructor(type, age) {
this.type = type;
this._age = age;
}
get age() { //只读
return this._age;
}
set age(val) { //可写
this._age = val;
}
}
使用这种方式可以在里面写语句
eg.
set age(val) { if (val > 0 && val < 10) { #age = val } }
4. 静态方法
使用static来标记
class Animal {
constructor(type) {
this.type = type
}
walk() {
console.log( `I am walking` )
}
static eat() {
console.log( `I am eating` )
}
}
- 类中的构造器不是必须写的,要写实例进行一些初始化的操作,如添加指定属性时才写
- 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的
- 类中所定义的方法,都是放在了类的原型对象上,供实例去使用
//传统方法
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
//class方法
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() { //方法必须使用该语法,方法名(){}
return '(' + this.x + ', ' + this.y + ')';
}
}
- Object.assign()一次向类添加多个方法
class Point {
constructor(){
// ...
}
}
Object.assign(Point.prototype, {
toString(){},
toValue(){}
});
- 类必须使用
new调用
es5里,实例的属性是函数原型的属性
在es6中,static声明静态属性,属性属于类不属于实例
function Phone(){ } Phone.name = '手机'; //name属性属于函数对象的,不属于实例对象,称为静态属性 Phone.prototype.size = '5.5inch'; //原型 let nokia = new Phone(); //实例化 console.log(nokia.name); //报错 console.log(nokia.size); //输出 5.5inch
//构造方法
constructor(brand, price){
this.brand = brand;
this.price = price;
}
//父类的成员属性
call(){
console.log("我可以打电话!!");
}
}
class SmartPhone extends Phone { //用extends来继承
//构造方法
constructor(brand, price, color, size){
super(brand, price);// Phone.call(this, brand, price) 关键字super
this.color = color;
this.size = size;
}
photo(){
console.log("拍照");
}
playGame(){
console.log("玩游戏");
}
//call(){
// console.log('我可以进行视频通话'); //子类对父类方法的重写
}
}
const xiaomi = new SmartPhone('小米',799,'黑色','4.7inch');
- 取值get 存值set
14. 新的原始数据类型Symbol
let s = new Symbol() 错误,不能使用new
Symbol不是对象,不能添加属性(是一种类似于字符串的数据类型)
1. 独一无二
这个可以保证相同key值的也保存下来(比如重名学生)
let s1 = Symbol();
console.log(s1); //Symbol()
let s2 = Symbol();
console.log(s1 === s2); //false
2. 自动调用toString()函数
const obj = {
name: 'yl',
toString(){
return this.name
}
}
let s = Symbol(obj);
console.log(s); //Symbol(yl)
3. Symbol.for()
在全局中注册的
不会每次调用都返回一个新的 Symbol 类型的值,而是先检查给定的key是否已经存在,不存在才新建
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
console.log(s1 === s2);//true
4. Symbol.keyFor()
返回一个已经登记的Symbol类型值的key
const s1 = Symbol('foo')
console.log(Symbol.keyFor(s1)) // undefined
const s2 = Symbol.for('foo')
console.log(Symbol.keyFor(s2)) // foo
5. 属性遍历
const sym = Symbol('imooc')
class User {
constructor(name) {
this.name = name
this[sym] = 'imooc.com'
}
getName() {
return this.name + this[sym]
}
}
const user = new User('xiecheng')
console.log(user.getName())
for (let key in user) { //不能遍历symbol类型的值
console.log(key)
}
for (let key of Object.keys(user)) { //不能遍历symbol类型的值
console.log(key)
}
for (let key of Object.getOwnPropertySymbols(user)) { //只能遍历symbol类型的值
console.log(key)
}
for (let key of Reflect.ownKeys(user)) { //全都能遍历
console.log(key)
}
可以很好地保护symbol值
6. 消除魔术字符串
function getArea(shape) {
let area = 0
switch (shape) {
case 'Triangle'://魔术字符串
area = 1
break
case 'Circle':
area = 2
break
}
return area
}
console.log(getArea('Triangle'))
const shapeType = {
triangle: Symbol(),//使用symbol赋一个独一无二的值
circle: Symbol()
}
function getArea(shape) {
let area = 0
switch (shape) {
case shapeType.triangle:
area = 1
break
case shapeType.circle:
area = 2
break
}
return area
}
console.log(getArea(shapeType.triangle))
15. 新的数据结构Set
数据结构 Se类似于数组,但是成员的值都是唯一的,没有重复的值
1. 基本语法
生成 Set 实例
let s = new Set()
let s = new Set([1, 2, 3, 4])
添加数据
s.add('hello')
s.add('goodbye')
s.add('hello').add('goodbye') //写在一起
添加重复的数据是无效的
删除数据
s.delete('hello') // 删除指定数据
s.clear() // 删除全部数据
统计数据
// 判断是否包含数据项,返回 true 或 false
s.has('hello') // true
// 计算数据项总数
s.size // 2
数组去重
let arr = [1, 2, 3, 4, 2, 3]
let s = new Set(arr)
合并去重
let arr1 = [1, 2, 3, 4]
let arr2 = [2, 3, 4, 5, 6]
let s = new Set([...arr1, ...arr2])
console.log(s)
console.log([...s])
console.log(Array.from(s))
交集
let s1 = new Set(arr1)
let s2 = new Set(arr2)
let result = new Set(arr1.filter(item => s2.has(item)))
console.log(Array.from(result))
差集
let arr3 = new Set(arr1.filter(item => !s2.has(item)))
let arr4 = new Set(arr2.filter(item => !s1.has(item)))
console.log(arr3)
console.log(arr4)
console.log([...arr3, ...arr4])
2. 遍历方式
console.log(s.keys()) // SetIterator {"hello", "goodbye"}
console.log(s.values()) // SetIterator {"hello", "goodbye"}
console.log(s.entries()) // SetIterator {"hello" => "hello", "goodbye" => "goodbye"}
s.forEach(item => {
console.log(item) // hello // goodbye
})
for (let item of s) {
console.log(item)
}
for (let item of s.keys()) {
console.log(item)
}
for (let item of s.values()) {
console.log(item)
}
for (let item of s.entries()) {
console.log(item[0], item[1]) //key值和value值都是一样的
}
3. WeakSet
区别:
成员只能是对象,而不能是其他类型的值
没有size属性,不能遍历
弱引用
所谓垃圾回收机制:
如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在
const ws = new WeakSet()
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
let ws = new WeakSet()
const obj1 = {
name: 'imooc'
}
const obj2 = {
age: 5
}
console.log(ws)
console.log(ws.has(obj2))
16. 新的数据类型Map
类似于对象,键值对的集合
“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应
是一种更完善的 Hash 结构实现
如果你需要“键值对”的数据结构,Map 比 Object 更合适。
1. 基本语法
实例化
let map = new Map();
let map = new Map([
['name','yl'],
['age',5]
])
console.log(map); //Map(2) {"name" => 'yl',"age" => 5}
添加数据
let obj = {
name: 'yl'
}
map.set(obj,'66');
删除数据
map.delete(keyObj); // 删除指定的数据
map.clear(); // 删除所有数据
统计数据
console.log(map.size) //2
console.log(map.has(keyObj)) //判断是否有 key-value
查询数据
console.log(map.get(keyObj)) // 和键'keyObj'关联的值
2. 遍历方式
map.forEach((value, key) => console.log(value, key)) //value, key
for (let [key, value] of map) { //key, value
console.log(key, value)
}
for (let key of map.keys()) {
console.log(key)
}
for (let value of map.values()) {
console.log(value)
}
for (let [key, value] of map.entries()) {
console.log(key, value)
}
Map VS Object:
键的类型
Object的键: 字符串或者 Symbols
Map 的键: 任意值
键的顺序
Object的键:无序
Map的键值:有序
进行遍历时,Map 对象是按插入的顺序返回键值。
键值对的统计
Object的个数:只能手算
Map的个数:用size
键值对的遍历
Object:先获取键数组,再进行迭代
Map:可直接进行迭代
性能
在涉及频繁增删键值对的场景下,Map 会有些性能优势
3. WeekMap
// WeakMap 可以使用 set 方法添加成员
const wm1 = new WeakMap()
const key = {
foo: 1
}
wm1.set(key, 2)
wm1.get(key) // 2
// WeakMap 也可以接受一个数组
// 作为构造函数的参数
const k1 = [1, 2, 3]
const k2 = [4, 5, 6]
const wm2 = new WeakMap([
[k1, 'foo'],
[k2, 'bar']
])
wm2.get(k2) // "bar"
区别:
-
WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名
-
不计入垃圾回收机制
17. 字符串的扩展
1. Unicode表示法(少用)
Unicode有啥用:
保证简便高效和保持与已有编码标准兼容之间的平衡
在内部使用Unicode的应用程序,能够同时存储和处理世界上所有的字符,这消除了传统的国际化方法所面临的一些困难
- es5
"\u0061" // "a"
只限于码点在\u0000~\uFFFF之间的字符
超出须用两个双字节的形式表示
"\uD842\uDFB7" // "𠮷"
- es6
将码点放入大括号
"\u{20BB7}" // "𠮷"
'\z' === 'z' // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true
2. 遍历器接口
for (let item of 'imooc') {
console.log(item)
}
3. ==模板字符串==
-
多行字符串
使用后,不需要使用/n换行
-
插入表达式
var a = 5; var b = 10; console.log(`Fifteen is ${a + b} and not ${2 * a + b}.`);如果模板字符串中的变量没有声明,会报错
// 变量place没有声明 let msg = `Hello, ${place}`; // 报错 -
嵌套模板
-
标签模板
==tag函数(?)==
4. 扩展方法
-
String.fromCodePoint()
从 Unicode 码点返回对应字符(可以识别大于0xFFFF的字符)
弥补了
String.fromCharCode()方法的不足 -
String.includes()
是否包含该字符串(es5中使用indexOf)
const str = 'imooc' console.log(str.includes('mo')) //true -
String.startsWith()
判断是否在头部
const str = 'imooc' console.log(str.endsWith('mooc')) //true -
String.endsWith()
判断是否在尾部
上述三个方法都有第二个参数n
includes和startsWith从第n个位置直到字符串结束
endsWith是对前n个字符
-
String.repeat(n)
将原字符串重复n次后返回一个字符串
如果是小数,会被取整
如果是负数或者infinity,报错
NaN等同0
const str = 'yl' const newStr = str.repeat(10) console.log(newStr) //ylylylylylylylylylyl -
String.raw() 在斜杆前面再加一个斜杆
String.raw`Hi\n${2+3}!` //"Hi\\n5!"// 等同于`foo${1 + 2}bar` "foo3bar" String.raw({ raw: ['foo', 'bar'] }, 1 + 2) -
String.codePointAt() 返回码点的十进制值
-
String.normalize()
-
String.trimStart()【trimLeft()】 消除头部的空格,尾部会被保留
-
String.trimEnd() 【trimRight()】消除尾部的空格,头部会被保留
-
String.matchAll() 返回一个正则表达式在当前字符串的所有匹配
-
String.replaceAll(searchValue, replacement) 替换掉所有匹配值
searchValue不能是不带g修饰符的正则表达式,会报错
replacement为替换的文本,也可以是函数,或是以下特殊字符串:
-
$&:匹配的子字符串。 -
$`:匹配结果前面的文本。 -
$':匹配结果后面的文本。 -
$n:匹配成功的第n组内容,n是从1开始的自然数。这个参数生效的前提是,第一个参数必须是正则表达式。 -
$$:指代美元符号$// $& 表示匹配的字符串,即`b`本身 // 所以返回结果与原字符串一致 'abbc'.replaceAll('b', '$&') // 'abbc' // $` 表示匹配结果之前的字符串 // 对于第一个`b`,$` 指代`a` // 对于第二个`b`,$` 指代`ab` 'abbc'.replaceAll('b', '$`') // 'aaabc' // $' 表示匹配结果之后的字符串 // 对于第一个`b`,$' 指代`bc` // 对于第二个`b`,$' 指代`c` 'abbc'.replaceAll('b', `$'`) // 'abccc' // $1 表示正则表达式的第一个组匹配,指代`ab` // $2 表示正则表达式的第二个组匹配,指代`bc` 'abbc'.replaceAll(/(ab)(bc)/g, '$2$1') // 'bcab' // $$ 指代 $ 'abc'.replaceAll('b', '$$') // 'a$c'
在es5中使用replace()如果想要匹配所有,需要使用正则表达式
-
18. 正则表达式的拓展
作用:检索、替换那些符合某个模式(规则)的文本
eg. 验证表单(匹配)、过滤页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)
1. RegExp构造函数
- 利用RegExp对象来创建
- 利用字面量创建
var regex = new RegExp('xyz', 'i');
// 等价于 var regex = /xyz/i;
//ES5不允许此时使用第二个参数添加修饰符
var regex = new RegExp(/xyz/i);
// 等价于 var regex = /xyz/i;
测试正则表达式 test() 返回布尔值 regexObj.test(str) 检测是否符合正则表达式要求的规范
正则表达式里面不需要使用引号
2. y修饰符
-
“粘连”修饰符
后一次匹配都从上一次匹配成功的下一个位置开始
与g修饰符类似,全局匹配
不同:
- g修饰符只要剩余位置中存在匹配就可
- y修饰符确保匹配必须从剩余的第一个位置开始
var s = 'aaa_aa_a'; var r1 = /a+/g; var r2 = /a+/y; r1.exec(s) // ["aaa"] r2.exec(s) // ["aaa"] r1.exec(s) // ["aa"] r2.exec(s) // null //y修饰符号隐含了头部匹配的标志^
- 检测 y 标志 =>
sticky
var pattern = /hello\d/y;
console.log(patten.sticky);
- lastIndex 指定从xx位置开始匹配
3. u修饰符
Unicode模式
-
处理大于
\uFFFF的Unicode字符 -
点字符 除了换行符以外的任意单个字符
var s = '𠮷'; /^.$/.test(s) // false /^.$/u.test(s) // true,需要添加u字符 -
i修饰符
/[a-z]/i.test('\u212A') // false /[a-z]/iu.test('\u212A') // true -
unicode 是否设置了
u修饰符 -
处理不兼容es6:
function hasRegExpU() { try { var pattern = new RegExp(".", "u"); return true; } catch (ex) { return false; } }
4. flags属性
-
source 获取正则表达式的文本
-
flags 返回正则表达式中石油标志组成的字符串形式
var re = /ab/g; console.log(re.source); // "ab" console.log(re.flags); // "g"
5. 后行断言
-
先行断言:
x只有在y前面才匹配,必须写成/x(?=y)/ -
先行否定断言:
x只有不在y前面才匹配,必须写成/x(?!y)/ -
后行断言:
x只有在y后面才匹配,必须写成/(?<=y)x/ -
后行否定断言:
x只有不在y后面才匹配,必须写成/(?<!y)x/
6. 具名组匹配
用圆括号分组
const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31
ES2018引入了具名组匹配
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
7. 引用
如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法
8. 正则匹配索引
-
indices 返回每个组
-
indices.groups 提供具名组匹配
Z的开始位置和结束位置获取组匹配不成功,均返回
undefined
const text = 'zabbcdef';
const re = /ab+(cd(ef))/;
const result = re.exec(text);
result.indices // [ [1, 8], [4, 8], [6, 8] ]
const text = 'zabbcdef';
const re = /ab+(?<Z>cd)/;
const result = re.exec(text);
result.indices.groups // { Z: [ 4, 6 ] }
19. 数值的拓展
1. 二进制0B 八进制0O
const a = 5;
console.log(a.toString(2)); //十进制转换成二进制 101
const b = 101;
console.log(parseInt(b,2)); //二进制转换成十进制
const a = 0B0101 //二进制
console.log(a)
const b = 0O777 //八进制
console.log(b) //输出的是十进制
2. 新增方法
-
Number.isFinite() 检查一个数值是否为有限的
//数值就会返回true,其他的都是false Number.isFinite(15) // true Number.isFinite(0.8) // true Number.isFinite(NaN) // false Number.isFinite(Infinity) // false Number.isFinite(-Infinity) // false Number.isFinite('foo') // false Number.isFinite('15') // false Number.isFinite(true) // false -
Number.isNaN() 检查一个值是否为NaN
//NAN值就返回true Number.isNaN(NaN) // true Number.isNaN(15) // false Number.isNaN('15') // false Number.isNaN(true) // false Number.isNaN(9 / NaN) // true Number.isNaN('true' / 0) // true Number.isNaN('true' / 'true') // true -
Number.parseInt()
在es5中,parseInt是window上的
-
Number.parseFloat()
同上
-
Number.isInteger() 判断一个数值是否为整数
JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。
存在误判的情况 例如精度丢失、小于Number.MIN_VALUE
Number.isInteger(25) // true Number.isInteger(25.1) // false Number.isInteger() // false Number.isInteger(null) // false Number.isInteger('15') // false Number.isInteger(true) // false -
Number.MAX_SAFE_INTEGER 最大安全数:2^53 = 9007199254740991
-
Number.MIN_SAFE_INTEGER -9007199254740991
-
Number.isSafeInteger() 在-2^53^到2^53^之间(不含两个端点)
-
Number.EPSILON 表示 1 与大于 1 的最小浮点数之间的差 [可接受的最小误差范围]
最小精度。误差如果小于这个值,就可以认为已经没有意义了,即不存在误差
3. Math拓展
ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用
-
Math.trunc() 去除一个数的小数部分,返回整数部分
true代表1,false代表0,其余非数值的返回NaN
console.log(Math.trunc(5.5)) console.log(Math.trunc(-5.5)) console.log(Math.trunc(true)) // 1 console.log(Math.trunc(false)) // 0 console.log(Math.trunc(NaN)) // NaN console.log(Math.trunc(undefined)) // NaN console.log(Math.trunc()) // NaN -
Math.sign() 判断正数、负数、零
true和false会转换为数值后进行判断
console.log(Math.sign(5)) // 1 console.log(Math.sign(-5)) // -1 console.log(Math.sign(0)) // 0 console.log(Math.sign(NaN)) // NaN console.log(Math.sign(true)) // 1 console.log(Math.sign(false)) // 0 -
Math.cbrt() 计算一个数的立方根,非数的返回NaN
-
Math.clz32() 将参数转为 32 位无符号整数的形式,返回 32 位值里面有多少个前导 0 只考虑整数部分
Math.clz32(1000) // 22 1000 的二进制形式是0b1111101000,一共有10位,所以32位之中有22个前导0 Math.clz32(0b01000000000000000000000000000000) // 1 Math.clz32(0b00100000000000000000000000000000) // 2左移运算符(
<<)与Math.clz32方法直接相关 -
Math.imul() 效果和
(a*b)|0相同,可以处理溢出的情况Math.imul(-2, -2) // 4 (0x7fffffff * 0x7fffffff)|0 // 0 Math.imul(0x7fffffff, 0x7fffffff) // 1
-
Math.fround() 将64位双精度浮点数转为32位单精度浮点数
// 丢失精度 Math.fround(0.7) // 0.699999988079071 //对于 NaN 和 Infinity,此方法返回原值 Math.fround(NaN) // NaN Math.fround(Infinity) // Infinity //先将其转为数值,再返回单精度浮点数 Math.fround('5') // 5 Math.fround(true) // 1 Math.fround(null) // 0 Math.fround([]) // 0 Math.fround({}) // NaN
-
Math.hypot() 返回所有参数的平方和的平方根
先将非数值的转换为数值,无法转换的返回NaN
Math.hypot(3, 4); // 5 Math.hypot(3, 4, 5); // 7.0710678118654755 Math.hypot(); // 0 Math.hypot(NaN); // NaN Math.hypot(3, 4, 'foo'); // NaN Math.hypot(3, 4, '5'); // 7.0710678118654755 Math.hypot(-3); // 3 -
Math.expm1()
Math.expm1(x)=> ex - 1 ==Math.exp(x) - 1。 -
Math.log1p()
Math.log1p(x)==Math.log(1 + x) -
Math.log10() 返回以 10 为底的
x的对数 -
Math.log2() 返回以 2 为底的
x的对数
以上三个方法,如果
x小于 0,则返回 NaN
- 双曲函数方法:
Math.sinh(x)返回x的双曲正弦Math.cosh(x)返回x的双曲余弦Math.tanh(x)返回x的双曲正切Math.asinh(x)返回x的反双曲正弦Math.acosh(x)返回x的反双曲余弦Math.atanh(x)返回x的反双曲正切
20. 代理proxy
自定义一些常用行为如查找、赋值、枚举、函数调用等
1. 基本语法
let p = new Proxy(target, handler)
target :用来代理的“对象”,被代理之后不能直接被访问
handler :实现代理的过程
2. 拦截操作场景
let o = {
name: 'xiaoming',
age: 20
}
let handler = {
get(obj, key) {
return Reflect.has(obj, key) ? obj[key] : ''
}
}
let p = new Proxy(o, handler)
console.log(p.from)
场景 1
从服务端获取的数据希望是只读,不允许在任何一个环节被修改。
// response.data 是 JSON 格式的数据,来自服务端的响应
// 在 ES5 中只能通过遍历把所有的属性设置为只读
for (let [key] of Object.entries(response.data)) {
Object.defineProperty(response.data, key, {
writable: false
})
}
使用 Proxy :
let data = new Proxy(response.data, {
set(obj, key, value) {
return false
}
})
场景 2
校验
// Validator.js
export default (obj, key, value) => {
if (Reflect.has(key) && value > 20) {
obj[key] = value
}
}
import Validator from './Validator'
let data = new Proxy(response.data, {
set: Validator
})
场景 3
对读写进行监控:
let validator = {
set(target, key, value) {
if (key === 'age') {
if (typeof value !== 'number' || Number.isNaN(value)) { // 非数值、空值
throw new TypeError('Age must be a number')
}
if (value <= 0) { // 输入的值小于等于0
throw new TypeError('Age must be a positive number')
}
}
return true
}
}
const person = {
age: 27
}
const proxy = new Proxy(person, validator)
// 添加监控
window.addEventListener(
'error',
e => {
console.log(e.message) // Uncaught TypeError: Age must be a number
},
true
)
场景 4
实例一个对象,每个对象都有一个自己的 id 而且只读。
class Component {
constructor() {
this.proxy = new Proxy({
id: Math.random().toString(36).slice(-8)
})
}
get id() {
return this.proxy.id
}
}
3. 常用拦截操作
get
拦截对象属性的读取
let arr = [7, 8, 9]
arr = new Proxy(arr, {
get(target, prop) {
return prop in target ? target[prop] : 'error'
}
})
console.log(arr[1]) //8
console.log(arr[10]) //error
set
拦截对象属性的设置
let arr = []
arr = new Proxy(arr, {
set(target, prop, val) {
if (typeof val === 'number') {
target[prop] = val
return true //需要返回一个布尔值
} else {
return false
}
}
})
arr.push(5)
arr.push(6)
console.log(arr[0], arr[1], arr.length)
has
拦截propKey in proxy的操作,返回一个布尔值。
let range = {
start: 1,
end: 5
}
range = new Proxy(range, {
has(target, prop) {
return prop >= target.start && prop <= target.end
}
})
console.log(2 in range)
console.log(9 in range)
ownKeys
拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组
该方法返回目标对象所有自身的属性的属性名
而Object.keys()的返回结果仅包括目标对象自身的可遍历属性
let obj = {
name: 'imooc',
[Symbol('es')]: 'es6'
}
console.log(Object.getOwnPropertyNames(obj)) //["name"]
console.log(Object.getOwnPropertySymbols(obj)) //[Symbol(es)]
console.log(Object.keys(obj))
for (let key in obj) {
console.log(key) //name
}
let userinfo = {
username: 'xiecheng',
age: 34,
_password: '***'
}
userinfo = new Proxy(userinfo, {
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_')) // 过滤
}
})
console.log(Object.keys(userinfo))
deleteProperty
拦截delete proxy[propKey]的操作,返回一个布尔值
let user = {
name: 'xiecheng',
age: 34,
_password: '***'
}
user = new Proxy(user, {
get(target, prop) {
if (prop.startsWith('_')) {
throw new Error('不可访问')
} else {
return target[prop]
}
},
set(target, prop, val) {
if (prop.startsWith('_')) {
throw new Error('不可访问')
} else {
target[prop] = val
return true //返回一个布尔值
}
},
deleteProperty(target, prop) { // 拦截删除
if (prop.startsWith('_')) {
throw new Error('不可删除')
} else {
delete target[prop]
return true
}
},
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'))
}
})
console.log(user.age)
console.log(user._password)
user.age = 18
console.log(user.age)
try {
user._password = 'xxx'
} catch (e) {
console.log(e.message)
}
try {
// delete user.age
delete user._password
} catch (e) {
console.log(e.message)
}
console.log(user.age)
for (let key in user) {
console.log(key)
}
apply
拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)
let sum = (...args) => {
let num = 0
args.forEach(item => {
num += item
})
return num
}
sum = new Proxy(sum, {
apply(target, ctx, args) {
return target(...args) * 2
}
})
console.log(sum(1, 2))
console.log(sum.call(null, 1, 2, 3))
console.log(sum.apply(null, [1, 2, 3])) //需要是数组
construct
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)
let User = class {
constructor(name) {
this.name = name
}
}
User = new Proxy(User, {
construct(target, args, newTarget) {
console.log('construct')
return new target(...args)
}
})
console.log(new User('imooc'))
21. 反射Reflect
和Proxy一起使用
1. 设计目的
- 将Object属于语言内部的方法放到Reflect上
let obj = {}
let newVal = ''
Reflect.defineProperty(obj, 'name', {
get() {
return newVal
},
set(val) {
console.log('set')
// this.name = val
newVal = val
}
})
obj.name = 'es'
console.log(obj.name)
- 修改某些Object方法的返回结果,让其变得更合理
// 老写法
try {
Object.defineProperty(target, property, attributes)
// success
} catch (e) {
// failure
}
// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}
- 让Object操作变成函数行为
// 老写法
'assign' in Object // true
// 新写法
Reflect.has(Object, 'assign') // true
-
Reflect对象的方法与Proxy对象的方法一一对应
(只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法)
Proxy(target, {
set: function(target, name, value, receiver) {
var success = Reflect.set(target, name, value, receiver)
if (success) {
console.log('property ' + name + ' on ' + target + ' set to ' + value)
}
return success
}
})
Reflect 是一个内置的对象,提供拦截 JavaScript 操作的方法,这些方法与处理器对象的方法相同
Reflect不是一个函数对象,因此它是不可构造的。
Reflect没有构造函数(不能与new使用,或将Reflect对象作为函数调用
Reflect的所有属性和方法都是静态的
2. 常用方法
Reflect.apply()
Reflect.apply(target, thisArgument, argumentsList)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 目标函数 | Y |
| thisArgument | target函数调用时绑定的this对象 | N |
| argumentsList | target函数调用时传入的实参列表,该参数应该是一个类数组的对象 | N |
Reflect.apply(Math.floor, undefined, [1.75]) // 1
Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]) // "hello"
Reflect.apply(RegExp.prototype.exec, /ab/, ['confabulation']).index // 4
Reflect.apply(''.charAt, 'ponies', [3]) // "i"
ES5 对比
与ES5中Function.prototype.apply()方法类似
Function.prototype.apply.call(Math.floor, undefined, [1.75])
Reflect.construct()
允许使用可变的参数来调用构造函数
Reflect.construct(target, argumentsList[, newTarget])
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 被运行的目标函数 | Y |
| argumentsList | 调用构造函数的数组或者伪数组 | Y |
| newTarget | 该参数为构造函数, 参考 new.target 操作符,如果没有newTarget参数, 默认和target一样 | N |
如果target或者newTarget不是构造函数,抛出TypeError
#### function someConstructor() {}
var result = Reflect.construct(Array, [], someConstructor)
Reflect.getPrototypeOf(result) // 输出:someConstructor.prototype
Array.isArray(result) // true
Reflect.defineProperty()
静态方法 Reflect.defineProperty() 基本等同于 Object.defineProperty() 方法,唯一不同是返回 Boolean 值。
Reflect.defineProperty(target, propertyKey, attributes)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 目标对象 | Y |
| propertyKey | 要定义或修改的属性的名称 | Y |
| attributes | 要定义或修改的属性的描述 | Y |
const student = {}
Reflect.defineProperty(student, 'name', {
value: 'Mike'
}) // true
student.name // "Mike"
Reflect.deleteProperty()
Reflect.deleteProperty 允许你删除一个对象上的属性
返回一个 Boolean 值表示该属性是否被成功删除
Reflect.deleteProperty(target, propertyKey)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 删除属性的目标对象 | Y |
| propertyKey | 将被删除的属性的名称 | Y |
#### var obj = {
x: 1,
y: 2
}
Reflect.deleteProperty(obj, "x") // true
obj // { y: 2 }
var arr = [1, 2, 3, 4, 5]
Reflect.deleteProperty(arr, "3") // true
arr // [1, 2, 3, , 5]
// 如果属性不存在,返回 true
Reflect.deleteProperty({}, "foo") // true
// 如果属性不可配置,返回 false
Reflect.deleteProperty(Object.freeze({
foo: 1
}), "foo") // false
Reflect.get()
Reflect.get() 方法的工作方式,就像从 object (target[propertyKey]) 中获取属性,但它是作为一个函数执行的。
Reflect.get(target, propertyKey[, receiver])
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 需要取值的目标对象 | Y |
| propertyKey | 需要获取的值的键值 | Y |
| receiver | 如果遇到 getter,此值将提供给目标调用 | N |
// Object
var obj = {
x: 1,
y: 2
}
Reflect.get(obj, 'x') // 1
// Array
Reflect.get(['zero', 'one'], 1) // "one"
// Proxy with a get handler
var x = {
p: 1
}
var obj = new Proxy(x, {
get(t, k, r) {
return k + 'bar'
}
})
Reflect.get(obj, 'foo') // "foobar"
Reflect.getOwnPropertyDescriptor()
与 Object.getOwnPropertyDescriptor() 方法相似
如果在对象中存在,则返回给定的属性的属性描述符,否则返回 undefined
Reflect.getOwnPropertyDescriptor(target, propertyKey)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 需要寻找属性的目标对象 | Y |
| propertyKey | 获取自己的属性描述符的属性的名称 | N |
Reflect.getOwnPropertyDescriptor({
x: 'hello'
}, 'x')
// {value: "hello", writable: true, enumerable: true, configurable: true}
Reflect.getOwnPropertyDescriptor({
x: 'hello'
}, 'y')
// undefined
Reflect.getOwnPropertyDescriptor([], 'length')
// {value: 0, writable: true, enumerable: false, configurable: false}
对比
如果该方法的第一个参数不是一个对象(一个原始值),那么将造成 TypeError 错误
而对于 Object.getOwnPropertyDescriptor,非对象的第一个参数将被强制转换为一个对象处理
Reflect.getOwnPropertyDescriptor("foo", 0) // TypeError: "foo" is not non-null object Object.getOwnPropertyDescriptor("foo", 0) // { value: "f", writable: false, enumerable: true, configurable: false }
Reflect.getPrototypeOf()
与 Object.getPrototypeOf() 方法是一样
返回指定对象的原型
Reflect.getPrototypeOf(target)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 获取原型的目标对象 | Y |
Reflect.has()
检查一个对象是否拥有某个属性, 相当于in 操作符
Reflect.has(target, propertyKey)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 获取原型的目标对象 | Y |
| propertyKey | 属性名,需要检查目标对象是否存在此属性 | Y |
Reflect.isExtensible()
Reflect.isExtensible 判断一个对象是否可扩展 (即是否能够添加新的属性)
与 Object.isExtensible() 方法一样
Reflect.isExtensible(target)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 获取原型的目标对象 | Y |
Reflect.ownKeys()
返回一个由目标对象自身的属性键组成的数组
返回值等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
Reflect.ownKeys(target)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 获取原型的目标对象 | Y |
Reflect.ownKeys({
z: 3,
y: 2,
x: 1
}) // [ "z", "y", "x" ]
Reflect.ownKeys([]) // ["length"]
var sym = Symbol.for("comet")
var sym2 = Symbol.for("meteor")
var obj = {
[sym]: 0,
"str": 0,
"773": 0,
"0": 0,
[sym2]: 0,
"-1": 0,
"8": 0,
"second str": 0
}
Reflect.ownKeys(obj)
// [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]
// Indexes in numeric order,
// strings in insertion order,
// symbols in insertion order
Reflect.preventExtensions()
阻止新属性添加到对象 (eg. 防止将来对对象的扩展被添加到对象中)
与 Object.preventExtensions() 方法一致
Reflect.preventExtensions(target)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 获取原型的目标对象 | Y |
#### // Objects are extensible by default.
var empty = {}
Reflect.isExtensible(empty) // === true
// ...but that can be changed.
Reflect.preventExtensions(empty)
Reflect.isExtensible(empty) // === false
Reflect.preventExtensions(1)
// TypeError: 1 is not an object
Object.preventExtensions(1)
// 1
Reflect.set()
允许在对象上设置属性
给属性赋值,并像 property accessor 语法一样,但以函数的方式
Reflect.set(target, propertyKey, value[, receiver])
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 获取原型的目标对象 | Y |
| propertyKey | 设置的属性的名称 | Y |
| value | 设置的值 | Y |
| receiver | 如果遇到 setter,this 将提供给目标调用 | N |
// Object
var obj = {}
Reflect.set(obj, "prop", "value") // true
obj.prop // "value"
// Array
var arr = ["duck", "duck", "duck"]
Reflect.set(arr, 2, "goose") // true
arr[2] // "goose"
// It can truncate an array.
Reflect.set(arr, "length", 1) // true
arr // ["duck"]
// With just one argument, propertyKey and value are "undefined".
var obj = {}
Reflect.set(obj) // true
Reflect.getOwnPropertyDescriptor(obj, "undefined")
// { value: undefined, writable: true, enumerable: true, configurable: true }
Reflect.setPrototypeOf()
改变指定对象的原型
Reflect.setPrototypeOf(target, prototype)
| 参数 | 含义 | 必选 |
|---|---|---|
| target | 获取原型的目标对象 | Y |
| prototype | 对象的新原型 (一个对象或 null) | Y |
Reflect.setPrototypeOf({}, Object.prototype) // true
// It can change an object's [[Prototype]] to null.
Reflect.setPrototypeOf({}, null) // true
// Returns false if target is not extensible.
Reflect.setPrototypeOf(Object.freeze({}), null) // false
// Returns false if it cause a prototype chain cycle.
var target = {}
var proto = Object.create(target)
Reflect.setPrototypeOf(target, proto) // false
22. 异步操作
console.log(1); //(1)
setTimeout(() => { //(2)
console.log(2)
},0);
console.log(3); //(3)
//1 3 2
(1)(3)属于主线程任务,为同步操作,(2)为异步任务,先进入Event Table中,等待0秒后进入Event Queue中等待主线程的任务全部完成后,再读取任务队列中结果进入主线程执行。
所以,如果有一个异步任务经过2秒后进入到Event Queue中,但是主线程的任务需要5秒才能执行完毕,此时的异步任务会在Event Queue中等待主线程任务完成,即等待3秒后进入主线程。
-
Ajax
function ajax(url,callback) { // 1. 创建XMLHttpRequest对象 var xmlhettp if(window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest() } else { //兼容早期浏览器 xmlhttp = new ActiveXObject('Microsoft.XMLHTTP') } // 2. 发送请求 xmlhttp.open('GET',url,true) xmlhttp.send() // 3. 服务端相应 xmlhttp.onreadystatechange = function () { if(xmlhttp.readState === 4 && xmlhttp.staus === 200) { var obj = JSON.parse(xmlhttp.responseText]) callback(obj) } } } var url = '...'; ajax(url,res => { console.log(res) })
23. Promise
1. 基本用法
//resolve成功,rejecth失败
let p = new Promise((resolve,rejecth) => {
setTimeout(() => {
console.log(1)
// 一般情况下,使用if else语句进行判断是否成功
//if(){
// resolve()
// }else{
// reject()
// }
},1000)
}).then(() => { //第一个方法必须要写,第二个方法可以省略
console.log('成功')
},() => {
console.log('失败')
})
//可以在resolve写入参数,再通过传参来完成
//resolve('success')
//reject('fail')
//.then((res) => {
// console.log(res) //success
//}),(err) => {
// console.log(err) //fail
//}
2. 状态
let p1 = new Promise((resolve, reject) => {
resolve(1)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 1000)
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(3)
}, 1000)
})
console.log(p1) // resolved
console.log(p2) // pending ==> 1秒后变成resolved
console.log(p3) // pending ==> 1秒后变成rejected
setTimeout(() => {
console.log(p2)
}, 2000)
setTimeout(() => {
console.log(p3)
}, 2000)
p1.then(res => {
console.log(res) //1
})
p2.then(res => {
console.log(res) //2
})
p3.catch(err => { //使用catch捕获错误
console.log(err) //3
})
let p = new Promise((resolve, reject) => {
resolve(2)
reject(1)
}).then(res => {
console.log(res)
}).catcj(err => {
console.log(err)
})
//只能输出2,Promise状态不能被改变
3. 使用Promise发送ajax请求
单纯使用ajax需要嵌套非常多层
使用Promise有大量重复代码,抽离出来写成一个函数,使得代码可读性更强,也有利于后期维护
function getPromise(url) {
return new Promise((resolve, reject) => {
ajax(url, res => {
resolve(res)
})
})
}
getPromise(...)
.then(res => {
console.log(res)
return getPromise(...)
}).then(res => {
console.log(res)
return getPromise(...)
}).then(res => {
console.log(res)
})
统一捕获err
function getPromise(url) {
return new Promise((resolve, reject) => {
ajax(url, res => {
resolve(res)
})
})
}
getPromise(...)
.then(res => {
console.log(res)
return getPromise(...)
}).then(res => {
console.log(res)
return getPromise(...)
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}) //上述任何一个出现错误都会调用
4. Promise的静态方法
Promise.resolve('success')
Promise.reject('fail')
function foo(flag) {
if(flag) {
return new Promise(resolve => {
//异步操作
resolve('success')
})
} else {
return Promise.reject('fail') //如果写成return 'fail',当条件为false的时候,会报错
}
}
foo(false).then(res => {
console.log(res) //fail
},err => {
console.log(err)
})
Promise.all([...]) 所有对象都完成之后才会进入res,只要有一个是失败的,都会进入err中
可应用于上传多张图片中
Promise.all([p1,p2,p3]).then(res => {
console.log(res)}, err => {
console.log(err)
})
const imgArr = ['1.jpg', '2.jpg', '3.jpg']
let promiseArr = []
imgArr.forEach(item => {
promiseArr.push(new Promise((resolve, reject) => {
// 图片上传的操作
resolve()
}))
})
Promise.all(promiseArr).then(res => {
// 插入数据库的操作
console.log('图片全部上传完成')
})
Promise.race([...]) 只要有一个成功,整个就会进入res中
可应用于请求图片超时
Promise.race([p1,p2,p3]).then(res => {
console.log(res)}, err => {
console.log(err)
})
function getImg() {
return new Promise((resolve, reject) => {
let img = new Image()
img.onload = function () {
resolve(img) //返回图片
}
// img.src = 'http://www.xxx.com/xx.jpg'
img.src = 'https://www.imooc.com/static/img/index/logo.png'
})
}
function timeout() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时')
}, 2000)
})
}
Promise.race([getImg(), timeout()]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}
24. Generator
function* foo() {
for (let i = 0; i < 3; i++) {
yield i
}
}
let f = foo()
console.log(f.next())
console.log(f.next())
console.log(f.next())
console.log(f.next())
//yield关键字只存在于Generator,这里的的yield关键字是在forEach函数里的
// function* gen(args) {
// args.forEach(item => {
// yield item + 1
// })
// }
对应结果:
function* gen(x) {
let y = 2 * (yield(x + 1))
let z = yield(y / 3)
return x + y + z
}
//在next里可以传递参数
let g = gen(5)
console.log(g.next()) // 6
console.log(g.next(12)) // y=24 8(对应的x+1=12)
console.log(g.next(13)) // z=13 x=5 42(对应的y/3=13
使用Generator进行ajax请求
function request(url) {
ajax(url, res => {
getData.next(res)
})
}
function* gen() {
let res1 = yield request('static/a.json')
console.log(res1)
let res2 = yield request('static/b.json')
console.log(res2)
let res3 = yield request('static/c.json')
console.log(res3)
}
let getData = gen()
getData.next()
25. Module
- export default 默认,导入不需要知道命名(可以直接使用别名)
- import * from '../../xx.js'
把庞大的代码拆开
将多个功能的代码按功能进行分开,以达到多个模块组合在一起形成一个功能复杂的功能
-
好处:
- 防止命名冲突
- 代码复用
- 高维护性
-
语法
<script type="module"> </script>也可以使用
<script src="./src/js/app.js" type="module"></script>将引用部分放到另一个js文件里-
export 对外接口
导入的时候命名要完全一样,可以起别名,起了别名之后文件中使用只能使用别名,原名已经失效了
export 和 export default 可以一起使用
import add, {str} from '../../xxx.js'-
分别暴露:在要暴露的语句前面+export
-
统一暴露:在某个位置使用export{},将要暴露的数据放在花括号里面
在模块文件里,使用export default
export default {
...}这样就可以直接使用了
-
默认暴露:export.default = { },这种方法在调用时需要添加default
导入不需要知道命名(可以直接使用别名)
-
-
import 输入其他模块提供的功能
-
通用的导入方式:import * as m1 from "./src/js/m1.js";
导入的是全部
-
解构赋值的形式:
-
import{school,teach} from "./src/js/m1.js";
-
import{default as m1} from "./src/js/m1.js";
重名时需要使用别名,不然会报错
-
-
简便形式(针对默认暴露):improt m3 from "./src/js/m3.js"
-
-
-
使用babel
-
安装工具
npm i babel-cli babel-preset-env browerify -D -
编译:
npx babel src/js -d dist/js --presets=babel-preset-env先 [原文件目录] 后 [存放文件目录] -
打包 :
npx browserify dist/js/app.js -o dist/bundle.js将存放文件目录下的文件打包生成bundle.js文件
-
ES7
1. 数组拓展
-
Array.prototype.includes(searchElement[,fromIndex])
-
includes VS indexOf
- includes 返回布尔值,可以检测NaN
- indexOf 返回index / -1,不可以检测NaN
-
幂运算符:**
等同于Math.pow()
ES8
1. 异步编程解决方案Async Await
两者成对出现
代码可读性更强
function timeout() {
return new Promise(resolve => {
setTimeout(() => {
console.log(1)
resolve()
},1000)
})
}
async function foo() {
await timeout() //等待timeout()运行完毕后再继续往下运行
console.log(2)
}
foo()
之前的ajax请求代码:
async function getData() {
const res1 = await request('static/a.json')
console.log(res1)
const res2 = await request('static/b.json')
console.log(res2)
const res3 = await request('static/c.json')
console.log(res3)
}
2. 对象拓展
-
Object.values() 获得值
-
Object.entries() 获得数组(key和value)
const res = Object,keys(obj).map(key => obj[key]) console.log(res) //上面可以写成 console.log(Object.values(obj))console.log(Object.entries(['a','b','c'])) //["0","a"],["1","b"],["2","c"]
3. 对象属性描述
- Object.getOwnPropertyDescriptors()
- value 当前对象的默认值
- writable 是否可以修改
- enumerable 是否可以通过for..in方式循环
- configurable 是否可以删除
4. 字符串拓展
-
String.prototype.padStart() 头部补全
-
String.prototype.padEnd() 尾部补全
第一个参数为长度,第二个参数为用于补全的字符串
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
//等于或大于最大长度,则字符串补全不生效,返回原字符串
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
//应用于日期 yyyy-mm-dd
const now = new Date()
const year = now.getFullYear()
//padStart是String原型下面的方法,所以想要将其转换为String
//getMonth()返回的是0-11的数字,所以要加1
const month = (now.getMonth() + 1).toString().padStart(2,'0')
const day = (now.getDate()) + 1.toString().padStart(2,'0')
console.log(`${year}-${month}-${day}`)
//加密手机号
const tel = '13011111111'
const NewTel = tel.slice(-4).padStart(tel.length,'*')
console.log(NewTel)
5. 尾逗号
允许数参数列表使用尾逗号
ES9
1. 异步迭代for await of
- for-await-of
- Symbol.asyncIterator
//同步迭代
const arr = ['es6','es7','es8','es9']
arr[Symbol.iterator] = function() {
let nextIndex = 0
return {
next() {
return nextIndex < arr.length ? {
value: arr[nextIndex++],
done: false
} : {
value: undefined,
done: true
}
}
}
}
for(let item of arr) {
console.log(item)
}
//异步迭代
function getPromise(time) {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve({ //写成对象的形式
value: time,
done:false
})
},time)
})
}
const arr = [getPromise(1000),getPromise(2000),getPromise(3000)]
arr[Symbol.asyncIterator] = function() {
let nextIndex = 0
return {
next() {
return nextIndex < arr.length ? arr[nextIndex++] :
Promise.resolve({
value: undefined,
done: true
})
}
}
}
async function test() {
for await (let item of arr) {
console.log(item)
}
}
test()
2. 正则表达式拓展
-
dotAll
dot不能匹配\n \r(包括两者的Unicode)
const reg = /./s //匹配任意单个字符 console.log(reg.test('5')) //true console.log(reg.test('x')) //true console.log(reg.test('\n')) //true console.log(reg.test('\r')) //true console.log(reg.test('\u{2028}')) //true console.log(reg.test('\u{2029}')) //true -
具名组匹配
const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/; //用圆括号分组 const matchObj = RE_DATE.exec('1999-12-31'); const year = matchObj[1]; // 1999 const month = matchObj[2]; // 12 const day = matchObj[3]; // 31const reg = /(?<year>\d{4}-(?<month)\d{2}-(?<day>\d{2})) const groups = reg.exec('2020-02-01').groups //使用解构赋值 const {year, month,day} = groups console.log(year, month, day) -
后行断言 match
-
先行断言:
x只有在y前面才匹配,必须写成/x(?=y)/ -
先行否定断言:
x只有不在y前面才匹配,必须写成/x(?!y)/ -
后行断言:
x只有在y后面才匹配,必须写成/(?<=y)x/ -
后行否定断言:
x只有不在y后面才匹配,必须写成/(?<!y)x/
-
3. 对象拓展Rest&Spread
//克隆对象 为深拷贝
const obj3 = {..obj1}
//合并对象 为浅拷贝
const obj4 = {...obj1, ...obj2} //obj1和obj2相同键名的会被后者覆盖
//...rest 获取剩余的属性
const {name, age, ...rest} = obj1 //...rest 必须放在最后,不然会报错
4. Promise拓展finally()
-
Promise.prototype.finally()
无论失败还是成功都会执行finally里面的语句【例如:成功失败相同的代码逻辑、关闭操作】
5. 字符串扩展
放松模板字符串文字限制,对一些错误不报错,返回undefined
ES10
1. 对象扩展
-
Object.fromEntries() 返回对象结构 【和Object.Entries()相反(返回键对结构)】
// map => 对象 const map = new Map() map.set('name', 'n1') map.set('name', 'n2') console.log(map) const fromEntries = Object.fromEntries(map) console.log(map) //对象格式
2. 字符串扩展
- String.prototype.trimStart()【trimLeft()】 消除头部的空格,尾部会被保留
- String.prototype.trimEnd() 【trimRight()】消除尾部的空格,头部会被保留
- String.prototype.trim() 消除空格
3. 数组扩展
-
Array.prototype.flat(num) 对多维数组进行扁平化操作
const arr = [1,2,3,[4,5,6,[7,8,9,10,11],12]] //三维数组 console.log(arr.flat().flat().flat()) console.log(arr.flat(3)) console.log(arr.flat(Infinity)) -
Array.prototype.flatMap()
const arr = [1,2,3,4,5] //const res = arr.map(x => [x + 1]).flat() 等价于↓ const res = arr.flatMap(x => [x + 1])
4. 修订toString()
返回源代码中的实际文本片段【原样输出返回一模一样的原始代码,包括注释空格等等】
5. 可选的Catch Binding
省略catch绑定的参数和括号
try {
// ...
} catch {
// ...
}
6. JSON扩展
- JSON superset
- JSON.stringify() 增强能力
// JSON 超集 【少用】\u2029 \u2028
eval('var str = "youlan";\u2029 function foo(){return str;}')
console.log(foo())
//0xD800~0xDfff
console.log(JSON.stringify('\uD830\uDE0E')) //emoji
console.log(JSON.stringify('\uD830')) //\ud830 原样输出
7. Symbol扩展
- Symbol.prototype.description 只读属性,不可写【修改description也不会报错,但是不能起作用】
const s = Symbol('yl')
console.log(s) //Symbol(yl)
console.log(s.description) //yl 如果没有值则返回undefined
ES11
1. 全局模式捕获matchAll()
-
String.prototype.matchAll() 和正则一起使用
const str = ` <html> <body> <div>第一个div</div> <p>这是p</p> <div>第二个div</div> <span>这是span</span> <div>第三个div</div> </body> </html> ` //exec g function selectDiv1(regExp, str) { let matches = [] while(true) { const match = regExp.exec(str) if(match == null) { break } matches.push(match[1]) //完整匹配 } return matches } const regExp = /<div>(.*)</div>/g const res1 = selectDiv1(regExp, str) console.log(res1) //["第一个div","第二个div","第三个div"] //match //console.log(str.match(regExp)) //["<div>第一个div</div>","<div>第二个div</div>","<div>第三个div</div>"] //replace function selectDiv2(regExp, str) { let matches = [] str.replace(regExp, (all, first) => { matches.push(first) //完整匹配 }) return matches } const res2 = selectDiv2(regExp, str) console.log(res2) //["第一个div","第二个div","第三个div"] //matchAll function selectDiv3(regExp, st){ let matches = [] for(let match of str.matchAll(regExp)){ matches.push(match[1]) //完整匹配 } return matches } const res3 = selectDiv3(regExp, str) console.log(res3) //["第一个div","第二个div","第三个div"]matchAll方法的正则表达式需要有g(全局匹配)
2. 动态导入Dynamic import()
按需引入,使得页面渲染更快
懒加载
eg. 点击按钮才导入某个模块、才开始渲染这一部分的东西
3. 新的原始数据类型BigInt
console.log(1n == 1) //true
console.log(1n === 1) //false
//创建
const bigInt = BigInt(900719925474740993n)
bigInt.toSring()
4. Promise扩展allSettled()
- Promise.allSettled()
- allSettled() Vs all()
Promise.allSettled([
Promise.resolve({
code: 200,
data: [1, 2, 3]
}),
Promise.reject({
code: 500,
data: []
}),
Promise.resolve({
code: 200,
data: [7, 8, 9]
}),
]).then(res => {
//console.log(res,"成功")
const data = res.filter(item => item.status === "fulfilled")
console.log(data)
}).catch(err => {
console.log(err,"失败")
})
如果使用all(),则其中有一个reject都会导致整个进程进入“失败”;而allSettled(),成功的会返回status: "fulfilled" value:{...},失败的返回reson: {...},使用filter进行过滤获得请求成功的数据
5. 全局对象globalThis
提供一个标准的方式去获取不同环境下的全局对象
// node: global
// web: window self
const getGlobal = () => {
if(typeof selt !== 'undefined'){
return self
}
if(typeof window !== 'undefined'){
return window
}
if(typeof global !== 'undefined'){
return global
}
throw new Error("无法找到全局变量")
}
const global = getGlobal()
console.log(global)
//在es11中
//console.log(globalThis)
6. 可选链Optional chaining
先判断这个方法属性是否存在,如果存在再往下取
const street = user && user.address && user.address.street
console.log(street)
const num = user && user.address && user.address.getNum && user.address.getNum()
console.log(num)
//es11中,代码更加简洁
const street = user?.address?.street
console.log(street)
const num = user?.address?.getNum?.()
console.log(num)
?. 中间不能有空格
7. 空值合并运算符Nullish coalescing Operator
const b = null
const a = b ?? 6 //当b为undefined或null时,取默认值
console.log(a)
?? 中间不能有空格
ES12
作者:AntCredit 链接:juejin.cn/post/701137…)
features
- String.prototype.replaceAll()
- Promise.any
- WeakRef
- &&=, ||= and ??=
- Numeric separators
下面来一一看一下:
String.prototype.replaceAll()
在此之前只能使用正则替换,现在可以直接使用一个快捷方式;replaceAll。
//前
'jxvxscript'.replace(/x/g, 'a');
//后
// jxvxscript becomes javascript
'jxvxscript'.replaceAll('x', 'a');
复制代码
Promise.any
Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。 如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise
const promise1 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise2 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1, promise2];
Promise.any(promiseList).then(values=>{
console.log(values);
})
.catch(e=>{
console.log(e);
});
复制代码
WeakRefs
使用WeakRefs的Class类创建对对象的弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)
Logical Assignment Operators
包括这些运算符:&&=, ||= ,??= ;
a = 1;
b = 2;
a&&=b // a=2
/*
以上代码相当于
a && a = b
??=
作用相当于
if(a == null || a==undefined){
a=b
}
*/
复制代码
Numeric Separators —— 数字分隔符
数字增加分隔符,可以使用_分割数字,方便阅读较大的数字 对于跟数字打交道比较多的同学来说,可能会更加舒服
// previous syntax before ES12
const number = 92145723;
// new syntax coming with ES12
const number = 92_145_723;
console.log(number) // 92145723
//对国人来说可以这样,万,亿为单位
const number = 1_0000;
console.log(number) // 10000