ES6-ES13

一. ES6新特性

ECMAScript 6.0(简称 ES6或ES2015) 2015 年 6 月正式发布

1. let和const声明常量
  • letconst 不允许重复声明变量,var可以

  • letconst 声明的变量不会在预解析的时候解析(没有变量提升),var可以

  • letconst 声明的变量会被所有代码块限制作用范围,作用域在{ }内,var为全局作用域

  • letconst 的区别

    • let 声明的变量的值可以改变,const 声明的变量的值不可以改变,用于声明常量

    • let 声明的时候可以不赋值,const 声明的时候必须赋值

2. 解构赋值

解构赋值,就是快速的从对象或者数组中取出成员的一个语法方式

  • 解构对象

    const obj = {
      name: '孙悟空',
      age: 18,
    }
    ​
    // ES5 的方法得到对象中的成员
    let name = obj.name
    let age = obj.age
    ​
    // 对象解构
    let { name, age } = obj

  • 解构数组

    const arr = [ 'one', 'two']
    ​
    // ES5 的方式从数组中获取成员
    let a = arr[0]
    let b = arr[1]
    ​
    // 数组解构
    let [a, b] = arr

3. 模版字符串

ES5 中我们表示字符串的时候使用 '' 或者 "",ES6新增【`` 】(反引号)

  • 反引号可以换行书写,单引号不行

    let str = `
        hello
        world`
  • 反引号可在字符串里拼接变量

    let num = 10
    ​
    // ES5 需要字符串拼接变量的时候
    let str = 'hello' + num + 'world'
    ​
    //模版字符串拼接变量
    let str = `hello${num}world${num}`

4. 字符串扩展
  • includes

    判断字符串中是否存在指定字符,返回true或false

    let str = "ke"
    ​
    str.includes("e")           //true
    str.startsWith("t")         //false
  • repeat

    表示将原字符串重复n次,返回一个新字符串

    let str = "js"
    ​
    str.repeat(3)       //jsjsjs
    str.repeat(0)       //"" 
    str.repeat(3.5)     //jsjsjs
    str.repeat("3")     //jsjsjs

5. 数值扩展
二进制和八进制表示法
0b111110111 === 503     // true
0o767 === 503           // true

isFinite、isNaN
Number.isFinite(100)    //true
​
Number.isNaN(NaN)       //true

传统方法isFinite和isNaN,先调用Number将非数值的值转为数值,再进行判断

Number.isFinite 非数值类型,返回false

Number.isNaN 非NaN类型,返回false

isInteger

用来判断一个数值是否为整数,JS整数和浮点数采用的是同样的储存方法

Number.isInteger(100) 		//true
Number.isInteger(100.0) 	//true

误判情况

//转成二进制位超过了53个二进制位,2被丢弃
Number.isInteger(3.0000000000000002) 	// true

//绝对值小于Number.MIN_VALUE
Number.isInteger(5E-324) 		// false

数值分隔符

ES2021,允许 JavaScript 的数值使用下划线(_)作为分隔符

let budget = 10_00_0000_0;
budget === 100000000;
  • 不能放在数值的最前面或最后面

  • 不能两个或两个以上的分隔符连在一起

  • 小数点的前后不能有分隔符

  • 科学计数法里面,表示指数的eE前后不能有分隔符

Number.EPSILON

表示 1 与大于 1 的最小浮点数之间的差,2.220446049250313e-16, 为JS能够表示的最小精度

0.1 + 0.2 === 0.3		// 0.30000000000000004 !== 0.3    falae

Math.trunc

除一个数的小数部分,返回整数部分

Math.trunc(1.2)	 //1

Math.sign

判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值

  • 参数为正数,返回【+1】

  • 参数为负数,返回【-1】

  • 参数为 0,返回【0】

  • 参数为-0,返回【-0】

  • 其他值,返回【NaN】

Math.sign(-10) 		// -1

6. 数组扩展
6-1 扩展运算符
  • 将一个数组转为用逗号分隔的参数序列

    console.log(...[1, 2, 3])		// 1 2 3
    const [first, ...rest] = [1, 2, 3];    	// first:1   rest:[2, 3]
  • 扩展运算符可以将字符串转为真正的数组,能够正确识别 Unicode 字符

    [...'he']				// [ "h", "e"]

6-2 Array.from
  • 可将两种对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)

    let arrayLike = {
        '0': 'a',
        '1': 'b',
        '2': 'c',
        length: 3
    };
    
    // ES5 的写法
    var arr1 = [].slice.call(arrayLike) 	// ['a', 'b', 'c']
    
    // ES6 的写法
    let arr2 = Array.from(arrayLike) 	// ['a', 'b', 'c']
  • 常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,函数内部的arguments对象,都可转换

  • Array.from()还可以接受一个函数作为第二个参数,作用类似于数组的map()方法,用来对每个元素进行处理,将处理后的值放入返回的数组

    Array.from(arrayLike, x => x * x);
    // 等同于
    Array.from(arrayLike).map(x => x * x);
    
    Array.from([1, 2, 3], (x) => x * x)		// [1, 4, 9]
  • Array.from()的另一个应用是,将字符串转为数组

6-3 Array.of

将一组值转化为数组,即新建数组

Array.of(3, 11, 8) 		// [3,11,8]

Array()当参数不少于 2 个,才会返回由参数组成的新数组,参数只有一个正整数时,实际上是指定数组的长度

Array.of()基本上可以用来替代Array()new Array(),并且不存在由于参数不同而导致的重载

filter 返回符合条件的数组内容

array.filter( item=> {
	return item
})
6-4. Array.find

find(),findIndex(),findLast(),findLastIndex()

参数是一个回调函数,可以接受三个参数,依次为当前的值、当前的位置和原数组。在函数中写要查找元素的条件

find()查找第一个符合条件的数组元素,并返回该元素,没有返回undefined

findIndex()则是返回下标,都不符合则返回【-1】

[1, 10, 15].find( (value, index, arr)=> { return value > 9 }) 		// 10

indexOf()方法无法识别数组的NaN成员,但是findIndex()方法可以借助Object.is()方法做到

6-5. Array.fill

使用给定值,填充一个数组,可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置

['a', 'b', 'c'].fill(7, 1, 2)			// ['a', 7, 'c']

6-6 Array.includes

返回一个布尔值,表示某个数组是否包含给定的值,第二个参数表示搜索的起始位置,默认为【0】

替代indexOf,找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判

6-7 flat、flatMap

flat数组的扁平化处理,接收一个参数为扁平化的层数,原数组有空位,会被跳过

