前言

ES全称ECMAScript,ECMAScript是ECMA制订的标准化脚本语言。目前JavaScript应用的ECMAScript版本为ECMA-417。对于ECMA的最新资讯能够浏览 ECMA news查看。

2015年正式公布的ECMAScript6(2015)曾经成为了JavaScript这门语言的下一代规范, 随着ES2015的公布,规范委员会决定在每年都会公布一个ES的新版本。本文汇合了 ES6 至 ES11 罕用到的个性,包含还在布局的 ES12,只列举大略应用,应用新个性须要应用最新版的 bable 就行本义。

ECMAScript2015(ES6)

Let

应用let申明的变量:1.不属于顶层对象window, 2.不容许反复申明,3.不存在变量晋升,4.暂时性死区,5.块级作用域

1. let 申明的全局变量不是全局对象window的属性

let a = 5console.log(window.a) // undefined

2. 用let定义变量不容许反复申明

let a = 5let a = 6// VM131:1 Uncaught SyntaxError: Identifier 'a' has already been declared//   at <anonymous>:1:1

3. let申明的变量不存在变量晋升

function foo() {    console.log(a)    let a = 5}foo()// Uncaught ReferenceError: Cannot access 'a' before initialization

4. let申明的变量具备暂时性死区

var a = 5if (true) {    a = 6    let a}// Uncaught ReferenceError: Cannot access 'a' before initialization

下面代码中,存在全局变量 a ,然而块级作用域内 let 又申明了一个局部变量 a ,导致后者绑定这个块级作用域,所以在let申明变量前,对 a 赋值会报错。

有时“暂时性死区”比拟荫蔽,比方:

function foo(b = a, a = 2) {    console.log(a, b)}foo()// Uncaught ReferenceError: Cannot access 'a' before initialization

5. let 申明的变量领有块级作用域

let实际上为 JavaScript 新增了块级作用域

{    let a = 5}console.log(a) // undefined

Const

应用const申明的常量:1.不属于顶层对象window,2.不容许反复申明,3.不存在变量晋升,4.暂时性死区,5. 块级作用域

1.const 定义变量后,不能批改它了,对变量的批改会抛出异样。

const PI = 3.1415console.log(PI)PI = 5console.log(PI)// Uncaught TypeError: Assignment to constant variable.

2.const申明变量不能扭转,如果申明的是一个援用类型,则不能扭转它的内存地址,能够扭转它的内容。

const obj = {    name: 'wawa',    age: 34}obj.school = 'imooc'console.log(obj)// {name: "wawa", age: 34, school: "imooc"}obj = {name: 'xx'}// VM109:9 Uncaught TypeError: Assignment to constant variable.

3. const 申明的时候必须初始化值,否则会报错

const PIPI = 3.1415// Uncaught SyntaxError: Missing initializer in const declaration

解构赋值

在 ES6 中新增了变量赋值的形式:解构赋值。容许依照肯定模式,从数组和对象中提取值,对变量进行赋值。

1. 数组解构赋值

  • 赋值元素能够是任意可遍历的对象

    let [a, b, c] = "abc" // ["a", "b", "c"]let [one, two, three] = new Set([1, 2, 3])
  • 被赋值的变量还能够是对象的属性,不局限于单纯的变量。

    let user = {}[user.firstName, user.secondName] = 'Kobe Bryant'.split(' ')console.log(user.firstName, user.secondName) // Kobe Bryant
  • 解构赋值在循环体中的利用,能够配合 entries 应用。

    let user = {name: 'John',age: 30}// loop over keys-and-valuesfor (let [key, value] of Object.entries(user)) {console.log(`${key}:${value}`) // name:John, then age:30}
  • 能够跳过赋值元素,如果想疏忽数组的某个元素对变量进行赋值,能够应用逗号来解决。

    // second element is not neededlet [name, , title] = ['John', 'Jim', 'Sun', 'Moon']console.log( title ) // Sun
  • rest 参数

    let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]console.log(name1) // Juliusconsole.log(name2) // Caesar// Note that type of `rest` is Array.console.log(rest[0]) // Consulconsole.log(rest[1]) // of the Roman Republicconsole.log(rest.length) // 2
  • 如果数组的内容少于变量的个数,并不会报错,没有调配到内容的变量会是 undefined。

    let [firstName, surname] = []console.log(firstName) // undefinedconsole.log(surname) // undefined

    当然你也能够给变量赋予默认值,避免 undefined 的状况呈现:

    // default valueslet [name = "Guest", surname = "Anonymous"] = ["Julius"]console.log(name)    // Julius (from array)console.log(surname) // Anonymous (default used)

    2. 对象解构赋值

    解构赋值除了能够利用在 Array,也能够利用在 Object。根本的语法如下:let {var1, var2} = {var1:…, var2…}
    let options = {title: "Menu",width: 100,height: 200}let {title, width, height} = optionsconsole.log(title)  // Menuconsole.log(width)  // 100console.log(height) // 200
  • 赋值的过程中能够指定默认值的:

    let options = {title: "Menu"}let {width = 100, height = 200, title} = optionsconsole.log(title)  // Menuconsole.log(width)  // 100console.log(height) // 200
  • rest 运算符

    let options = {title: "Menu",height: 200,width: 100}let {title, ...rest} = options// now title="Menu", rest={height: 200, width: 100}console.log(rest.height)  // 200console.log(rest.width)   // 100
  • 嵌套对象

    如果一个 Array 或者 Object 比较复杂,它嵌套了 Array 或者 Object,那只有被赋值的构造和右侧赋值的元素统一就好了

    let options = {size: {  width: 100,  height: 200},items: ["Cake", "Donut"],extra: true    // something extra that we will not destruct}// destructuring assignment on multiple lines for claritylet {size: { // put size here  width,  height},items: [item1, item2], // assign items heretitle = 'Menu' // not present in the object (default value is used)} = optionsconsole.log(title)  // Menuconsole.log(width)  // 100console.log(height) // 200console.log(item1)  // Cakeconsole.log(item2)  // Donut

    3.字符串解构赋值

能够当做是数组的解构:

let str = 'imooc'let [a, b, c, d, e] = strconsole.log(a, b, c, d, e)

Array

在 ES6 中新增了很多实用的原生 API,不便开发者对 Array 的操控性更强,如 for...of、from、of、fill、find、findIndex等。

1. ES6 中数组遍历形式 for...of

for (let val of [1, 2, 3]) {    console.log(val);}// 1,2,3

for...of是反对 break、continue、return的,所以在性能上十分贴近原生的 for。

2. Array.from()将为数组转换为数组

let arrLike = {    0: 'a',    1: 'b',    2: 'c',    length: 3}let arr = Array.from(arrLike);// ["a", "b", "c"]

3.Array.of()

Array.of() 办法创立一个具备可变数量参数的新数组实例,而不思考参数的数量或类型。

Array.of() 和 Array 构造函数之间的区别在于解决整数参数:Array.of(7) 创立一个具备单个元素 7 的数组,而 Array(7) 创立一个长度为7的空数组(留神:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。

Array.of(7); // [7]Array.of(1, 2, 3); // [1, 2, 3]Array(7); // [ , , , , , , ]Array(1, 2, 3); // [1, 2, 3]

4.Array.prototype.fill()

fill() 办法用一个固定值填充一个数组中从起始索引到终止索引内的全副元素。不包含终止索引。

let array = [1, 2, 3, 4]array.fill(0, 1, 2)// [1,0,3,4]

5. Array.prototype.find()

find() 办法返回数组中满足提供的测试函数的第一个元素的值,否则返回 undefined。

let array = [5, 12, 8, 130, 44];let found = array.find(function(element) {    return element > 10;});console.log(found);// 12

6.Array.prototype.findIndex()

findIndex()办法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。其实这个和 find() 是成对的,不同的是它返回的是索引而不是值。

let array = [5, 12, 8, 130, 44];let found = array.find(function(element) {    return element > 10;});console.log(found);// 1

7. Array.prototype.copyWithin()

在以后数组外部,将指定地位的成员复制到其余地位(会笼罩原有成员),而后返回以后数组。也就是说,应用这个办法,会批改以后数组。

let arr = [1, 2, 3, 4, 5]console.log(arr.copyWithin(1, 3))// [1, 4, 5, 4, 5]

Function

1.默认参数

function foo(x, y = 'world') {    console.log(x, y)}foo('hello', 0)

2.Rest 参数

function sum(...nums) {    let num = 0    nums.forEach(function(item) {        num += item * 1    })    return num}console.log(sum(1, 2, 3)) // 6console.log(sum(1, 2, 3, 4)) // 10

3.扩大运算符

Spread Operator 和 Rest Parameter 是形似但相同意义的操作符,简略的来说 Rest Parameter 是把不定的参数“收敛”到数组,而 Spread Operator 是把固定的数组内容“打散”到对应的参数。示例如下:

function sum(x = 1, y = 2, z = 3) {    return x + y + z}console.log(sum(...[4])) // 9console.log(sum(...[4, 5])) // 12console.log(sum(...[4, 5, 6])) // 15

4.length属性

函数指定了默认值当前,函数的length属性,将返回没有指定默认值的参数个数。

function foo(x = 1, y = 2, z = 3) {    console.log(x, y)}console.log(foo.length)// 0

5.name属性

function foo() {}foo.name // "foo"

6.箭头函数

1、箭头函数中this指向定义时所在的对象,而不是调用时所在的对象,
2、箭头函数不能够当作构造函数,
3、箭头函数不能够应用arguments对象
  • 如果只有一个参数,能够省略括号,如果大于一个参数肯定要记得带括号.

    let hello = (name) => {  console.log('say hello', name)}// 或者let hello = name => {  console.log('say hello', name)}
  • 如果返回值是表达式,如果返回值是表达式能够省略 return 和 {}

    let pow = x => x * x
  • 如果返回值是字面量对象,肯定要用小括号包起来

    let person = (name) => ({    age: 20,    addr: 'Beijing City'})

    7.拓展

    let foo = {  name: 'es',  say: () => {      console.log(this.name, this)  }}console.log(foo.say()) // undefined

    因为箭头函数中对 this 的解决是定义时,this 的指向也就是 foo 外层的所指向的 window,而 window 没有 name 属性,所以后果是 undefined。

Object

1. 属性简洁表示法

let name = 'xx'  let age = 18  let obj = {      name,      age  }

2.属性名表达式

  let s = 'school'  let obj = {      foo: 'bar',      [s]: 'xx'  }

3.Object.is()

判断两个对象是否相等。

let obj1 = { // new Object()    name: 'xx',    age: 34}let obj2 = { // new Object()    name: 'xx',    age: 34}console.log(obj1 == obj2) // falseconsole.log(Object.is(obj1, obj2)) // falselet obj2 = obj1console.log(Object.is(obj1, obj2)) // true

4.Object.assign()

Object.assign() 办法用于将所有可枚举属性的值从一个或多个源对象复制到指标对象,它将返回指标对象。

const target = {    a: 1,    b: 2}const source = {    b: 4,    c: 5}const returnedTarget = Object.assign(target, source)console.log(target)// expected output: Object { a: 1, b: 4, c: 5 }console.log(returnedTarget)// expected output: Object { a: 1, b: 4, c: 5 }

Class

1. 申明类

class Animal {    constructor(type) {        this.type = type    }    walk() {        console.log( `I am walking` )    }}let dog = new Animal('dog')let monkey = new Animal('monkey')

2. Setters & Getters

对于类中的属性,能够间接在 constructor 中通过 this 间接定义,还能够间接在类的顶层来定义:

class Animal {    constructor(type, age) {        this.type = type        this._age = age    }    get age() {        return this._age    }    set age(val) {        this._age = val    }}

3. 静态方法

在 ES6 中应用 static 的标记是不是静态方法,代码如下:

class Animal {    constructor(type) {        this.type = type    }    walk() {        console.log( `I am walking` )    }    static eat() {        console.log( `I am eating` )    }}

4. 继承

class Animal {    constructor(type) {        this.type = type    }    walk() {        console.log( `I am walking` )    }    static eat() {        console.log( `I am eating` )    }}class Dog extends Animal {  constructor () {    super('dog')  }  run () {    console.log('I can run')  }}

Symbol

ES6 引入了一种新的原始数据类型 Symbol ,示意举世无双的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
1. 申明形式
let s = Symbol()typeof s// "symbol"

变量s就是一个举世无双的值。typeof的后果阐明s是 Symbol 数据类型。

既然是举世无双的,那么两个Symbol()就肯定是不相等的:

let s1 = Symbol()let s2 = Symbol()console.log(s1)console.log(s2)console.log(s1 === s2) // false

Symbol函数前不能应用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,因为 Symbol 值不是对象,所以不能增加属性。基本上,它是一种相似于字符串的数据类型。

2. Symbol.for()

Symbol.for() 承受一个字符串作为参数,而后搜寻有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。
let s1 = Symbol.for('foo')let s2 = Symbol.for('foo')console.log(s1 === s2) // true

Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被注销在全局环境中供搜寻,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先查看给定的key是否曾经存在,如果不存在才会新建一个值。

3. Symbol.keyFor()

Symbol.keyFor()办法返回一个已登记的 Symbol 类型值的key。

const s1 = Symbol('foo')console.log(Symbol.keyFor(s1)) // undefinedconst s2 = Symbol.for('foo')console.log(Symbol.keyFor(s2)) // foo

4.作为属性名

因为每一个 Symbol 值都是不相等的,这意味着 Symbol 值能够作为标识符,用于对象的属性名,就能保障不会呈现同名的属性。这对于一个对象由多个模块形成的状况十分有用,能避免某一个键被不小心改写或笼罩。

const stu1 = Symbol('李四')const stu2 = Symbol('李四')const grade = {    [stu1]: {        address: 'yyy',        tel: '222'    },    [stu2]: {        address: 'zzz',        tel: '333'    },}console.log(grade)console.log(grade[stu1])console.log(grade[stu2])

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) {    console.log(key)}for (let key of Object.keys(user)) {    console.log(key)}for (let key of Object.getOwnPropertySymbols(user)) {    console.log(key)}for (let key of Reflect.ownKeys(user)) {    console.log(key)}

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'))

下面代码中,字符串Triangle和Circle就是魔术字符串。它屡次呈现,与代码造成“强耦合”,不利于未来的批改和保护。

应用Symbol就能够很好的解决这个问题:

const shapeType = {    triangle: 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))

Set

在 JavaScript 里通常应用 Array 或 Object 来存储数据。然而在频繁操作数据的过程中查找或者统计并须要手动来实现,并不能简略的间接应用。 比方如何保障 Array 是去重的,如何统计 Object 的数据总数等,必须本人去手动实现相似的需要,不是很不便。 在 ES6 中为了解决上述痛点,新增了数据结构 Set 和 Map,它们别离对应传统数据结构的“汇合”和“字典”。
1.根本语法
  • 生成 Set 实例

    let s = new Set()

    能够定义一个空的 Set 实例,也能够在实例化的同时传入默认的数据。

     let s = new Set([1, 2, 3, 4])

    初始化的参数必须是可遍历的,能够是数组或者自定义遍历的数据结构。

  • 增加数据

    s.add('hello')s.add('goodbye')或者s.add('hello').add('goodbye')

    Set 数据结构不容许数据反复,所以增加反复的数据是有效的

  • 删除数据
    删除数据分两种,一种是删除指定的数据,一种是删除全副数据。

    // 删除指定数据s.delete('hello') // true// 删除全副数据s.clear()
  • 统计数据
    Set 能够疾速进行统计数据,如数据是否存在、数据的总数。

    // 判断是否蕴含数据项,返回 true 或 falses.has('hello') // true// 计算数据项总数s.size // 2
  • 数组去重

    let arr = [1, 2, 3, 4, 2, 3]let s = new Set(arr)console.log(s)
  • 合并去重

    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.遍历形式

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():应用回调函数遍历每个成员
  • for...of:能够间接遍历每个成员

     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])}

    3.WeakSet

    WeakSet 构造与 Set 相似,也是不反复的值的汇合。然而,它与 Set 有两个区别。WeakSet 的成员只能是对象,而不能是其余类型的值。
