阅读 102

面试官:来说一下ES6新特性? 15383字,最全ES6-ES12总结

先来张Vue的风暴图,感受一下抽象的席卷~

20b41a3010b649ff8131bc6b47b60ce8_tplv-k3u1fbpfcp-zoom-crop-mark_1304_1304_1304_734.png

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 复制代码

image-20210510212827201

对于引用类型,const不能改变其引用地址,但是可以改变堆内存中的值

const obj = {     name:'yl',     age:11 } //这里添加一行 Object.freeze(obj),后面的就无法改变(但只能冻结第一层,如果多层嵌套需要obj.name) obj.sex = 'G'  //obj中会添加这一值(堆内存可以改变,栈不能改变) 复制代码

  • 不属于顶层对象window

  • 不允许重复声明

  • 不存在变量提升

  • 暂时性死区

  • 块级作用域

区别:

varletconst
函数级作用域块级作用域块级作用域
变量提升不存在变量提升不存在变量提升
值可更改值可更改值不可更改

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});  //将解构赋值语句放在一个原括号里 复制代码

  • 数值和布尔值的解构赋值:

    会先转换为对象

    解构赋值的规则:

    只要等号右边的值不是对象或数组,就先转换为对象

    undefinednull 无法转为对象,故无法进行解构赋值

  • 函数的参数也可以使用解构赋值

用途:

  1. 交换变量的值

    let x = 1; let y = 2; [x, y] = [y, x]; 复制代码

  2. 从函数返回多个值

    // 返回一个数组 function example() {   return [1, 2, 3]; } let [a, b, c] = example(); // 返回一个对象 function example() {   return {     foo: 1,     bar: 2   }; } let { foo, bar } = example(); 复制代码

  3. 函数参数的定义

    // 参数是一组有次序的值 function f([x, y, z]) { ... } f([1, 2, 3]); // 参数是一组无次序的值 function f({x, y, z}) { ... } f({z: 3, y: 2, x: 1}); 复制代码

  4. 提取JSON数据

  5. 函数参数的默认值

  6. 遍历Map结构

  7. 输入模块的指定方法

    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() 即===

    image-20210413230838386

  • 拓展运算符 与 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:3,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 } 复制代码

11. 面向过程与面向对象

面向过程:强调实现需求的步骤

面向对象:对象的属性、方法

image-20210420160238158

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); 复制代码

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` )     } } 复制代码

  1. 类中的构造器不是必须写的,要写实例进行一些初始化的操作,如添加指定属性时才写

  2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的

  3. 类中所定义的方法,都是放在了类的原型对象上,供实例去使用


//传统方法 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 + ')';   } } 复制代码

  1. Object.assign()一次向类添加多个方法

class Point {   constructor(){     // ...   } } Object.assign(Point.prototype, {   toString(){},   toValue(){} }); 复制代码

  1. 类必须使用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'); 复制代码

  1. 取值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的应用程序,能够同时存储和处理世界上所有的字符,这消除了传统的国际化方法所面临的一些困难

  1. es5

"\u0061"    // "a" 复制代码

只限于码点在\u0000~\uFFFF之间的字符

超出须用两个双字节的形式表示

"\uD842\uDFB7"   // "????" 复制代码

  1. 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为替换的文本,也可以是函数,或是以下特殊字符串:

    在es5中使用replace()如果想要匹配所有,需要使用正则表达式

    • $&:匹配的子字符串。

    • $ `:匹配结果前面的文本。

    • $':匹配结果后面的文本。

    • $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' 复制代码

18. 正则表达式的拓展

正则表达式在线测试及常用正则表达式

正则表达手册

作用:检索、替换那些符合某个模式(规则)的文本

eg. 验证表单(匹配)、过滤页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)

1. RegExp构造函数

  1. 利用RegExp对象来创建

  2. 利用字面量创建

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')) 复制代码

image-20210421235558470 image-20210421235705899

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
thisArgumenttarget函数调用时绑定的this对象N
argumentsListtarget函数调用时传入的实参列表,该参数应该是一个类数组的对象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. 异步操作

image-20210430202144003

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. 状态

image-20210430210851701

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 }) 复制代码

image-20210430211605871

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 //     }) // } 复制代码

对应结果:

image-20210430224027207

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'


把庞大的代码拆开

将多个功能的代码按功能进行分开,以达到多个模块组合在一起形成一个功能复杂的功能

  1. 好处:

    • 防止命名冲突

    • 代码复用

    • 高维护性

  2. 语法

    <script type="module">  </script>

    也可以使用 <script src="./src/js/app.js" type="module"></script> 将引用部分放到另一个js文件里

    • 通用的导入方式:import * as m1 from "./src/js/m1.js";

      导入的是全部

    • 解构赋值的形式:

      重名时需要使用别名,不然会报错

    • 简便形式(针对默认暴露):improt m3 from "./src/js/m3.js"

    • import{school,teach} from "./src/js/m1.js";

    • import{default as m1} from "./src/js/m1.js";

    • 分别暴露:在要暴露的语句前面+export

    • 统一暴露:在某个位置使用export{},将要暴露的数据放在花括号里面

      在模块文件里,使用export default

      export default {

      ...

      }

      这样就可以直接使用了

    • 默认暴露:export.default = { },这种方法在调用时需要添加default

      导入不需要知道命名(可以直接使用别名)

    • export 对外接口

      导入的时候命名要完全一样,可以起别名,起了别名之后文件中使用只能使用别名,原名已经失效了

      export 和 export default 可以一起使用   import add, {str} from '../../xxx.js'

    • import 输入其他模块提供的功能

  3. 使用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]; // 31 复制代码

    const 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


作者:Koi是攻城狮呦
链接:https://juejin.cn/post/7012519052841074696


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