[1, 2, [3, [4, 5]]].flat(1)			// [1, 2, 3,[4, 5]]

flatMap()方法对原数组的每个成员执行一个函数

[2, 3, 4].flatMap((x) => [x, x * 2])		// [2, 4, 3, 6, 4, 8]

7.对象扩展
7-1 对象简写
let name = '孙悟空';

const Person = {
   
    name: this.name,
    age: 18,
    hello: function ({ console.log('我的名字是', this.name) })
    return {name:name, age:age}
};

const Person = {
    name,
    age: 18,
    hello() { console.log(this.name) }
    return {name, age}
};

7-2 属性名表达式

允许对象的属性通过【[ ]】包裹,属性名表达式与简洁表示法,不能同时使用,会报错。

const a = {
  'first word': 'hello',
  [lastWord]: 'world'
};

a['first word'] 	// "hello"
a[lastWord] 		// "world"
a['last word'] 		// "world"

7-3 Object.assign

Object.assign(target, obj1,obj2)的第一个参数是目标对象,后面可以跟一个或多个源对象作为参数

target:参数合并后存放的对象,obj1:参数1,obj2:参数2

const obj1 = { name: "a" };
const obj2 = { name: "b" };
const obj3 = { age :100 };

Object.assign(obj1, obj2, obj3);		//obj1 {name: 'b', age: 100}
7-4 Object.is

方法判断两个值是否是相同的值

NaN===NaN 			//false
+0===-0				//true

Object.is(NaN,NaN) 	//true
Object.is(+0,-0)	//false

7-5 name

函数的name属性,返回函数名。对象方法也是函数,因此也有name属性

7-6 属性可枚举性和遍历

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为