const ws = new WeakSet()ws.add(1)// TypeError: Invalid value used in weak setws.add(Symbol())// TypeError: invalid value used in weak set
let ws = new WeakSet()const obj1 = {    name: 'imooc'}const obj2 = {    age: 5}ws.add(obj1)ws.add(obj2)ws.delete(obj1)console.log(ws)console.log(ws.has(obj2))

WeakSet 没有size属性,没有方法遍历它的成员。

WeakSet 中的对象都是弱援用,即垃圾回收机制不思考 WeakSet 对该对象的援用,也就是说,如果其余对象都不再援用该对象,那么垃圾回收机制会主动回收该对象所占用的内存,不思考该对象还存在于 WeakSet 之中。

Map

ES6 提供了 Map 数据结构。它相似于对象,也是键值对的汇合,然而“键”的范畴不限于字符串,各种类型的值(包含对象)都能够当作键。也就是说,Object 构造提供了“字符串—值”的对应,Map 构造提供了“值—值”的对应,是一种更欠缺的 Hash 构造实现。如果你须要“键值对”的数据结构,Map 比 Object 更适合。
1.根本语法
  • 实例化

    let map = new Map([iterable])

    Iterable 能够是一个数组或者其余 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, 'one' ], [ 2, 'two' ]])。 每个键值对都会增加到新的 Map。null 会被当做 undefined。

  • 增加数据

    let keyObj = {}let keyFunc = function() {}let keyString = 'a string'// 增加键map.set(keyString, "和键'a string'关联的值")map.set(keyObj, '和键keyObj关联的值')map.set(keyFunc, '和键keyFunc关联的值')
  • 删除数据

    // 删除指定的数据map.delete(keyObj)// 删除所有数据map.clear()
  • 统计数据

    // 统计所有 key-value 的总数console.log(map.size) //2// 判断是否有 key-valueconsole.log(map.has(keyObj)) // true
  • 查问数据
    get() 办法返回某个 Map 对象中的一个指定元素

    console.log(map.get(keyObj)) // 和键keyObj关联的值

    2.遍历形式

  • keys() 返回一个新的 Iterator 对象。它蕴含依照程序插入 Map 对象中每个元素的 key 值
  • values() 办法返回一个新的 Iterator 对象。它蕴含按程序插入Map对象中每个元素的 value 值
  • entries() 办法返回一个新的蕴含 [key, value] 对的 Iterator ? 对象,返回的迭代器的迭代程序与 Map 对象的插入程序雷同
  • forEach() 办法将会以插入程序对 Map 对象中的每一个键值对执行一次参数中提供的回调函数
  • for...of 能够间接遍历每个成员

    map.forEach((value, key) => console.log(value, key))for (let [key, value] of map) { 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)}

    其实 Object 也是按键值对存储和读取的,那么他俩之间除了咱们之前说的区别以外还有其余的吗?

  • 键的类型

一个Object的键只能是字符串或者 Symbols,但一个 Map 的键能够是任意值,包含函数、对象、根本类型。

  • 键的程序

