面试官:来说一下ES6新特性? 15383字,最全ES6 | 您所在的位置:网站首页 › mathtrunc函数 › 面试官:来说一下ES6新特性? 15383字,最全ES6 |
先来张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 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. 新的原始数据类型Symbollet 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')) //trueString.startsWith() 判断是否在头部 const str = 'imooc' console.log(str.endsWith('mooc')) //trueString.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) //ylylylylylylylylylylString.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') // trueunicode 是否设置了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后面才匹配,必须写成/(? 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]; // 31ES2018引入了具名组匹配 /(?\d{4})-(?\d{2})-(?\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+(?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) // falseNumber.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') // trueNumber.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) // falseNumber.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()) // NaNMath.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)) // 0Math.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左移运算符( { 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 { 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]) //errorset 拦截对象属性的设置 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 !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') // trueReflect对象的方法与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目标函数YthisArgumenttarget函数调用时绑定的this对象NargumentsListtarget函数调用时传入的实参列表,该参数应该是一个类数组的对象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被运行的目标函数YargumentsList调用构造函数的数组或者伪数组YnewTarget该参数为构造函数, 参考 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目标对象YpropertyKey要定义或修改的属性的名称Yattributes要定义或修改的属性的描述Y const student = {} Reflect.defineProperty(student, 'name', { value: 'Mike' }) // true student.name // "Mike" Reflect.deleteProperty()Reflect.deleteProperty 允许你删除一个对象上的属性 返回一个 Boolean 值表示该属性是否被成功删除 Reflect.deleteProperty(target, propertyKey) 参数含义必选target删除属性的目标对象YpropertyKey将被删除的属性的名称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需要取值的目标对象YpropertyKey需要获取的值的键值Yreceiver如果遇到 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需要寻找属性的目标对象YpropertyKey获取自己的属性描述符的属性的名称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获取原型的目标对象YpropertyKey属性名,需要检查目标对象是否存在此属性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获取原型的目标对象YpropertyKey设置的属性的名称Yvalue设置的值Yreceiver如果遇到 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获取原型的目标对象Yprototype对象的新原型 (一个对象或 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'把庞大的代码拆开 将多个功能的代码按功能进行分开,以达到多个模块组合在一起形成一个功能复杂的功能 好处: 防止命名冲突 代码复用 高维护性语法
也可以使用 将引用部分放到另一个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]; // 31 const reg = /(?\d{4}-(? 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 = ` 第一个div这是p 第二个div 这是span 第三个div ` //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 = /(.*)/g const res1 = selectDiv1(regExp, str) console.log(res1) //["第一个div","第二个div","第三个div"] //match //console.log(str.match(regExp)) //["第一个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.anyPromise.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 |
CopyRight 2018-2019 实验室设备网 版权所有 |