Object.getOwnPropertyDescriptor`方法可以获取该属性的描述对象

描述对象的enumerable属性,称为“可枚举性”,如果该属性为false

  • for...in循环:只遍历对象自身的和继承的可枚举的属性

  • Object.keys():返回对象自身的所有可枚举的属性的键名

  • JSON.stringify():只串行化对象自身的可枚举的属性

  • Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性

7-7 属性的遍历

(1)for...in

循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)

(2)Object.keys(obj)

返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名

(3)Object.getOwnPropertyNames(obj)

返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名

(4)Object.getOwnPropertySymbols(obj)

返回一个数组,包含对象自身的所有 Symbol 属性的键名

(5)Reflect.ownKeys(obj)

返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举

7-8 super 关键字

this关键字总是指向函数所在的当前对象,ES6 新增关键字super,指向当前对象的原型对象

7-9 对象的赋值解构

ES2018 扩展运算符【...】引入对象,解构赋值和扩展运算详情可看【数组篇】

8. 函数扩展
8-1 箭头函数
  • 箭头函数只能简写函数表达式,不能简写声明式函数

  • 语法: (函数的行参) => { 函数体内要执行的代码 }

  • 函数的行参只有一个的时候可以不写 () 其余情况必须写

  • 函数体只有一行代码的时候,可以不写 {} ,并且会自动 return,默认返回undefined

    //单行语句可以省略return
    var sum = (num1, num2) => num1 + num2
    
    //返回一个对象或对象的形式,必须在对象的外面加上括号,单个参数可以省略括号
    let getTempItem = id => ({ id: id, name: "Temp" });
  • 箭头函数内部没有 this,导致内部的this就是外层代码块的this,是固定的

  • 不可以当作构造函数,不可以使用arguments对象,不可以使用yield命令,bind方法无效

  • 下面两个场合不应该使用箭头函数

    • 定义对象的方法,且该方法内部包括this

    • 对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域

    const cat = {
      lives: 9,
      jumps: () => {
        this.lives--;
      }
    }
    • 需要动态this

    • 代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,this 此时是全局对象。普通函数,this就会动态指向被点击的按钮对象

    var button = document.getElementById('press');
    button.addEventListener('click', () => {
      this.classList.toggle('on');
    });

8-2 函数的参数

参数的默认值

  • ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。参数赋值了,但是布尔值为false,则赋值失效,改为默认值。为了避免这个问题,通常需要先判断一下参数是否被赋值

    function log(x, y) {
        if (typeof x === 'undefined') {
            x = 'Hello';
        }
        y = y || 'World';
    
        console.log(x, y);
    }
    
    log('Hello', '') 		// Hello World
  • 在 ES6 中我们可以直接把默认值写在函数的行参位置,而且无需判断类型

    function log(x, y = 'World') {
      console.log(x, y);
    }

    注意事项

    // 默认值采用0
    function m1({x = 0, y = 0} = {}) {
      return [x, y];
    }
    
    function m1(x = 0, y = 0) {
      return [x, y];
    }
    
    
    // 传值非m2({x: 3, y: 8}),二者都有,则默认值采用undefined
    function m2({x, y} = { x: 0, y: 0 }) {
      return [x, y];
    }

    如果传入undefined,将触发该参数等于默认值,null则没有这个效果

res参数

用于获取函数的多余参数,这样就不需要使用arguments对象【类数组,还需要转化为数组】

function add(...values) {
  let sum = 0;
  for (var val of values) { sum += val }
}

add(2, 5, 3)

函数参数的尾逗号

ES2017 允许函数的最后一个参数有尾逗号

function fn(param1,param2,) { /* ... */ }

8-3 尾调用

指某个函数的最后一步是调用另一个函数

function f(x){ return g(x) }

以下三种情况,都不属于尾调用

//最后一步为赋值
function f(x){
  let y = g(x);
  return y;
}

//最后一步为+1
function f(x){
  return g(x) + 1;
}

//最后一步为 return undefined
function f(x){
  g(x);
}

使用场景:递归(复杂度 O(n) ==> 复杂度 O(1) ), Fibonacci 数列,...

递归非常耗费内存,需要同时保存多个调用帧,容易“栈溢出”。但尾递只存在一个调用帧,不会发生“栈溢出”

ES6 的尾调用优化只在严格模式下开启,正常模式是无效的,正常模式采用“循环”替换“递归”

8-4 toString方法

ES2019对函数实例的Function.prototype.toString()方法做出了修改

toString()方法返回函数代码本身,以前会省略注释和空格

修改后的toString()方法,明确要求返回一模一样的原始代码

function /* foo comment */ foo () {}

foo.toString()		// "function /* foo comment */ foo () {}"

8-5 catch 的参数省略

JS的try...catch结构,要求catch命令后面必须跟参数,接受try代码块抛出的错误对象

ES2019允许catch语句省略参数

9. Symbol

ES6 新增的原始数据类型Symbol,表示独一无二的值。属于 JavaScript 语言的原生数据类型之一

原数据类型:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)

  1. 使用Symbol作为对象属性名,接受一个字符串作为参数,控制台显示,比较容易区分

  2. 能防止某一个键被不小心改写或覆盖

  3. Symbol 值作为对象属性名时,不能用点运算符

  4. 对象的Symbol属性遍历时会在最后

let name = Symbol()
let age = Symbol('age')
let obj  ={
    [name]:"孙悟空",
    [age]:18,
    gender:'男'
}

// Symbol属性只能通过这种方式访问
obj[age] === 18
obj['gender']  === ’男‘

10.Iterator迭代器
  • 为各种数据结构,提供一个统一的、简便的访问接口

  • 使得数据结构的成员能够按某种次序排列

  • 【for...of】循环使用Iterator ,对象不能直接使用【for...of】

    • for in得到对象的key,数组和字符串的下标

    • for offorEach得到值

11.Set结构

类似于数组,但成员的值都是唯一的,没有重复的值,是一个对象构造函数

let obj = new Set([1, 2, 3, 2, 3]) 			//Set(3) {1, 2, 3}

实例的属性和方法

  • Set.size:返回Set实例的成员总数

  • Set.add(value):添加某个value

  • Set.delete(value):删除某个value,返回一个布尔值,表示删除是否成功

  • Set.has(value):返回一个布尔值,表示该值是否为Set的成员

  • Set.clear():清除所有成员,没有返回值

遍历

  • Set.keys():返回键名

  • Set.values():返回键值

  • Set.entries():返回键值对

  • Set.forEach():遍历每个成员

常用去重

[...new Set('ababbc')].join('')			// "abc"
[...new Set(arr=[2,2,3])]				// arr=[2,3]

WeakSet结构 类似于 Set结构,但WeakSet 的成员只能是对象

12.Map结构

类似于对象,键值对的集合,”键“的范围不限于字符串,键可以是各种类型的值(包括对象)

let map = new Map([ ["name","孙悟空"] ])
map.set({a:1},"18")

map.set(['a'], 555);
map.get(['a']) 			// undefined,两个不同的数组实例,内存地址是不一样的

实例的属性和方法

  • Map.size:返回 Map 结构的成员总数

  • Map.set(key,value):添加key对应得value,返回 Map 结构本身

  • Map.get(key):获取key对应的value

  • Map.delete(key):删除某个键(键名+键值)

  • Map.has(key):某个键是否在当前 Map 对象之中

  • Map.clear():清除所有成员,没有返回值

遍历

  • Map.keys():返回键名

  • Map.values():返回键值

  • Map.entries():返回所有成员

  • Map.forEach():遍历 Map 的所有成员

13.Proxy代理

在对象和对象的属性值之间设置一个代理,获取该对象的值或者设置该对象的值, 以及实例化等多种操作, 都会被拦截住,进行统一处理

13-1.get方法

用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身【操作行为所针对的对象,经过Proxy化的原对象,可选】

let target = {}
let proxy = new Proxy(target,{
    get(target, prop){
        return target[prop]
    }
})

13-2.set方法

拦截某个属性的赋值操作,接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身【可选】

let target = {}
let proxy = new Proxy(target, {
    set(target, prop, value){
        if(prop === "data"){
            box.innerHTML = value
        }
        target[prop] = value;
    }
})

13-3.has方法

拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。如:in运算符

接受两个参数,分别是目标对象、需查询的属性名

目标对象不可扩展,或某个属性不可配置,has拦截就会报错,has()拦截对for...in循环不生效

let target = { _prop: "内部数据" }

let proxy = new Proxy(target, {
    has(target, key) {
        if (key[0] === '_') {
            return false;
        }
        return key in target;
    }
})

13-4.this问题

Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。不做任何拦截的情况下,也无法保证与目标对象的行为一致

let target = new Set()
const proxy = new Proxy(target, {
    get(target, key) {
        const value =  target[key]
        // 遇到 Function 都手动绑定一下 this
        if (value instanceof Function) {
            return value.bind(target) //不能是 call、apply 
        }
        return value
    }
})
proxy.add(1)

其他方法
  • apply

    • 拦截函数的调用、callapply操作

  • construct

    • 拦截new命令

  • deleteProperty

    • 拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除

  • defineProperty

    • 拦截 Object.defineProperty()操作

  • getOwnPropertyDescriptor

    • 拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined

  • getPrototypeOf

    • 拦截获取对象原型,拦截以下操作:

      • Object.prototype.__proto__

      • Object.prototype.isPrototypeOf()

      • Object.getPrototypeOf()

      • Reflect.getPrototypeOf()

      • instanceof

  • isExtensible

    • 拦截Object.isExtensible()操作

  • ownKeys

    • 拦截对象自身属性的读取操作,拦截以下操作:

      • Object.getOwnPropertyNames()

      • Object.getOwnPropertySymbols()

      • Object.keys()

      • for...in循环

  • preventExtensions

    • 拦截Object.preventExtensions(),该方法必须返回一个布尔值,否则会被自动转为布尔值

  • setPrototypeOf

    • 拦截Object.setPrototypeOf()方法

  • Proxy.revocable

    • 返回一个可取消的 Proxy 实例

14.Reflect对象

Object对象的部分属于语言内部的方法,放到Reflect对象上。现阶段,某些方法同时在ObjectReflect对象上部署,未来的新方法将只部署在Reflect对象上。之后从Reflect对象上可以拿到语言内部的方法

  • 修改某些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方法拦截target对象的属性赋值行为。采用Reflect.set方法将值赋值给对象的属性,确保完成原有的行为,然后再部署额外的功能

    var loggedObj = new Proxy(obj, {
      get(target, name) {
        console.log('get', target, name);
        return Reflect.get(target, name);
      },
      deleteProperty(target, name) {
        console.log('delete' + name);
        return Reflect.deleteProperty(target, name);
      },
      has(target, name) {
        console.log('has' + name);
        return Reflect.has(target, name);
      }
    });

  • 静态方法

    //同Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数
    Reflect.apply(target, thisArg, args)
    
    //同new target(...args),提供了一种不使用new,来调用构造函数的方法
    Reflect.construct(target, args)
    
    //查找并返回target对象的name属性,如果没有该属性,则返回undefined
    Reflect.get(target, name, receiver)
    
    //设置target对象的name属性等于value
    Reflect.set(target, name, value, receiver)
    
    //同于Object.defineProperty【已废除】,用来为对象定义属性
    Reflect.defineProperty(target, name, desc)
    
    //同delete obj[name],用于删除对象的属性
    Reflect.deleteProperty(target, name)
    
    //对应name in obj里面的in运算符
    Reflect.has(target, name)
    
    //返回对象的所有属性,等同Object.getOwnPropertyNames与Object.getOwnPropertySymbols结合
    Reflect.ownKeys(target)
    
    //对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展
    Reflect.isExtensible(target)
    
    //对应Object.preventExtensions方法,让一个对象变为不可扩展,返回一个布尔值
    Reflect.preventExtensions(target)
    
    //同Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象
    Reflect.getOwnPropertyDescriptor(target, name)
    
    //读取对象的__proto__属性,对应Object.getPrototypeOf(obj)
    Reflect.getPrototypeOf(target)
    
    //设置目标对象的原型(prototype),对应Object.setPrototypeOf(obj, newProto),返回一个布尔值
    Reflect.setPrototypeOf(target, prototype)

15.Promise

Promise 是异步编程的一种解决方案,比传统的解决方案回调函数, 更合理和更强大。ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象

  • 指定回调函数方式更灵活易懂,解决异步 回调地狱

  • 状态不会再次改变,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)

Promise也有一些缺点:

  • 无法取消Promise,一旦新建它就会立即执行,无法中途取消

  • 不设置回调函数,Promise内部抛出的错误,不会反应到外部

  • 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择

15-1 回调地狱
  • 当一个回调函数嵌套一个回调函数的时候,会出现一个嵌套结构,多层嵌套导致回调地狱发生

  • 发送多个请求,第二个请求需要第一个请求的结果中的某一个值作为参数,第三个则需要第二个,...,嵌套结构的增多,导致代码横向发展(不健康),不方便阅读和维护

15-2 Promise使用
  • 基础用法

    new Promise1(function (resolve, reject) {
      // 函数体,resolve 表示成功的回调,reject 表示失败的回调
    }).then(function (res) {
      // 成功的函数
    }).catch(function (err) {
      // 失败的函数
    })
    
    new Promise2(function (resolve, reject) { }
    Promise2.then(()=>{ })
    Promise2.catch(()=> { })
  • 异步加载图片示例

    function loadImageAsync(url) {
        return new Promise(function(resolve, reject) {
            const image = new Image()
            image.src = url
    
            image.onload = function() { resolve(image) }
    
            image.onerror = function() { reject(new Error('图片加载失败:' + url)) }
        })
    }
  • 实现的 Ajax 操作示例

    const getJSON = function(url) {
      return new Promise(function(resolve, reject){
    
          const client = new XMLHttpRequest();
          client.open("GET", url);
          client.onreadystatechange = handler;
          client.send();
          
          if (this.status === 200) {
            resolve(this.response)
          } else {
            reject(new Error(this.statusText))
          }
          
      })
    }
    
    getJSON("/posts.json").then( 
        (res)=> { }, 
        (err)=> { }
    );

15-3 Promise.then

为Promise 实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的

then方法返回新的Promise实例,可以采用链式写法,即then方法后面再调用另一个then方法

15-4 Promise.catch

指定发生错误时的回调函数

异步方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then()方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获

15-5 Promise.finally

ES2018 引入,指定不管 Promise 对象最后状态如何,都会执行的操作

15-6 Promise.all

接受一个数组【成员是 Promise 实例】作为参数,包装成一个新的 Promise 实例

成员不是 Promise 实例,会将参数转为 Promise 实例。参数可以不是数组,但必须具有 Iterator 接口

const p = Promise.all([p1, p2, p3])

p的 fulfilled 状态:只有数组的全部Promise实例的状态都变成fulfilledp的状态才会变成fulfilled,此时实例的返回值组成一个数组,传递给p的回调函数

p的 rejected 状态:只要实例之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数

15-7 Promise.race

同上,将多个 Promise 实例,包装成一个新的 Promise 实例

参数实例第一个改变状态的,p的状态就跟着改变,接受其返回值传递给p的回调函数

const p = Promise.race([p1, p2, p3])

15-8 Promise.allSettled

ES2020确定一组异步操作是否都结束了(不管成功或失败)

接受一个数组作为参数,数组的成员都是Promise 对象,并返回一个新的 Promise 对象。参数数组的所有 Promise 对象都发生状态变更,它的状态才会变成fulfilled

15-9 Promise.any

ES2021引入,接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态

15-10 Promise.resolve

将现有对象转为 Promise 对象

15-11 Promise.reject

返回一个新的 Promise 实例,该实例的状态为rejected

16.Generator 函数
  • ES6 提供的一种异步编程解决方案,是一个状态机,封装了多个内部状态,可以改写异步的回调函数

  • function关键字与函数名之间有一个星号【*,在哪个位置都可】

  • 调用 Generator 函数后,函数不执行,返回一个指向内部状态的指针对象【遍历器对象,遍历函数内部的每一个状态】

  • 函数体内部使用yield表达式,暂停执行的标记,定义不同的内部状态【yield:产出】

  • 每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式或return语句为止,

    let st = function *state(){
        yield  1;
        yield  2;
        return 3;
    }()
    				
    st.next()		//{ value: 1, done: false }
    st.next()		//{ value: 2, done: false }
    st.next()		//{ value: 3, done: true }
    st.next()		//{ value: undefined, done: true }
    
    // value:状态值,done:遍历是否结束
    //yield表达式本身没有返回值,或者说总是返回undefined
    //next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
  • 异步操作同步化

    function* main() {
      var result = yield request("http://some.url");
      var resp = JSON.parse(result);
        console.log(resp.value);
    }
    
    function request(url) {
      makeAjaxCall(url, function(response){
        it.next(response);
      });
    }
    
    var it = main();
    it.next();

17. Class语法
17-1 类的写法
class Person {
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    say(){
        console.log(this.age)
    }
}
let obj = new Person("孙悟空",18)
17-2 getter与setter
class List{
    constructor(ele){
        this.element = ele
    }

    get html(){
        return this.element.innerHTML
    }
    set html(arr){
        this.element.innerHTML = arr.map(item=>`<li>${item}</li>`).join("")
    }
}
let obj = new List(document.querySelector("#list"))

obj.html = ["aaa","bbb","cccc"]
17-3 静态属性和方法

通过static修饰的属性和方法

ES2022正式为class添加了私有属性,方法是在属性名之前使用#表示,只能在类的内部使用

class Person {
    #count = 0;
    static name = "Person这个类"
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    say(){ console.log( this.age ) }

    static eat(){ console.log("eat") }
}
let obj = new Person("孙悟空",18)

Person.eat()
17-4 继承

ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象

class Person {
    static name = "Person这个类"
    constructor(name){
        this.name = name;
    }
    say(){ console.log(this.name) }
}

class Student extends Person{
    constructor(name, score){
        super(name)
        this.score = score
    }
    say(){ super.say() }
}

let obj = new Student("孙悟空", 200)

18. 模块化

JS 现有两种模块:ES6 模块,简称 ESM; CommonJS 模块【Node.js 专用】,简称 CJS。两者不兼容

ES6 模块使用 exportimport ;CommonJS 模块使用 module.exportsrequire()

写法 export default :

// 快速写法,默认输出,一个模块只能用一次,不能输出变量
// import命令可以为该匿名函数指定任意名字,引入时不需要{}
export default A1

import a1 from "./a.js"

写法 export

export {A1, A2}

import {A1, A2} from "./a.js"

import {A1 as a1, A2 as a2} from "./a.js"

import * as A from "./a.js"		//通过 A.A1、A.A2 调用

// as 可以重命名
// export对外输出的三种接口:函数(Functions),类(Class),变量(var、let、const 声明的Variables)
// import命令具有提升效果,会提升到整个模块的头部,首先执行

混合

export function each() { }

export default function() { }			//export default默认输出
export { each as forEach }			//export 具名输出

import _, { each, forEach } from 'lodash';	//混合引入

整体加载fs模块,生成一个对象(_fs),然后再从这个对象上面读取方法

这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”

// CommonJS模块
let { stat, exists } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入

fs模块加载所需的方法,其他方法不加载。称为“编译时加载”或者静态加载

ES6 可以在编译时就完成模块加载,效率较 CJS 加载高。这也导致了没法引用 ES6 模块本身,因为它不是对象

// ES6模块自动采用严格模式
import { stat, exists, readFile } from 'fs'

模块的继承、跨模块常量、import( )支持动态加载模块...

二. ES7新特性

1. 求幂运算符
Math.pow(x,y)		//返回 x 的 y 次幂

			// Math常用方法
Math.random()		//返回 0 ~ 1 之间的随机数
Math.floor(x)		//向下舍入
Math.ceil(x)		//向行上舍入
Math.round(x)		//四舍五入           
Math.trunc(x)		//去掉小数部分,只保留整数部分
方法描述
abs(x)返回 x 的绝对值
acos(x)返回 x 的反余弦值
asin(x)返回 x 的反正弦值
atan(x)以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值
atan2(y,x)返回从 x 轴到点 (x,y) 的角度(介于 -PI/2 与 PI/2 弧度之间)
ceil(x)对数进行上舍入
cos(x)返回数的余弦
exp(x)返回 Ex 的指数
floor(x)对 x 进行下舍入
log(x)返回数的自然对数(底为e)
max(x,y,z,...,n)返回 x,y,z,...,n 中的最高值
min(x,y,z,...,n)返回 x,y,z,...,n中的最低值
pow(x,y)返回 x 的 y 次幂
random()返回 0 ~ 1 之间的随机数
round(x)四舍五入
sin(x)返回数的正弦
sqrt(x)返回数的平方根
tan(x)返回角的正切
tanh(x)返回一个数的双曲正切函数值
trunc(x)将数字的小数部分去掉,只保留整数部分

2. 数组的includes方法

includes:判断字符串是否包含指定的子字符串,返回symbol,区分大小写

indexOf:某个指定的字符串值在字符串中首次出现的位置。没有匹配返回 -1

//str:规定需检索的字符串值,start:开始检索的位置

string.includes(str, start)

string.indexOf(str,start)

  • 如果仅仅查找数据是否在数组中,建议使用includes

  • 如果是查找数据的索引位置,建议使用indexOf

    [1, 2, NaN].includes(NaN)     	// true
    [1, 2, NaN].indexOf(NaN)  	// -1

三. ES8新特性

1. async 和 await

ES2017 引入async 函数,使得异步操作变得更加方便, Generator 函数的语法糖。内置执行器完全自动化,更好的语义、更广的适用性

async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await

async表示函数里有异步操作,await表示需要等待后面的表达式执行完毕,返回结果才会执行下一步

async函数内部return语句返回的Promise对象,会成为then方法回调函数的参数

async function f() { return 'hello world' }

f().then(v => console.log(v))		// "hello world"

await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值

async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象可以通过

  • async内部的try catch处理

  • then方法的第二个参数处理

  • catch 方法回调函数处理【推荐】

//方式一
async function f() {
    try{
        var res =  await ajax("http://baidu.com")
    }catch(err){
        console.log("err",err)
    }
}


async function f() {
  throw new Error('出错了');
}

//方式二
f().then(
  v => console.log('resolve', v),
  e => console.log('reject', e)			//reject Error: 出错了
)

//方式三
f().then(
  v => console.log('resolve', v)
).catch(
  e => console.log('reject', e) 		//reject Error: 出错了
)

2. 对象方法扩展
  • 对象数据

let obj = {
    name:"孙悟空",
    age:18
}

Object.values(obj)		                // ['孙悟空', 18]

Object.entries(obj)		                // [['name', '孙悟空'],['age', 18]]

Object.getOwnPropertyDescriptors(obj)		
						//{{name: {valeu: '孙悟空', ...}}, {age: {valeu: 18, ...}}}

  • 克隆对象

let obj = {
    name:"孙悟空",
    age:100,
    location:{
        provice:"蓬莱",
        city:"仙岛"
    },
    
    // 修改属性名称,避免陷入死循环
    get nameget(){
        return this.name
    },
    set nameset(value){
        this.name = value
        // 首字母大写功能
        // this.name = value.substring(0,1).toUpperCase()+value.substring(1)
    },
    
    //只设置city,防止破坏province,简化路径【obj.location.city】=>【obj.city】
    get city(){
        return this.location.city
    },
    set city(value){
        this.location.city = value
    },
}


//Object.assign(obj2,obj)  //无法克隆get、set方法,其他方法复制出错
Object.defineProperties(obj2,Object.getOwnPropertyDescriptors(obj))

3. 字符串填充

padStart、padEnd方法可以使得字符串达到固定长度。有两个参数,字符串目标最小长度和填充内容

let str= "wukong"

str.padStart(9,"x")			//xxxwukong
str.padStart(5,"x")			//wukong
str.padEnd(9,"x")			//wukongxxx
str.padEnd(5,"x")			//wukong

4. 函数参数末尾加逗号

新加一行时不必给上一行再补充一个逗号,版本控制工具【Git】的修改记录也更加整洁

四. ES9新特性

1. 对象的扩展运算符扩充
let obj1 = {
    name:"孙悟空",
    age:18,
    location:"蓬莱"
}
let obj2 = {
    gender:"男"
}

let {name,...other} = obj1     // name: "孙悟空", other: { age:18, location:"蓬莱"}
{...obj1,...obj2}	       // { name:"孙悟空", age:18, location:"蓬莱", gender:"男"}

2. 正则表达式命名捕获组

JS正则表达式可以返回一个匹配的对象,一个包含匹配字符串的类数组,比如: 以 YYYY-MM-DD的格式解析日期,

这样的代码可读性很差,并且在改变正则表达式的结构的时候很有可能就会改变匹配对象的索引

ES9允许使用命名捕获 【?<name>】,在打开捕获括号后立即命名

let str = "今天是2023-06-19"
let reg1 = /([0-9]{4})-([0-9]{2})-([0-9]{2})/g
let reg2 = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/g

let res1 = reg1.exec(str)
// 简述:['2023-06-19', '2023', '06', '19', groups: undefined]

let res2 = reg2.exec(str)
// 在此基础修改【groups】:[groups: {year: '2023', month: '06', day: '19'}]

3. Promise.finally

Promise回调无论是成功还是失败,最后都运行finally方法。用于隐藏对话框,关闭数据连接等

function ajax(){
    return new Promise((resolve, reject)=> { reject('') })
}

ajax().then(res=>{
    
}).catch(err=>{

}).finally(()=>{
    
})

4. 异步遍历器
4-1 同步遍历器的问题
function* fn() {
    yield  1
    yield  2
}
const asyncI = fn()
asyncI.next()			// {value: 1, done: false}
asyncI.next()			// {value: 2, done: false}
asyncI.next()			// {value: undefined, done: true}

function* fn() {
    yield  new Promise(resolve=>resolve("1"))
    yield  new Promise(resolve=>resolve("2"))
}

const asyncI = fn();
asyncI.next().value.then(res=>{console.log(res)})		// 1
asyncI.next().value.then(res=>{console.log(res)})		// 2

value 属性的返回值是一个 Promise 对象,用来放置异步操作。此写法,不太符合直觉,语义也比较绕

4-2 异步遍历器生成函数

Generator 函数返回一个同步遍历器,异步 Generator 函数的作用,是返回一个异步遍历器对象。在语法上,异步 Generator 函数就是async函数与 Generator 函数的结合

async function* fn() {
    yield  new Promise(resolve=>resolve("1111"))
    yield  new Promise(resolve=>resolve("2222"))

}
const asyncI = fn();

asyncI.next().then(res=>{
    console.log(res)			// {value: '1111', done: false}
    return asyncI.next()
}).then(res=>{
    console.log(res)			// {value: '2222', done: false}
    return asyncI.next()
}).then(res=>{
    console.log(res)			// {value: undefined, done: true}
})

4-3 for await of

for...of循环用于遍历同步的 Iterator 接口,for await...of`循环,用于遍历异步的 Iterator 接口