Map 中的键值是有序的,而增加到对象中的键则不是。因而,当对它进行遍历时,Map 对象是按插入的程序返回键值。

  • 键值对的统计

你能够通过 size 属性间接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。

  • 键值对的遍历

Map 可间接进行迭代,而 Object 的迭代须要先获取它的键数组,而后再进行迭代。

  • 性能

Map 在波及频繁增删键值对的场景下会有些性能劣势。

3.WeekMap
WeakMap构造与Map构造相似,也是用于生成键值对的汇合。

// 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与Map的区别有两点。

  • WeakMap只承受对象作为键名(null除外),不承受其余类型的值作为键名。

    const map = new WeakMap()map.set(1, 2)// TypeError: 1 is not an object!map.set(Symbol(), 2)// TypeError: Invalid value used as weak map keymap.set(null, 2)// TypeError: Invalid value used as weak map key
  • WeakMap的键名所指向的对象,不计入垃圾回收机制。

    String

    1.Unicode表示法

ES6 增强了对 Unicode 的反对,容许采纳\uxxxx模式示意一个字符,其中xxxx示意字符的 Unicode 码点。

"\u0061"// "a"

然而,这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范畴的字符,必须用两个双字节的模式示意。

"\uD842\uDFB7"// """\u20BB7"// " 7"

下面代码示意,如果间接在\u前面跟上超过0xFFFF的数值(比方\u20BB7),JavaScript 会了解成\u20BB+7。因为\u20BB是一个不可打印字符,所以只会显示一个空格,前面跟着一个7。

ES6 对这一点做出了改良,只有将码点放入大括号,就能正确解读该字符。

"\u{20BB7}"// ""

有了这种表示法之后,JavaScript 共有 6 种办法能够示意一个字符。

'\z' === 'z' // true'\172' === 'z' // true'\x7A' === 'z' // true'\u007A' === 'z' // true'\u{7A}' === 'z' // true

2.遍历器接口

ES6 为字符串增加了遍历器接口,详见Iterator一节,使得字符串能够被for...of循环遍历。

for (let item of 'imooc') {    console.log(item)}

3.模板字符串

  • String Literals
    这个是用来解决字符串拼接问题,从 ES6 开始能够这样定义字符串了。

    `string text` `string text line 1 string text line 2``string text ${expression} string text` 

    在这里你能够任意插入变量或者表达式,只有用 ${} 包起来就好。

  • Tag Literals
    后面的字符串字面量解决了字符串拼接的问题,对于蕴含简单逻辑的字符串并不是简略的表达式能搞定的。所以须要另一种解决方案:Tag Literals,例子:

    var retailPrice = 20var wholesalePrice = 16var type = 'retail'var showTxt = ''if (type === 'retail') {  showTxt += '您此次的购买单价是:' + retailPrice} else {  showTxt += '您此次的批发价是:' + wholesalePrice}

    当初能够定义一个 Tag 函数,而后用这个 Tag 函数来充当一个模板引擎:

    function Price(strings, type) {  let s1 = strings[0]  const retailPrice = 20  const wholesalePrice = 16  let txt = ''  if (type === 'retail') {      txt = `购买单价是:${retailPrice}`   } else {      txt = `批发价是:${wholesalePrice}`   }  return `${s1}${txt}` }let showTxt = Price `您此次的${'retail'}` console.log(showTxt) //您此次的购买单价是:20

    strings 参数指的是 Tag 函数前面被变量宰割开的字符串汇合,type 参数是对应第一个变量,Tag 函数能够有多个 type 相似的参数

4.扩大办法

  • String.prototype.fromCodePoint()
    用于从 Unicode 码点返回对应字符,并且能够辨认大于0xFFFF的字符。

    // ES5console.log(String.fromCharCode(0x20BB7))// // ES6console.log(String.fromCodePoint(0x20BB7))// 
  • String.prototype.includes()
    ES5中能够应用indexOf办法来判断一个字符串是否蕴含在另一个字符串中,indexOf返回呈现的下标地位,如果不存在则返回-1。

    const str = 'isxxx'console.log(str.indexOf('is'))

    ES6提供了includes办法来判断一个字符串是否蕴含在另一个字符串中,返回boolean类型的值。

    const str = 'isxxx'console.log(str.includes('xx'))
  • String.prototype.startsWith()
    判断参数字符串是否在原字符串的头部, 返回boolean类型的值。

    const str = 'isxxx'console.log(str.startsWith('is'))
  • String.prototype.endsWith()
    判断参数字符串是否在原字符串的尾部, 返回boolean类型的值。

    const str = 'isxxx'console.log(str.endsWith('xxx'))
  • String.prototype.repeat()
    repeat办法返回一个新字符串,示意将原字符串反复n次。

    const str = 'isxxx'const newStr = str.repeat(10)console.log(newStr)

    RegExp

    1.y修饰符
    ES6为正则表达式增加了y修饰符,叫做“粘连”(sticky)修饰符。

y修饰符的作用与g修饰符相似,也是全局匹配,后一次匹配都从上一次匹配胜利的下一个地位开始。不同之处在于,g修饰符只有残余地位中存在匹配就可,而y修饰符确保匹配必须从残余的第一个地位开始,这也就是“粘连”的涵义。

const s = 'aaa_aa_a'const r1 = /a+/gconst r2 = /a+/yr1.exec(s) // ["aaa"]r2.exec(s) // ["aaa"]r1.exec(s) // ["aa"]r2.exec(s) // null

下面代码有两个正则表达式,一个应用g修饰符,另一个应用y修饰符。这两个正则表达式各执行了两次,第一次执行的时候,两者行为雷同,残余字符串都是_aa_a。因为g润饰没有地位要求,所以第二次执行会返回后果,而y修饰符要求匹配必须从头部开始,所以返回null。

如果改一下正则表达式,保障每次都能头部匹配,y修饰符就会返回后果了。

const s = 'aaa_aa_a'const r = /a+_/yr.exec(s) // ["aaa_"]r.exec(s) // ["aa_"]

应用lastIndex属性,能够更好地阐明y修饰符。

const regexp = /a/g// 指定从2号地位(y)开始匹配regexp.lastIndex = 2// 匹配胜利const match = regexp.exec('xaya')// 在3号地位匹配胜利console.log(match.index) // 3// 下一次匹配从4号位开始console.log(regexp.lastIndex) // 4// 4号位开始匹配失败regexp.exec('xaxa') // null

下面代码中,lastIndex属性指定每次搜寻的开始地位,g修饰符从这个地位开始向后搜寻,直到发现匹配为止。

y修饰符同样恪守lastIndex属性,然而要求必须在lastIndex指定的地位发现匹配。

const regexp = /a/y// 指定从2号地位开始匹配regexp.lastIndex = 2// 不是粘连,匹配失败regexp.exec('xaya') // null// 指定从3号地位开始匹配regexp.lastIndex = 3// 3号地位是粘连,匹配胜利const match = regexp.exec('xaxa')console.log(match.index) // 3console.log(regexp.lastIndex) // 4

进一步说,y润饰符号隐含了头部匹配的标记^。

const reg = /b/yreg.exec('aba')// nullconsole.log(reg.lastIndex)

sticky 模式在正则匹配过程中只会影响两件事:

  • 匹配必须从 re.lastIndex 开始(相当于正则表白中的 ^)
  • 如果匹配到会批改 re.lastIndex(相当于 g 模式)
    2.u修饰符
    ES6为正则表达式增加了u修饰符,含意为“Unicode模式”,用来正确处理大于 \uFFFF 的Unicode字符。也就是说,会正确处理四个字节的UTF-16编码。

    /^\uD83D/u.test('\uD83D\uDC2A') // false/^\uD83D/.test('\uD83D\uDC2A') // true

    下面代码中, \uD83D\uDC2A 是一个四个字节的UTF-16编码,代表一个字符 ""。然而,ES5不反对四个字节的UTF-16编码,会将其辨认为两个字符,导致第二行代码后果为true。加了u修饰符当前,ES6就会辨认其为一个字符,所以第一行代码后果为false。

一旦加上u润饰符号,就会批改上面这些正则表达式的行为。

  • 点字符
    点(.)字符在正则表达式中,含意是除了换行符以外的任意单个字符。对于码点大于 0xFFFF 的 Unicode 字符,点字符不能辨认,必须加上u修饰符。

    let s = ''/^.$/.test(s) // false/^.$/u.test(s) // true
  • Unicode字符表示法
    ES6新增了应用大括号示意Unicode字符,这种表示法在正则表达式中必须加上u修饰符,能力辨认。

    /\u{61}/.test('a') // false/\u{61}/u.test('a') // true/\u{20BB7}/u.test('') // true
  • 量词
    应用u修饰符后,所有量词都会正确识别码点大于0xFFFF的Unicode字符。

    /a{2}/.test('aa') // true/a{2}/u.test('aa') // true/{2}/.test('') // false/{2}/u.test('') // true
  • 预约义模式
    u修饰符也影响到预约义模式,是否正确识别码点大于0xFFFF的Unicode字符。

    /^\S$/.test('') // false/^\S$/u.test('') // true

    下面代码的\S是预约义模式,匹配所有不是空格的字符。只有加了u修饰符,它能力正确匹配码点大于0xFFFF的Unicode字符。

利用这一点,能够写出一个正确返回字符串长度的函数。