async function test() {
    for await (let i of asyncI) {
        console.log(i)
    }
}
test()

4-4 案例改造
function timer(t) {
      return new Promise(resolve => {
          setTimeout(() => {
              resolve(t)
          }, t)
      })
 }


async function* fn() {
    yield timer(1000)//任务1
    yield timer(2000)//任务2
    yield timer(3000)//任务3
}

// 使用 for await ...of
async function fn1() {
    for await(const val of fn()) {
        console.log("start",Date.now())
        console.log(val);
        console.log("end",Date.now())
    }
}

fn1();

4-5 node用法
// 传统写法
function main(inputFilePath) {
  const readStream = fs.createReadStream(
    inputFilePath,
    { encoding: 'utf8', highWaterMark: 1024 }
  );
  readStream.on('data', (chunk) => {
    console.log('>>> '+chunk);
  });
  readStream.on('end', () => {
    console.log('### DONE ###');
  });
}

// 异步遍历器写法
async function main(inputFilePath) {
  const readStream = fs.createReadStream(
    inputFilePath,
    { encoding: 'utf8', highWaterMark: 1024 }
  );

  for await (const chunk of readStream) {
    console.log('>>> '+chunk);
  }
  console.log('### DONE ###');
}

五. ES10新特性

1. Object.fromEntries

将键值对列表转换为对象,如map对象,【forEach】也可实现

let arr = [["name", "kerwin"], ["age", 100]];
Object.fromEntries(arr)					// {name: 'kerwin', age: 100}

// map对象转对象
let m = new Map()
m.set("name","孙悟空")
Object.fromEntries(m)					// {name: '孙悟空'}

// url参数转对象
let str ="name='孙悟空'&age=18"
let searchParams = new URLSearchParams(str)
Object.fromEntries(searchParams)		        // {name: '孙悟空', age: '18'}

2. trimStart 和 trimEnd

trimStart 和 trimEnd 方法在实现与 trimLeft 和 trimRight 相同,去除字符串的前边或后边的空格

let str = "  孙悟空   "

srt.trim				// "孙悟空"
str.trimStart(str)			// "孙悟空   "
str.trimEnd(str)			// "  孙悟空"
str.trimLeft(str)			// "孙悟空   "
str.trimRight(str)			// "  孙悟空"

3. Symbol 的 description

为Symbol对象添加了只读属性 description ,该对象返回包含Symbol描述的字符串

let s = Symbol("孙悟空")
s.description				 //孙悟空

4. catch的参数可选

ES10之前catch的参数是必须的,ES10之后,如无需知晓错误参数,统一操作,即可忽略