function codePointLength(text) {    const result = text.match(/[\s\S]/gu)    return result ? result.length : 0}const s = ''s.length // 4codePointLength(s) // 2
  • i修饰符
    有些Unicode字符的编码不同,然而字型很相近,比方,\u004B与\u212A都是大写的K。

    /[a-z]/i.test('\u212A') // false/[a-z]/iu.test('\u212A') // true

    下面代码中,不加u修饰符,就无奈辨认非标准的K字符。

    Number

    1.二进制与八进制

  • JS中如何把十进制转化为二进制?

    const a = 5 // 101console.log(a.toString(2))
  • 如何把八进制转化为二进制?

    const b = 101console.log(parseInt(b, 2))

    ES6 提供了二进制和八进制数值的新的写法,别离用前缀0b(或0B)和0o(或0O)示意。

    const a = 0B0101console.log(a)const b = 0O777console.log(b)

    2.新增办法

  • Number.isFinite()
    用来查看一个数值是否为无限的(finite),即不是Infinity。

    Number.isFinite(15) // trueNumber.isFinite(0.8) // trueNumber.isFinite(NaN) // falseNumber.isFinite(Infinity) // falseNumber.isFinite(-Infinity) // falseNumber.isFinite('foo') // falseNumber.isFinite('15') // falseNumber.isFinite(true) // false
  • Number.isNaN()
    用来查看一个值是否为NaN。

    Number.isNaN(NaN) // trueNumber.isNaN(15) // falseNumber.isNaN('15') // falseNumber.isNaN(true) // falseNumber.isNaN(9 / NaN) // trueNumber.isNaN('true' / 0) // trueNumber.isNaN('true' / 'true') // true
  • Number.parseInt()
    ES6 将全局办法parseInt()移植到Number对象下面,行为齐全放弃不变。 这样做的目标,是逐渐缩小全局性办法,使得语言逐渐模块化。

    // ES5的写法parseInt('12.34') // 12// ES6的写法Number.parseInt('12.34') // 12
  • Number.parseFloat()
    ES6 将全局办法parseFloat()移植到Number对象下面,行为齐全放弃不变。这样做的目标,是逐渐缩小全局性办法,使得语言逐渐模块化。

    // ES5的写法parseFloat('123.45#') // 123.45// ES6的写法Number.parseFloat('123.45#') // 123.45
  • Number.isInteger()
    用来判断一个数值是否为整数。

    Number.isInteger(25) // trueNumber.isInteger(25.1) // falseNumber.isInteger() // falseNumber.isInteger(null) // falseNumber.isInteger('15') // falseNumber.isInteger(true) // false
  • Number.MAX_SAFE_INTEGER

    Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 // trueNumber.MAX_SAFE_INTEGER === 9007199254740991 // true
  • Number.MIN_SAFE_INTEGER

    Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER // trueNumber.MIN_SAFE_INTEGER === -9007199254740991 // true
  • Number.isSafeInteger()
    JavaScript 可能精确示意的整数范畴在-2^53到2^53之间(不含两个端点),超过这个范畴,无奈准确示意这个值。

    Math.pow(2, 53) // 9007199254740992Math.pow(2, 53) === Math.pow(2, 53) + 1 // true

    3.Math扩大

  • Math.trunc()
    办法用于去除一个数的小数局部,返回整数局部。

     console.log(Math.trunc(5.5)) // 5console.log(Math.trunc(-5.5)) // -5console.log(Math.trunc(true)) // 1console.log(Math.trunc(false)) // 0console.log(Math.trunc(NaN)) // NaNconsole.log(Math.trunc(undefined)) // NaNconsole.log(Math.trunc()) // NaN
  • Math.sign()
    办法用来判断一个数到底是负数、正数、还是零。对于非数值,会先将其转换为数值。

它会返回五种值。

  • 参数为负数,返回+1
  • 参数为正数,返回-1
  • 参数为 0,返回0
  • 参数为-0,返回-0
  • 其余值,返回NaN

    console.log(Math.sign(5)) // 1console.log(Math.sign(-5)) // -1console.log(Math.sign(0)) // 0console.log(Math.sign(NaN)) // NaNconsole.log(Math.sign(true)) // 1console.log(Math.sign(false)) // 0
  • Math.cbrt()
    办法用于计算一个数的立方根。

    console.log(Math.cbrt(8)) // 2console.log(Math.cbrt('xx')) // NaN

    Proxy

    在 ES6 规范中新增的一个十分弱小的性能是 Proxy,它能够自定义一些罕用行为如查找、赋值、枚举、函数调用等。通过 Proxy 这个名称也能够看进去它蕴含了“代理”的含意,只有有“代理”的诉求都能够思考应用 Proxy 来实现。

1.根本语法

let p = new Proxy(target, handler)
参数含意必选
target用 Proxy 包装的指标对象(能够是任何类型的对象,包含原生数组,函数,甚至另一个代理)y
handler一个对象,其属性是当执行一个操作时定义代理的行为的函数y