function test() {
    try {
        
    } catch(e) {
        console.log(e)
    }
}

六. ES11新特性

1. Promise.allSettled

之前的 promise 的处理只要其中有一个失败,就要走 catch ,而拿不到其他成功的回调

Promise.allSettled 返回一个数组,成员是所有需要处理的 promise 的回调的结果,无论成功或失败

const promises = [ ajax('/200接口'), ajax('/800接口') ];

// 如多个请求是同一资源,但服务器存在替换,此时只需要拿到任意一个即可
Promise.allSettled(promises).then(results=>{
    // 过滤出成功的请求
    results.filter(item =>item.status === 'fulfilled');
    // 过滤出失败的请求
    results.filter(item=> item.status === 'rejected');
})

2. module新增
2-1 动态导入 import

标准用法的 import 导入的模块是静态的,会使所有被导入的模块,在加载时就被编译,降低首页加载速度

希望根据条件导入模块或者按需导入模块,可以使用动态导入代替静态导入,如点击通过点击事件

async function (){
    if(xxx){
        let res1 = await import("./1.js")
    }else{
        let res2 = await import("./2.js")
    }
}

2-2 import.meta

返回一个对象,有一个 url 属性,值为当前模块的url路径,只能在模块内部使用

2-3 export * as ' ' from ' '

使用场景:如果需要A模块,但又不能修改A模块,可以采用此方式【类似于继承】,再写入该模块的方法

export * as A from  './a.js'

test(){ }

3. 字符串的matchAll

返回一个包含所有匹配正则表达式的结果的迭代器。可以使用 for...of 遍历,或者使用展开运算符【...】或者 Array.from 转换为数组

业务场景:拿到某标签的值

let str = `
<ul>
<li>1</li>
<li>2</li>
</ul>
`
let reg = /<li>(.*)<\/li>/g


str.match(reg)					// '<li>1</li>', '<li>2</li>'


let match = null;
while(match = reg.exec(str)){ match[1] }	// 1  2


for(let i of str.matchAll(reg)){ i[1] }		// 1  2

4. BigInt

JS 能够准确表示的整数范围在-253到253之间(不含两个端点),超过这个范围,无法精确表示这个值,这使得 JS 不适合进行科学和金融方面的精确计算

2**53			//9007199254740992
9007199254740992	//9007199254740992
9007199254740993	//9007199254740992

Math.pow(2,53) === Math.pow(2,53)+1

BigInt 类型的数据必须添加后缀n ,BigInt 类型只能和 BigInt 类型运算,不能和 int 类型计算

let b = BigInt(3)	// 3n
1n + 2n 		// 3n

使用场景:后端传递的id的值大于2^53,前端接收的处理时,会将值处理错

  • 让后端传递id时,将id的类型从 int 改为 string

  • 使用【json-bigint】库,自行安装

    import JSONBigInt from 'json-bigint'
    
    // id从 int型 转 string型
    let JSONBigIntStr = JSONBigInt({ storeAsString: true })
    JSONBigIntStr.parse(id)
    
    // id从 int型 转 bigint型
    let JSONBigIntNative = JSONBigInt({ useNativeBigInt: true })
    JSONBigIntNative.parse(id)

5. globalThis

globalThis 提供了一个标准的方式来获取不同环境下【node、浏览器、app等】的全局 this 对象

省略对环境的判断的兼容处理,通常用于跨平台库的编写

// 以前
var getGlobal = function () {
    if (typeof self !== 'undefined') return self
    if (typeof window !== 'undefined') return window
    if (typeof global !== 'undefined') return global
    throw new Error('unable to locate global object')
};

let globals = getGlobal()

if (globals.document) {
    console.log("进行dom操作相关")
} else {
    console.log("不能进行dom操作")
}

// 现在
if (globalThis.document) {
    console.log("进行dom操作相关")
} else {
    console.log("不能进行dom操作")
}

6. 空值合并运算符

【??】逻辑运算符,当左侧操作数为 null 或 undefined 时,返回右侧的操作数,否则返回左侧的操作

let obj = null ?? '孙悟空'			// 孙悟空

【??】和 【||】 的区别

  • 【''】和 【0】,【??】的左侧 为 【''】或者【0】时,依然会返回左侧的值

  • 【||】 会对左侧的数据进行boolean类型转换,所以【''】和 【0】会被转换成false,返回右侧的值

7. 可选链操作符

【?.】可选链前面的值如果是null或undefined,则不再执行后面的,之前返回可选链前面的值

【undefined.undefined】会报错,为避免错误的发生,不能直接取值,

let obj = {
    name:"孙悟空",
    // location:{
    //     city:"蓬莱"
    // }
}

// 【&&】如果为真,去下一个值,直到最后
obj && obj.location && obj.location.city

// 【?.】可选链操作符
obj?.location?.city
obj?.location?.city || '蓬莱'

七. ES12新特性

1. 逻辑赋值操作符

逻辑赋值操作符 ??=、&&=、 ||=

let a = true
let b = false

a = a && b		//false
a &&= b 		//false

a = a || b		//true
a ||= b			//true

a = b ?? "true"		//false
a ??= "true"		//false

2. 数字分隔符

方便程序员查看代码,分隔符可以分割十进制、二进制、十六进制的数据,不能在头和尾出现,不能连续出现

const number = 1_000_000_000_000
const hex = 0xA1_B2_C3

3. replaceAll

所有匹配都会被替代项替换

模式可以是字符串或正则表达式,而替换项可以是字符串或针对每次匹配执行的函数。并返回一个全新的字符串

let str ="I wish to wish the wish you wish to wish"

// 方式一 replace + 正则
let newStr = str.(/wish/g, "wish")

// 方式二 replaceAll
let newStr = str.replaceAll("wish", "wish")

4. Promise.any
Promise.all			// 当所有成功才执行
Promise.race			// 取最快成功的那一个
Promise.allSettlned		// 不管成功失败都取,自行过滤

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态

如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态

业务场景:多个账号【qq、微信、手机、邮箱】登录同一用户,但凡账号符合其中一个,登录成功

let ajax1 = function(){
	return new Promise((resolve, reject) =>{
		resolve()
	})
}
...

Promise.any([ajax1(), ajax2(), ajax3()]).then(res =>{
	...
}).catch(err={
	...
})

5. WeakRef

ES6:Set、Map、WeakSet、WeakMap

垃圾回收机制常用算法:引用计数法【引用为0时,回收】、标记清除法

WeakSet使用传入值必须为 引用类型,引用计数不增加,当应用类型设置为空时,size和for方法无法使用

使用场景:如数组中存放Dom节点,当节点变量被删除时【设置为null】,数组中依然存在引用,造成内存泄漏,使用WeakSet可以有效避免内存泄漏的发生

注意:添加值时是通过变量的形式,删除的也是变量,则无法回收内存,需要添加节点本身或变量所引用值得本身,不能通过变量传入,删除变量的方式,导致代码量大。【WeakSet为强引用方式】

<button id="like">按钮</button>

<!-- WeakSet为强引用方式,变量like删除之后,wmap引用的节点依然存在 -->
<script>
    let wmap = new WeakMap()
	let like = document.getElementById("like")
	wmap.set(like, { click: 0 })
	document.body.removeChild(like)