2.罕用拦挡操作

  • get
    拦挡对象属性的读取,比方proxy.foo和proxy['foo']。

    let arr = [7, 8, 9]arr = new Proxy(arr, {  get(target, prop) {      // console.log(target, prop)      return prop in target ? target[prop] : 'error'  }})console.log(arr[1])console.log(arr[10])
    let dict = {  'hello': '你好',  'world': '世界'}dict = new Proxy(dict, {  get(target, prop) {      return prop in target ? target[prop] : prop  }})console.log(dict['world'])console.log(dict['imooc'])
  • set
    拦挡对象属性的设置,比方proxy.foo = v或proxy['foo'] = v,返回一个布尔值。

    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 userinfo = {  username: 'xxx',  age: 18,  _password: '***'}userinfo = new Proxy(userinfo, {  ownKeys(target) {      return Object.keys(target).filter(key => !key.startsWith('_'))  }})// for (let key in userinfo) {//     console.log(key)// }console.log(Object.keys(userinfo))
  • deleteProperty
    拦挡delete proxy[propKey]的操作,返回一个布尔值。

    let user = {  name: 'xxx',  age: 18,  _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 = 18console.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'))

    Reflect

    Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。

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不是一个函数对象,因而它是不可结构的。

2、罕用办法

  • Reflect.apply()

    Reflect.apply(target, thisArgument, argumentsList),target为指标函数;thisArgument为target函数调用时绑定的this对象;argumentsList为target函数调用时传入的实参列表,该参数应该是一个类数组的对象
Reflect.apply(Math.floor, undefined, [1.75])// 1Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111])// "hello"Reflect.apply(RegExp.prototype.exec, /ab/, ['confabulation']).index// 4Reflect.apply(''.charAt, 'ponies', [3])// "i"
  • Reflect.construct()
    Reflect.construct() 办法的行为有点像 new 操作符 构造函数 , 相当于运行 new target(...args)

    var d = Reflect.construct(Date, [1776, 6, 4])d instanceof Date // trued.getFullYear() // 1776
  • Reflect.defineProperty()
    静态方法 Reflect.defineProperty() 根本等同于 Object.defineProperty() 办法,惟一不同是返回 Boolean 值。

    const student = {}Reflect.defineProperty(student, 'name', {  value: 'Mike'}) // truestudent.name // "Mike"
  • Reflect.deleteProperty()
    Reflect.deleteProperty 容许你删除一个对象上的属性。返回一个 Boolean 值示意该属性是否被胜利删除。它简直与非严格的 delete operator 雷同。

    var obj = {  x: 1,  y: 2}Reflect.deleteProperty(obj, "x") // trueobj // { y: 2 }var arr = [1, 2, 3, 4, 5]Reflect.deleteProperty(arr, "3") // truearr // [1, 2, 3, , 5]// 如果属性不存在,返回 trueReflect.deleteProperty({}, "foo") // true// 如果属性不可配置,返回 falseReflect.deleteProperty(Object.freeze({  foo: 1}), "foo") // false
  • Reflect.get()
    Reflect.get() 办法的工作形式,就像从 object (target[propertyKey]) 中获取属性,但它是作为一个函数执行的。

    // Objectvar obj = {  x: 1,  y: 2}Reflect.get(obj, 'x') // 1// ArrayReflect.get(['zero', 'one'], 1) // "one"// Proxy with a get handlervar x = {  p: 1}var obj = new Proxy(x, {  get(t, k, r) {      return k + 'bar'  }})Reflect.get(obj, 'foo') // "foobar"
  • Reflect.getOwnPropertyDescriptor()
    静态方法 Reflect.getOwnPropertyDescriptor() 与 Object.getOwnPropertyDescriptor() 办法类似。如果在对象中存在,则返回给定的属性的属性描述符,否则返回 undefined。

    Reflect.getOwnPropertyDescriptor({  x: 'hello'}, 'x')// {value: "hello", writable: true, enumerable: true, configurable: true}Reflect.getOwnPropertyDescriptor({  x: 'hello'}, 'y')// undefinedReflect.getOwnPropertyDescriptor([], 'length')// {value: 0, writable: true, enumerable: false, configurable: false}
  • 更多办法能够参考Reflect

Promise

1、根本语法

Promise 就是为了解决“回调天堂”问题的,它能够将异步操作的解决变得很优雅。回调天堂,代码难以保护, 经常第一个的函数的输入是第二个函数的输出这种景象promise能够反对多个并发的申请,获取并发申请中的数据这个promise能够解决异步的问题,自身不能说promise是异步的。

创立Promise实例。

const promise = new Promise(function(resolve, reject) {    // ... some code    if ( /* 异步操作胜利 */ ) {        resolve(value)    } else {        reject(error)    }})

Promise构造函数承受一个函数作为参数,该函数的两个参数别离是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不必本人部署。

  • 处理结果失常的话,调用resolve(处理结果值),将Promise对象的状态从“未实现”变为“胜利”(即从 pending 变为 resolved),在异步操作胜利时调用,并将异步操作的后果,作为参数传递进来
  • 处理结果谬误的话,调用reject(Error对象),将Promise对象的状态从“未实现”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的谬误,作为参数传递进来。
    Promise实例生成当前,能够用then办法别离指定resolved状态和rejected状态的回调函数。

    promise.then(function(value) {  // success}, function(error) {  // failure})

    Promise 外部是有状态的(pending、fulfilled、rejected),Promise 对象依据状态来确定执行哪个办法。Promise 在实例化的时候状态是默认 pending 的,当异步操作是实现的,状态会被批改为 fulfilled,如果异步操作遇到异样,状态会被批改为 rejected。

2、Promise.prototype.then()

var promise = new Promise(function(resolve, reject) {    resolve('传递给then的值')})promise.then(function(value) {    console.log(value)}, function(error) {    console.error(error)})
  • 当 handler 返回一个正常值的时候,这个值会传递给 Promise 对象的 onFulfilled 办法。
  • 定义的 handler 中产生异样的时候,这个值则会传递给 Promise 对象的 onRejected 办法。

3、Promise.prototype.catch()

捕捉异样是程序品质保障最根本的要求,能够应用 Promise 对象的 catch 办法来捕捉异步操作过程中呈现的任何异样

function test() {    return new Promise((resolve, reject) => {        reject(new Error('es'))    })}test().catch((e) => {    console.log(e.message) // es})

4、Promise.resolve()

个别状况下咱们都会应用 new Promise() 来创立 Promise 对象,然而除此之外咱们也能够应用其余办法。

在这里,咱们将会学习如何应用 Promise.resolve 和 Promise.reject 这两个办法。

静态方法 Promise.resolve(value) 能够认为是 new Promise() 办法的快捷方式。

比方 Promise.resolve(42) 能够认为是以下代码的语法糖。

new Promise(function(resolve) {    resolve(42)})

办法 Promise.resolve(value) 的返回值也是一个 Promise 对象,所以咱们能够像上面那样接着对其返回值进行 .then 调用。

Promise.resolve(42).then(function(value) {    console.log(value)})

5、Promise.reject()

Promise.reject(error) 是和 Promise.resolve(value) 相似的静态方法,是 new Promise() 办法的快捷方式。

比方 Promise.reject(new Error("出错了")) 就是上面代码的语法糖模式。

new Promise(function(resolve, reject) {    reject(new Error('出错了'))})

这段代码的性能是调用该Promise 对象通过then指定的 onRejected 函数,并将谬误(Error)对象传递给这个 onRejected 函数。

Promise.reject(new Error('BOOM!'))

6、Promise.all()

var p1 = Promise.resolve(1)var p2 = Promise.resolve(2)var p3 = Promise.resolve(3)Promise.all([p1, p2, p3]).then(function(results) {    console.log(results) // [1, 2, 3]})

Promise.all 生成并返回一个新的 Promise 对象,所以它能够应用 Promise 实例的所有办法。参数传递promise数组中所有的 Promise 对象都变为resolve的时候,该办法才会返回, 新创建的 Promise 则会应用这些 promise 的值。

如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立刻终止,并返回一个reject的新的 Promise 对象。

因为参数数组中的每个元素都是由 Promise.resolve 包装(wrap)的,所以Promise.all 能够解决不同类型的 Promise 对象。

7、Promise.race()

var p1 = Promise.resolve(1)var p2 = Promise.resolve(2)var p3 = Promise.resolve(3)Promise.race([p1, p2, p3]).then(function(value) {    console.log(value) // 1})

Promise.race 生成并返回一个新的 Promise 对象。

参数 promise 数组中的任何一个 Promise 对象如果变为 resolve 或者 reject 的话, 该函数就会返回,并应用这个 Promise 对象的值进行 resolve 或者 reject。

Generator

Generators 是能够用来管制迭代器的函数。它们能够暂停,而后在任何时候复原。如果这句话不好了解,能够看下接下来的示例。

  1. 惯例循环

    for (let i = 0; i < 5; i += 1) { console.log(i)}// this will return immediately 0 -> 1 -> 2 -> 3 -> 4
  2. 利用 Generator

    function* generatorForLoop() { for (let i = 0; i < 5; i += 1) {     yield console.log(i) }}const genForLoop = generatorForLoop()console.log(genForLoop.next()) // first console.log - 0console.log(genForLoop.next()) // 1console.log(genForLoop.next()) // 2console.log(genForLoop.next()) // 3console.log(genForLoop.next()) // 4

    比照下代码,惯例的循环只能一次遍历完所有值,Generator 能够通过调用 next 办法拿到顺次遍历的值,让遍历的执行变得“可控”。

1、根本语法

function* gen() {    yield 1    yield 2    yield 3}let g = gen()// "Generator { }"

这个是 Generator 的定义方法,有几个点值得注意:

  • 比一般函数多一个 *
  • 函数外部用 yield 来控制程序的执行的“暂停”
  • 函数的返回值通过调用 next 来“复原”程序执行

Generator 函数的定义不能应用箭头函数,否则会触发 SyntaxError 谬误

let generator = * () => {} // SyntaxErrorlet generator = () * => {} // SyntaxErrorlet generator = ( * ) => {} // SyntaxError

2、yield 表达式
yield 关键字用来暂停和复原一个生成器函数

  • yield 表达式的返回值是 undefined,然而遍历器对象的 next 办法能够批改这个默认值。
  • Generator 对象的 next 办法,遇到 yield 就暂停,并返回一个对象,这个对象包含两个属性:value 和 done。

     function* gen() {    let val    val = yield 1    console.log( `1:${val}` ) // 1:undefined    val = yield 2    console.log( `2:${val}` ) // 2:undefined    val = yield 3    console.log( `3:${val}` ) // 3:undefined}var g = gen()console.log(g.next()) // {value: 1, done: false}console.log(g.next()) // {value: 2, done: false}console.log(g.next()) // {value: 3, done: false}console.log(g.next()) // {value: undefined, done: true}

    3、办法

Generator 对象有几个办法,next、return、throw。

  • next([value])
    Generator 对象通过 next 办法来获取每一次遍历的后果,这个办法返回一个对象,这个对象蕴含两个属性:value 和 done。value 是指以后程序的运行后果,done 示意遍历是否完结。

其实 next 是能够承受参数的,这个参数能够让你在 Generator 内部给外部传递数据,而这个参数就是作为 yield 的返回值。

function* gen() {      var val = 100      while (true) {          console.log( `before ${val}` )          val = yield val          console.log( `return ${val}` )      }  }  var g = gen()  console.log(g.next(20).value)  // before 100  // 100  console.log(g.next(30).value)  // return 30  // before 30  // 30  console.log(g.next(40).value)  // return 40  // before 40  // 40
  • return()
    return 办法能够让 Generator 遍历终止,有点相似 for 循环的 break。

    function* gen() {yield 1yield 2yield 3}var g = gen()console.log(g.next()) // {value: 1, done: false}console.log(g.return()) // {value: undefined, done: true}console.log(g.next()) // {value: undefined, done: true}
  • throw()
    能够通过 throw 办法在 Generator 内部管制外部执行的“终断”。

    function* gen() {  while (true) {      try {          yield 42      } catch (e) {          console.log(e.message)      }  }}let g = gen()console.log(g.next()) // { value: 42, done: false }console.log(g.next()) // { value: 42, done: false }console.log(g.next()) // { value: 42, done: false }// 中断操作g.throw(new Error('break'))console.log(g.next()) // {value: undefined, done: true}

    Iterator

    解决汇合中的每个项是很常见的操作。JavaScript 提供了许多迭代汇合的办法,从简略的for循环到map()和filter()。迭代器和生成器将迭代的概念间接带入外围语言,并提供了一种机制来自定义for...of循环的行为。

如果对 MDN 这个形容了解不是很到位的话,能够看下接下来这个小示例:

let authors = {    allAuthors: {        fiction: [            'Agatha Christie',            'J. K. Rowling',            'Dr. Seuss'        ],        scienceFiction: [            'Neal Stephenson',            'Arthur Clarke',            'Isaac Asimov',            'Robert Heinlein'        ],        fantasy: [            'J. R. R. Tolkien',            'J. K. Rowling',            'Terry Pratchett'        ]    }}

这个数据结构是汇总了所有作者,每个作者按创作性质进行了分类。如果咱们想获取所有作者的名单,该怎么做呢?

for (let author of authors) {    console.log(author)}

你发现这个遍历遇到了报错:Uncaught TypeError: authors is not iterable

1、根本语法

Iterator 就是 ES6 中用来实现自定义遍历的接口,依照上述的示例,咱们来实现下这个接口:

authors[Symbol.iterator] = function() {    let allAuthors = this.allAuthors    let keys = Reflect.ownKeys(allAuthors)    let values = []    return {        next() {            if (!values.length) {                if (keys.length) {                    values = allAuthors[keys[0]]                    keys.shift()                }            }            return {                done: !values.length,                value: values.shift()            }        }    }}

这个代码在数据结构上部署了 Iterator 接口,咱们就能够用 for...of 来遍历代码了:

for (let value of authors) {    console.log( `${value}` )}

2、可迭代协定和迭代器协定。

    1. 迭代器协定

这是两个概念:可迭代协定、迭代器协定。艰深的讲,迭代器协定要求合乎以下条件:

  • 首先,它是一个对象
  • 其次,这个对象蕴含一个无参函数 next
  • 最初,next 返回一个对象,对象蕴含 done 和 value 属性。其中 done 示意遍历是否完结,value 返回以后遍历的值。
    1. 可迭代协定
      可迭代协定容许 JavaScript 对象去定义或定制它们的迭代行为, 例如(定义)在一个 for..of 构造中什么值能够被循环(失去)。一些内置类型都是内置的可迭代类型并且有默认的迭代行为, 比方 Array or Map, 另一些类型则不是 (比方Object) 。

为了变成可迭代对象, 一个对象必须实现 @@iterator 办法, 意思是这个对象(或者它原型链 prototype chain 上的某个对象)必须有一个名字是 Symbol.iterator 的属性。

如果让一个对象是可遍历的,就要恪守可迭代协定,该协定要求对象要部署一个以 Symbol.iterator 为 key 的键值对,而 value 就是一个无参函数,这个函数返回的对象要恪守迭代器协定。

3、Generator

相熟了 Generator 之后,发现它是人造满足可迭代协定的。上述的代码咱们能够用 Generator 来实现:

authors[Symbol.iterator] = function*() {    let allAuthors = this.allAuthors    let keys = Reflect.ownKeys(allAuthors)    let values = []    while (1) {        if (!values.length) {            if (keys.length) {                values = allAuthors[keys[0]]                keys.shift()                yield values.shift()            } else {                return false            }        } else {            yield values.shift()        }    }}

同一个场景,同一个数据结构,写法的确不同的,利用 Generator 就不再须要显示的写迭代协定了(next办法和蕴含 done、value 属性的返回对象)。

Module

1、模块化的倒退状况

无模块化-->CommonJS标准-->AMD标准-->CMD标准-->ES6模块化

1、CommonJS标准 Node中模块化标准

Commonjs的诞生给js模块化倒退有了重要的启发,Commonjs十分受欢迎, 然而局限性很显著:Commonjs基于Node原生api在服务端能够实现模块同步加载, 然而仅仅局限于服务端,客户端如果同步加载依赖的话工夫耗费十分大,所以须要一个 在客户端上基于Commonjs然而对于加载模块做改良的计划,于是AMD标准诞生了。

2、AMD标准, 异步模块定义, 容许指定回调函数,AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。它采纳异步形式加载模块,模块的加载不影响它前面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到所有依赖加载实现之后(前置依赖),这个回调函数才会运行。

3、CMD标准,同样是受到Commonjs的启发,国内(阿里)诞生了一个CMD(Common Module Definition)标准。该标准借鉴了Commonjs的标准与AMD标准,在两者根底上做了改良。

CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。

AMD 推崇依赖前置、提前执行 CMD推崇依赖就近、提早执行。

4、 到了2015年,ES6标准中,终于将模块化纳入JavaScript规范,从此js模块化被官网扶正,也是将来js的规范. 在ES6中,咱们能够应用 import 关键字引入模块,通过 exprot 关键字导出模块,性能较之于前几个计划更为弱小,也是咱们所推崇的, 然而因为ES6目前无奈在浏览器中执行,所以,咱们只能通过babel将不被反对的import编译为以后受到广泛支持的 require。

2、export

模块性能次要由两个命令形成:export和import。export命令用于规定模块的对外接口,import命令用于输出其余模块提供的性能。

一个模块就是一个独立的文件。该文件外部的所有变量,内部无奈获取。如果你心愿内部可能读取模块外部的某个变量,就必须应用export关键字输入该变量。

  • 导出变量或者常量

    export const name = 'hello'export let addr = 'BeiJing City'export var list = [1, 2, 3]

    或者

    const name = 'hello'let addr = 'BeiJing City'var list = [1, 2, 3]export {name,addr,list}
  • 导出函数

    export function say(content) {console.log(content)}export function run() {console.log('run')}

    或者

    const say = (content) => {console.log(content)}let run = () => {console.log('run')}export {say,run}
  • 导出 Object

    export ({code: 0,message: 'success'})

    或者

    let data = {code: 0,message: 'success'}export {data}
  • 导出 Class

    class Test {constructor() {    this.id = 2}}export {Test}

    或者

    export class Test {constructor() {    this.id = 2}}

    3、as

如果想为输出的变量从新取一个名字,import命令要应用as关键字,将输出的变量重命名。

  const name = 'hello'  let addr = 'BeiJing City'  var list = [1, 2, 3]  export {      name as cname,      addr as caddr,      list  }

4、export default

应用import命令的时候,用户须要晓得所要加载的变量名或函数名,否则无奈加载。然而,用户必定心愿疾速上手,未必违心浏览文档,去理解模块有哪些属性和办法。

为了给用户提供方便,让他们不必浏览文档就能加载模块,就要用到export default命令,为模块指定默认输入。

const name = 'hello'let addr = 'BeiJing City'var list = [1, 2, 3]export {  name as cname,  addr as caddr}export default list

5、import

应用export命令定义了模块的对外接口当前,其余 JS 文件就能够通过import命令加载这个模块。

  • 间接导入
    假如导出模块 A 是这样的:

    const name = 'hello'let addr = 'BeiJing City'var list = [1, 2, 3]export {name as cname,addr as caddr}export default list

    则导入:

    import list, {cname,caddr} from A
  • 批改导入名称

    import list, {cname as name,caddr} from A
  • 批量导入

    import list, * as mod from Aconsole.log(list)console.log(mod.cname)console.log(mod.caddr)

    ECMAScript2016(ES7)

    Array.prototype.includes()

    ES7引入的Array.prototype.includes() 办法用来判断一个数组是否蕴含一个指定的值,依据状况,如果蕴含则返回 true,否则返回false。

  • 根本用法

    const arr = ['es6', 'es7', 'es8']console.log(arr.includes('es6')) // trueconsole.log(arr.includes('es9')) // false
  • 接管俩个参数
    要搜寻的值和搜寻的开始索引。第二个参数可选。从该索引处开始查找 searchElement。如果为负值,

    const arr = ['es6', 'es7', 'es8']console.log(arr.includes('es7', 1)) // trueconsole.log(arr.includes('es7', 2)) // falseconsole.log(arr.includes('es7', -1)) // falseconsole.log(arr.includes('es7', -2)) // true
  • 与indexOf()比拟

    ['a', 'b', 'c'].includes('a') //true['a', 'b', 'c'].indexOf('a') > -1 //trueconsole.log(arr.indexOf('es7')) // 1console.log(arr.indexOf('es7') > -1) // true

    如果只想晓得某个值是否在数组中存在,而并不关怀它的索引地位,倡议应用includes()。如果想获取一个值在数组中的地位,那么只能应用indexOf办法。

幂运算符**

求幂运算

console.log(2 ** 10) // 1024

ECMAScript2017(ES8)

async / await

async 和 await 是一种更加优雅的异步编程解决方案,是Promise 的拓展。

  • 根本语法
    后面增加了async的函数在执行后都会主动返回一个Promise对象:

    async function foo() {  return 'xxx' // Promise.resolve('xxx')  // let res =  Promise.resolve('xxx')  // console.log(res)}console.log(foo()) // Promisefoo()

    await前面须要跟异步操作,不然就没有意义,而且await前面的Promise对象不用写then,因为await的作用之一就是获取前面Promise对象胜利状态传递进去的参数。

    function timeout() {  return new Promise(resolve => {      setTimeout(() => {          console.log(1)          resolve() // resolve('success')      }, 1000)  })}// 不加async和await是2、1   加了是1、2async function foo() {  await timeout() // let res = await timeout() res是success  console.log(2)}foo()
  • 对于失败的解决

    function timeout() {  return new Promise((resolve, reject) => {      setTimeout(() => {          // resolve('success')          reject('error')      }, 1000)  })}async function foo() {  return await timeout()}foo().then(res => {  console.log(res)}).catch(err => {  console.log(err)})

    Object 扩大

  • Object.values()
    Object.values() 返回一个数组,其元素是在对象上找到的可枚举属性值。属性的程序与通过手动循环对象的属性值所给出的程序雷同(for...in,然而for...in还会遍历原型上的属性值)。

    const obj = {  name: 'baidu',  web: 'www.baidu.com',  course: 'es'}console.log(Object.values(obj))// ["baidu", "www.baidu.com", "es"]
  • Object.entries()
    Object.entries()办法返回一个给定对象本身可枚举属性的键值对数组,其排列与应用 for...in 循环遍历该对象时返回的程序统一。(区别在于 for-in 循环也枚举原型链中的属性)

    let grade = {  'lilei': 98,  'hanmei': 87}for (let [key, value] of grade) {  console.log(key, value) // Uncaught TypeError: grade is not iterable}
  • Object.getOwnPropertyDescriptors()

    console.log(Object.getOwnPropertyDescriptor(data, 'Lima'))// {value: "58/40", writable: true, enumerable: false, configurable: true}

    String 扩大

    ES8 中 String 新增了两个实例函数 String.prototype.padStart 和 String.prototype.padEnd,容许将空字符串或其余字符串增加到原始字符串的结尾或结尾。
  • String.prototype.padStart()
    把指定字符串填充到字符串头部,返回新字符串。

    const str = 'xxx'console.log(str.padStart(8, 'x'))console.log(str.padEnd(8, 'y'))console.log(str.padStart(8))

    场景1:日期格式化

    const now = new Date()const year = now.getFullYear()const month = (now.getMonth() + 1).toString().padStart(2, '0')const day = (now.getDate()).toString().padStart(2, '0')console.log(year, month, day)console.log( `${year}-${month}-${day}` )

    场景2:数字替换

    // 数字替换,比方手机号,身份证号const tel = '13012345678'const newTel = tel.slice(-4).padStart(tel.length, '*')console.log(newTel) // *******5678
  • String.prototype.padEnd()
    办法会用一个字符串填充以后字符串(如果需要的话则反复填充),返回填充后达到指定长度的字符串。从以后字符串的开端(右侧)开始填充。

    const str1 = 'I am learning es in imooc'console.log(str1.padEnd(30, '.'))// I am learning es in imooc.....const str2 = '200'console.log(str2.padEnd(5))// "200  "

    场景:工夫戳对立长度

    // 伪代码console.log(new Date().getTime()) // 工夫戳 13位的timestamp = +String(timestamp).padEnd(13, '0')

    尾逗号 Trailing commas

    ES8 容许函数的最初一个参数有尾逗号(Trailing comma)。

    function clownsEverywhere(  param1,  param2,) {  /* ... */}clownsEverywhere(  'foo',  'bar',)

    ECMAScript2018(ES9)

    for await of

    异步迭代器(for-await-of):循环期待每个Promise对象变为resolved状态才进入下一步。

    function Gen(time) {  return new Promise(function(resolve, reject) {      setTimeout(function() {          resolve(time)      }, time)  })}async function test() {  let arr = [Gen(2000), Gen(100), Gen(3000)]  for await (let item of arr) {      console.log(Date.now(), item)  }}test()// 1560092345730 2000// 1560092345730 100// 1560092346336 3000

    RegExp Updates

  • dotAll 模式
    正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,然而有两个例外。一个是四个字节的 UTF-16 字符,这个能够用u修饰符解决;另一个是行终止符(line terminator character)。
  • U+000A 换行符(\n)
  • U+000D 回车符(\r)
  • U+2028 行分隔符(line separator)
  • U+2029 段分隔符(paragraph separator)
    如何判断以后正则是否应用了 dotAll 模式呢

    const re = /foo.bar/s // Or, `const re = new RegExp('foo.bar', 's')` .console.log(re.test('foo\nbar')) // trueconsole.log(re.dotAll) // trueconsole.log(re.flags) // 's'

    记住一句话就能够了解 dotAll 模式:它让 . 货真价实。

  • 具名组匹配
    咱们在写正则表达式的时候,能够把一部分用()包裹起来,被包裹起来的这部分称作“分组捕捉”。

    console.log('2020-05-01'.match(/(\d{4})-(\d{2})-(\d{2})/))// ["2020-05-01", "2020", "05", "01", index: 0, input: "2020-05-01", groups: undefined]

    这个正则匹配很简略,依照 match 的语法,没有应用 g 标识符,所以返回值第一个数值是正则表达式的残缺匹配,接下来的第二个值到第四个值是分组匹配(2020, 05, 01)。

此外 match 返回值还有几个属性,别离是 index、input、groups。

  1. index [匹配的后果的开始地位]
  2. input [搜寻的字符串]
  3. groups [一个捕捉组数组 或 undefined(如果没有定义命名捕捉组)]
    这里提到了命名捕捉组的概念,如果没有定义 groups 就是 undefined。很显著,咱们上述的返回值就是 undefined 间接阐明没有定义命名捕捉分组。那什么是命名捕捉分组呢?

    console.log('2020-05-01'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/))// ["2020-05-01", "2020", "05", "01", index: 0, input: "2020-05-01", groups: {…}]

    这段代码的返回值 groups 曾经是 Object 了,具体的值是:

    groups: { year: "2020", month: "05", day: "01"}

    这个 Object 的 key 就是正则表达式中定义的,也就是把捕捉分组进行了命名。想获取这些捕捉能够这样做:

    let t = '2020-05-01'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/)// ["2020-05-01", "2020", "05", "01", index: 0, input: "2020-05-01", groups: {…}]console.log(t.groups.year) // 2020console.log(t.groups.month) // 05console.log(t.groups.day) // 01
  4. 后行断言
    在 ES9 之前 JavaScript 正则只反对后行断言,不反对后行断言。简略温习下后行断言的常识:

    let test = 'hello world'console.log(test.match(/hello(?=\sworld)/))// ["hello", index: 0, input: "hello world", groups: undefined]

    这段代码要匹配前面是 world 的 hello,然而反过来就不成:

    let test = 'world hello'console.log(test.match(/hello(?=\sworld)/))// null

    比方咱们想判断后面是 world 的 hello,这个代码是实现不了的。在 ES9 就反对这个后行断言了:

    let test = 'world hello'console.log(test.match(/(?<=world\s)hello/))// ["hello", index: 6, input: "world hello", groups: undefined]

    (?<...)是后行断言的符号,(?...)是后行断言的符号,而后联合 =(等于)、!(不等)、\1(捕捉匹配)。

    Object Rest & Spread

    在 ES9 新增 Object 的 Rest & Spread 办法,间接看下示例:

    const input = {  a: 1,  b: 2}const output = {  ...input,  c: 3}console.log(output) // {a: 1, b: 2, c: 3}

    咱们再来看下 Object rest 的示例:

    const input = {  a: 1,  b: 2,  c: 3}let { a, ...rest } = inputconsole.log(a, rest) // 1 {b: 2, c: 3}

    当对象 key-value 不确定的时候,把必选的 key 赋值给变量,用一个变量收敛其余可选的 key 数据,这在之前是做不到的。

    Promise.prototype.finally()

    指定不论最初状态如何都会执行的回调函数。

Promise.prototype.finally() 办法返回一个Promise,在promise执行完结时,无论后果是fulfilled或者是rejected,在执行then()和catch()后,都会执行finally指定的回调函数。这为指定执行完promise后,无论后果是fulfilled还是rejected都须要执行的代码提供了一种形式,防止同样的语句须要在then()和catch()中各写一次的状况。

new Promise((resolve, reject) => {    setTimeout(() => {        resolve('success')        // reject('fail')    }, 1000)}).then(res => {    console.log(res)}).catch(err => {    console.log(err)}).finally(() => {    console.log('finally')})

场景1:loading敞开

须要每次发送申请,都会有loading提醒,申请发送结束,就须要敞开loading提示框,不然界面就无奈被点击。不论申请胜利或是失败,这个loading都须要敞开掉,这时把敞开loading的代码写在finally里再适合不过了。

场景2:数据库断开链接

let connectiondb.open().then(conn => {    connection = conn    return connection.select({        name: 'Jane'    })}).then(result => {    // Process result    // Use `connection` to make more queries})···.catch(error => {    // handle errors}).finally(() => {    connection.close()})

字符串扩大

放松对标签模板里字符串本义的限度, 遇到不非法的字符串本义返回undefined,并且从raw上可获取原字符串。

ES9开始,模板字符串容许嵌套反对常见转义序列,移除对ECMAScript在带标签的模版字符串中转义序列的语法限度。

// 带标签的模板字符串

const foo = (a, b, c, d) => {    console.log(a)    console.log(b)    console.log(c)    console.log(d)}// foo(1, 2, 3, 4)const name = 'xxx'const age = 18foo `这是${name},他的年龄是${age}岁` 

ES9 规范移除了对 ECMAScript带标签的模板字符串 中转义序列的语法限度。

function tag(strs) {    console.log(strs)    // strs[0] === undefined    // strs.raw[0] === "\\unicode and \\u{55}"}// 在标签函数中应用tag `\u{61} and \u{62}`  //tag `\u{61} and \unicode`  // 后果是 undefined// 之前的版本会报错:Invalid Unicode escape sequence// 有效的Unicode转义序列// 报错:let bad = `bad escape sequence: \unicode` 

ECMAScript2019(ES10)

Object.fromEntries()

Object.fromEntries() 把键值对列表转换为一个对象,这个办法是和 Object.entries() 绝对的。
Object.fromEntries([    ['foo', 1],    ['bar', 2]])// {foo: 1, bar: 2}

String 扩大

  • String.prototype.trimStart()

    trimStart() 办法从字符串的结尾删除空格,trimLeft()是此办法的别名。
    let str = '   foo  'console.log(str.length) // 8str = str.trimStart()console.log(str.length) // 5
  • String.prototype.trimEnd()

    trimEnd() 办法从一个字符串的右端移除空白字符,trimRight 是 trimEnd 的别名。
    let str = '   foo  'console.log(str.length) // 8str = str.trimEnd()console.log(str.length) // 6

    Array 扩大

  • Array.prototype.flat()

    flat() 办法会依照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
    const numbers = [1, 2, [3, 4, [5, 6]]]console.log(numbers.flat())// [1, 2, 3, 4, [5, 6]]

    此时 flat 的参数没有设置,取默认值 1,也就是说只扁平化向下一级,遇到 [3, 4, [5, 6]] 这个数组会扁平会解决,不会再持续遍历外部的元素是否还有数组

    const numbers = [1, 2, [3, 4, [5, 6]]]console.log(numbers.flat(2))// [1, 2, 3, 4, 5, 6]

    当 flat 的参数大于等于 2,返回值就是 [1, 2, 3, 4, 5, 6] 了。

  • Array.prototype.flatMap()

    flatMap() 办法首先应用映射函数映射每个元素,而后将后果压缩成一个新数组。从办法的名字上也能够看进去它蕴含两局部性能一个是 map,一个是 flat(深度为1)。
    const numbers = [1, 2, 3]numbers.map(x => [x * 2]) // [[2], [4], [6]]numbers.flatMap(x => [x * 2]) // [2, 4, 6]

    这个示例能够简略比照下 map 和 flatMap 的区别。当然还能够看下上面的示例:

    let arr = ['今天天气不错', '', '早上好']arr.map(s => s.split(''))// [["今", "天", "天", "气", "不", "错"],[""],["早", "上", "好"]]arr.flatMap(s => s.split(''))// ["今", "天", "天", "气", "不", "错", "", "早", "上", "好"]

    订正 Function.prototype.toString()

    函数是对象,并且每个对象都有一个 .toString() 办法,因为它最后存在于Object.prototype.toString() 上。所有对象(包含函数)都是通过基于原型的类继承从它继承的。这意味着咱们以前曾经有 funcion.toString() 办法了。

Function.prototype.toString() 办法返回一个示意以后函数源代码的字符串。

这意味着还将返回正文、空格和语法详细信息。

function foo() {    // es10新个性    console.log('xxxx')}console.log(foo.toString())// 间接在办法名toString()console.log(Number.parseInt.toString())

可选的Catch Binding

在 ES10 之前咱们都是这样捕捉异样的:

try {    // tryCode} catch (err) {    // catchCode}

在这里 err 是必须的参数,在 ES10 能够省略这个参数:

try {    console.log('Foobar')} catch {    console.error('Bar')}

JSON扩大

  • JSON superset

    什么是 JSON 超集?,简而言之就是让 ECMAScript 兼容所有JSON反对的文本。 ECMAScript 曾在规范 JSON.parse 局部说明 JSON 确为其一个子集,但因为 JSON 内容能够失常蕴含 U+2028行分隔符 与 U+2029段分隔符,而ECMAScript 却不行。
  • JSON.stringify() 加强能力

    JSON.stringify 在 ES10 修复了对于一些超出范围的 Unicode 展现谬误的问题。因为 JSON 都是被编码成 UTF-8,所以遇到 0xD800–0xDFFF 之内的字符会因为无奈编码成 UTF-8 进而导致显示谬误。在 ES10 它会用转义字符的形式来解决这部分字符而非编码的形式,这样就会失常显示了。
    // \uD83D\uDE0E  emoji 多字节的一个字符console.log(JSON.stringify('\uD83D\uDE0E')) // 笑脸// 如果咱们只去其中的一部分  \uD83D 这其实是个有效的字符串// 之前的版本 ,这些字符将替换为特殊字符,而当初将未配对的代理代码点示意为JSON转义序列console.log(JSON.stringify('\uD83D')) // "\ud83d"

    Symbol 扩大

  • Symbol.prototype.description

    只读属性,返回 Symbol 对象的可选形容的字符串。

咱们晓得,Symbol 的形容只被存储在外部的 Description ,没有间接对外裸露,咱们只有调用 Symbol 的 toString() 时才能够读取这个属性:

const name = Symbol('es')console.log(name.toString()) // Symbol(es)console.log(name) // Symbol(es)console.log(name === 'Symbol(es)') // falseconsole.log(name.toString() === 'Symbol(es)') // true

当初能够通过 description 办法获取 Symbol 的形容:

const name = Symbol('es')console.log(name.description) // esconsole.log(name.description === 'es') // true

ECMAScript2020(ES11)

String 扩大

  • String.prototype.matchAll()

    matchAll() 办法返回一个蕴含所有匹配正则表达式及分组捕捉后果的迭代器
    const str = `<html>  <body>    <div>第一个div</div>    <p>这是一个p</p>    <span>span</span>    <div>第二个div</div>  <body></html>`

    请找出所有的div元素。

    function selectDiv(regExp, str) {  let matches = []  for (let match of str.matchAll(regExp)) {      matches.push(match[1])  }  return matches}const res = selectDiv(regExp, str)console.log(res)

    Dynamic Import

    按需 import 提案几年前就已提出,现在终于能进入ES正式标准。这里集体了解成“按需”更为贴切。古代前端打包资源越来越大,打包成几M的JS资源已成常态,而往往前端利用初始化时基本不须要全量加载逻辑资源,为了首屏渲染速度更快,很多时候都是按需加载,比方懒加载图片等。而这些按需执行逻辑资源都体现在某一个事件回调中去加载。

页面上有一个按钮,点击按钮才去加载ajax模块。

const oBtn = document.querySelector('#btn')oBtn.addEventListener('click', () => {    import('./ajax').then(mod => {        // console.log(mod)        mod.default('static/a.json', res => {            console.log(res)        })    })})

当然,webpack目前已很好的反对了该个性。

BigInt

在 ES10 减少了新的原始数据类型:BigInt,示意一个任意精度的整数,能够示意超长数据,能够超出2的53次方。

Js 中 Number类型只能平安的示意-(2^53-1)至 2^53-1 范的值

console.log(2 ** 53) // es7 幂运算符console.log(Number.MAX_SAFE_INTEGER) // 最大值-1

应用 BigInt 有两种形式:

  • 形式一:数字前面减少n

    const bigInt = 9007199254740993nconsole.log(bigInt)console.log(typeof bigInt) // bigintconsole.log(1n == 1) // trueconsole.log(1n === 1) // false
  • 形式二:应用 BigInt 函数

    const bigIntNum = BigInt(9007199254740993n)console.log(bigIntNum)

    Promise.allSettled()

    学习了ES新个性,咱们都晓得 Promise.all() 具备并发执行异步工作的能力。但它的最大问题就是如果其中某个工作出现异常(reject),所有工作都会挂掉,Promise间接进入reject 状态。而 Promise.allSettled 返回一个在所有给定的promise已被决定或被回绝后决定的promise,并带有一个对象数组,每个对象示意对应的promise后果。
    Promise.allSettled([  Promise.reject({      code: 500,      msg: '服务异样'  }),  Promise.resolve({      code: 200,      data: ['1', '2', '3']  }),  Promise.resolve({      code: 200,      data: ['4', '5', '6']  })]).then(res => {  console.log(res)  // console.log('胜利')  const data = res.filter(item => item.status === 'fulfilled')  console.log(data)}).catch(err => {  console.log(err)  console.log('失败')})

    globalThis

    Javascript 在不同的环境获取全局对象有不通的形式:

  • node 中通过 global
  • web 中通过 window, self 等.

    self:关上任何一个网页,浏览器会首先创立一个窗口,这个窗口就是一个window对象,也是js运行所附丽的全局环境对象和全局作用域对象。self 指窗口自身,它返回的对象跟window对象是截然不同的。也正因为如此,window对象的罕用办法和函数都能够用self代替window。

globalThis 提供了一个规范的形式来获取不同环境下的全局 this 对象(也就是全局对象本身)。不像 window 或者 self 这些属性,它确保能够在有无窗口的各种环境下失常工作。所以,你能够安心的应用 globalThis,不用放心它的运行环境。为便于记忆,你只须要记住,全局作用域中的 this 就是 globalThis。

console.log(globalThis)

可选链 Optional chaining

可让咱们在查问具备多层级的对象时,不再须要进行冗余的各种前置校验。
const user = {    address: {        street: 'xx街道',        getNum() {            return '80号'        }    }}

在之前的语法中,想获取到深层属性或办法,不得不做的前置校验,否则很容易命中 Uncaught TypeError: Cannot read property... 这种谬误,这极有可能让你整个利用挂掉。

const street = user && user.address && user.address.streetconst num = user && user.address && user.address.getNum && user.address.getNum()console.log(street, num)

用了 Optional Chaining ,下面代码会变成

const street2 = user?.address?.streetconst num2 = user?.address?.getNum?.()console.log(street2, num2)

可选链中的 ? 示意如果问号右边表达式有值, 就会持续查问问号前面的字段。依据下面能够看出,用可选链能够大量简化相似繁琐的前置校验操作,而且更平安。

空值合并运算符(Nullish coalescing Operator)

空值合并运算符(??)是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。

当咱们查问某个属性时,常常会遇到,如果没有该属性就会设置一个默认的值。

const b = 0 // 或者null undefined falseconst a = b || 5console.log(a)

空值合并运算符 ?? 咱们仅在第一项为 null 或 undefined 时设置默认值

// false 0  有效const a = b ?? 123console.log(a)

ECMAScript2021(ES12)

replaceAll

返回一个全新的字符串,所有合乎匹配规定的字符都将被替换掉
const str = 'hello world';str.replaceAll('l', ''); // "heo word"

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的回收行为)

当咱们通过(const、let、var)创立一个变量时,垃圾收集器GC将永远不会从内存中删除该变量,只有它的援用依然存在可拜访。WeakRef对象蕴含对对象的弱援用。对对象的弱援用是不会阻止垃圾收集器GC复原该对象的援用,则GC能够在任何时候删除它。

WeakRefs在很多状况下都很有用,比方应用Map对象来实现具备很多须要大量内存的键值缓存,在这种状况下最不便的就是尽快开释键值对占用的内存。

目前,能够通过WeakMap()或者WeakSet()来应用WeakRefs

我想要跟踪特定的对象调用某一特定办法的次数,超过1000条则做对应提醒

let map = new Map()function doSomething(obj){    ...}function useObject(obj){    doSomething(obj)    let called = map.get(obj) || 0  called ++     if(called>1000){     console.log('以后调用次数曾经超过1000次了,over')  }    map.set(obj, called)}

如上尽管能够实现咱们的性能,然而会产生内存溢出,因为传递给doSomething函数的每个对象都永恒保留在map中,并且不会被GC回收,因而咱们能够应用WeakMap

let wmap = new WeakMap()function doSomething(obj){    ...}function useObject(obj){    doSomething(obj)    let called = wmap.get(obj) || 0    called ++    if(called>1000){     console.log('以后调用次数曾经超过1000次了,over')  }    wmap.set(obj, called)}

逻辑运算符和赋值表达式

运算符和赋值表达式,新个性联合了逻辑运算符(&&,||,??)和赋值表达式而JavaScript已存在的 复合赋值运算符有如下
a ||= b//等价于a = a || (a = b)a &&= b//等价于a = a && (a = b)a ??= b//等价于a = a ?? (a = b)

数字分隔符

数字分隔符,能够在数字之间创立可视化分隔符,通过_下划线来宰割数字,使数字更具可读性
const money = 1_000_000_000;//等价于const money = 1000000000;1_000_000_000 === 1000000000; // true

参考文章

  • JavaScript ES12新个性领先体验
  • JS语法 ES6、ES7、ES8、ES9、ES10、ES11、ES12新个性
  • ECMAScript2015~2020语法全解析