</script>

<!-- 方式一:使用过程不通过变量 -->
<script>
    let wmap = new WeakMap()
    wmap.set(document.getElementById("like"), { click: 0 });
    document.body.removeChild(document.getElementById("like"))
</script>

<!-- 方式二: 使用 WeakRef 弱引用-->
<script>
    let wmap = new WeakMap()
    let like = new WeakRef(document.getElementById("like"))
    wmap.set(like.deref(), { a: 0 })
    document.body.removeChild(like.deref())
    
    like.deref()	//undefined      WeakRef实例deref方法,返回 原始对象 或 undefined【被回收】
</script>

在一般情况下,对象的引用是强引用的,这意味着只要持有对象的引用,它就不会被垃圾回收。只有当该对象没有任何的强引用时,垃圾回收才会销毁该对象并且回收该对象所占的内存空间

WeakRef 允许保留对另一个对象的弱引用,而不会阻止被弱引用对象被垃圾回收

6. FinalizationRegistry

清理器注册表功能,用来指定目标对象被垃圾回收机制清除以后,所要执行的回调函数

let obj = {}

const registry = new FinalizationRegistry(data => {
    console.log("被销毁了", data)
});

registry.register( obj , data);		//参数1:观察的对象;参数2:想要传递的数据

八. ES13新特性

1. 类的私有和静态属性

新增【#】私有属性,之前程序员之间的君子协定【_】下划线,但还是能实例修改;新增静态属性【static】

class Cache{
    // 私有属性
    #obj = {};
    get(key){ return this.#obj[key] };
    #myFun(){ }
    
    // 静态属性私有化 配合 静态方法
    static #num = 0;
    static getCount(){
        return this.#count
    }
}

新增静态代码块,允许在类中通过static关键字定义一系列静态代码块,代码块会在类被创造的时候执行一次

代码块配合静态成员变量按照顺序在类初始化的时候执行一次。可以使用super关键字来访问父类的属性

class myClass{
    static obj = new Map()
    static {
        this.obj.set("name","孙悟空")
        this.obj.set("age",18)
    }
}

通过 in来判断某个对象是否拥有某个私有属性

class Cache {
    #obj = {}
    
    hasObj(){
        return #obj in this
    }
}

2. 支持最外层await

顶层await只能用在 ES6 模块,不能用在 CommonJS 模块,不能用在普通<script>标签中,需要模块化声明

CommonJS 模块的require()是同步加载,有顶层await,就无法处理加载

<-- 模块化声明 【type="module"】 -->
<script type="module"></script>
    
    
<script type="module">
function ajax() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve("data");
        }, 1000);
    })
}
let res = await ajax();
</script>

使用场景:顶层await + 模块化 + 动态导入

// 模块B ./moduleB.js
function ajax(){
    return new Promise((resolve)=>{
        setTimeout(()=>{
            resolve("data")
        },2000)
    }
})
let data = await ajax()

export·default {
	name : "moduleB",
    data
}

    
// 模块A 动态导入 模块B  ./index.js
let moduleB = await import("./moduleB.js")

3. at函数来索引元素

可以替代通过【leng】取倒数元素,代码简洁

let arr = [1, 2, 3, 4]

arr[1] === arr.at(1)
arr[arr.length-1] === arr.at(-1)
arr[arr.length-2] === arr.at(-2)

3. 正则的结束索引

新增【d】正则的结束索。原先只有开始索引,匹配对象在原对象的起始位置

let str = "今天是2023-06-20
let reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/d
reg.exec(str)

// 无结束索引打印
[..., index: 3 ]

// 结束索引打印,indices存放结束索引位置和其他信息
[..., index: 3, indices: Array]  

4. findLast和findLastIndex

find:第一个符合条件的元素

findIndex:第一个符合条件的元素的下标

findLast:最后一个符合条件的元素

findLastIndex:最后一个符合条件的元素的下标

let arr = [1,2,3,4,5]

arr.find( val=> { return val % 2 === 0 })			// 2
arr.findIndex( val=> { return val % 2 === 0 })			// 1
arr.findLast( val=> { return val % 2 === 0 })			// 4
arr.findLastIndex( val=> { return val % 2 === 0 })		// 3

5. Error对象的Cause属性

Error对象新增 cause 属性来指明错误出现的原因。手动为错误添加更多的信息,帮助开发者更好地定位错误

function getData(){
    try{ }
    catch(e){
        throw new Error('New error ',{cause:"错误信息..."})
    }
}


try{
    getData()
}catch(e){
    console.log(e.cause)
}

其他

in 语句用来判断一个属性(name)是否属于一个对象(obj),返回Symbol

Map 转为数组

  • [...myMap]

数组 转为 Map

  • new Map([ arr ])

Map 转为对象

Map 的键都是字符串,无损地转为对象。非字符串的键名,键名会被转成字符串,再作为对象的键名

  • let obj = {}
    for (let [k,v] of Map) { obj[k] = v }

对象转为 Map

  • let map = new Map(Object.entries(obj))

Map 转为 JSON

Map 的键名都是字符串,可以选择转为对象 JSON

  • JSON.stringify(strMapToObj(Map))

Map 的键名有非字符串,可以选择转为数组 JSON

  • JSON.stringify([...map])

JSON 转为 Map

所有键名都是字符串

  • objToStrMap(JSON.parse(jsonStr))

JSON 是一个数组,且每个数组成员本身,又是一个有两个成员的数组

  • new Map(JSON.parse(jsonStr))

严格模式主要有以下限制:

  • 变量必须声明后再使用

  • 函数的参数不能有同名属性,否则报错

  • 不能使用with语句

  • 不能对只读属性赋值,否则报错

  • 不能使用前缀 0 表示八进制数,否则报错

  • 不能删除不可删除的属性,否则报错

  • 不能删除变量delete prop,会报错,只能删除属性delete global[prop]

  • eval不会在它的外层作用域引入变量

  • evalarguments不能被重新赋值

  • arguments不会自动反映函数参数的变化

  • 不能使用arguments.callee

  • 不能使用arguments.caller

  • 禁止this指向全局对象

  • 不能使用fn.callerfn.arguments获取函数调用的堆栈

  • 增加了保留字(比如protectedstaticinterface

...

参考资料地址:

千锋教育 kerwin ES6-ES13:千锋教育最新版Web前端ES6-ES13教程,JavaScript高级进阶视频教程_哔哩哔哩_bilibili

ECMAScript 6 入门 阮一峰:https://es6.ruanyifeng.com

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值