1

ES6-ES12部分简单知识点总结,希望对大家有用~ - MomentYY

 2 years ago
source link: https://www.cnblogs.com/MomentYY/p/16047880.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ES6-ES12简单知识点总结

1.ES6相关知识点

1.1.对象字面量的增强

ES6中对对象字面量的写法进行了增强,主要包含以下三个方面的增强:

  • 属性的简写:当给对象设置属性时,如果希望变量名和属性名一样就可以直接写该变量名;
  • 方法的简写:对象中的方法可直接写成foo() {}的形式;
  • 计算属性名:对象的属性名可以动态传入,将变量使用[]包裹即可;
const obj = {
  // 1.属性简写
  name,
  age,
  // 2.方法简写
  foo() {
    console.log('foo')
  },
  // 3.计算属性名
  [key]: 'value'
}

1.2.解构

为了方便从数组或对象中获取数据,ES6给我们提供了解构的方案,可分为数组的解构和对象的解构。

  • 数组的解构:注意数组的解构是按元素顺序来的。

    const names = ['curry', 'kobe', 'klay', 'james']
    
    // 1.基本的解构,解构出数组所有的元素
    var [name1, name2, name3, name4] = names
    console.log(name1, name2, name3, name4) // curry kobe klay james
    
    // 2.解构部分元素,只解构后面两个元素
    var [, , name3, name4] = names
    console.log(name3, name4) // klay james
    
    // 3.解构出第一个元素,后面的元素放到一个新数组中
    var [name1, ...newNames] = names
    console.log(newNames) // [ 'kobe', 'klay', 'james' ]
    
    // 4.解构数组的同时,给元素指定默认值,如果数组中没有该元素,就会使用默认值
    var [name1, name2, name3, name4, name5 = 'default'] = names
    console.log(name1, name2, name3, name4, name5) // curry kobe klay james default
    
  • 对象的解构:对象的解构是不按顺序的,是根据key来赋值的。

    const obj = {
      name: 'curry',
      age: 30,
      team: '金州勇士'
    }
    
    // 1.基本的解构
    var { name, age, team } = obj
    console.log(name, age, team) // curry 30 金州勇士
    
    // 2.解构的同时重命名
    var { name: newName, age: newAge, team: newTeam } = obj
    console.log(newName, newAge, newTeam) // curry 30 金州勇士
    
    // 3.解构的同时指定默认值,当对象中没有该属性时,就会取默认值
    var { height = '1.83' } = obj
    console.log(height) // 1.83
    
    // 4.同时重命名和指定默认值
    var { height: newHeight = '1.83' } = obj
    console.log(newHeight) // 1.83
    
  • 解构的应用场景:一般开发中拿到一个变量,可以对齐进行解构使用,比如对函数的参数进行解构。

    function fn({ name, age, team }) {
      console.log(name, age, team)
    }
    fn(obj) // curry 30 金州勇士
    

1.3.let和const的使用

在ES5中,声明变量都是使用var,从ES6开始新增了两个声明变量的关键字let和const。

  • let关键字:从直观的角度来说,let和var是没有太大的区别的,都是用于声明一个变量。

    let message = 'hello world'
    
  • const关键字:const声明的变量称为常量,表示保存的数据一旦被赋值就不能再被修改了,但是如果是引用类型的数据,还是可以通过引用找到对应的对象进行修改的。

    const obj = {
      name: 'curry',
      age: 30
    }
    const names = ['curry', 'kobe']
    
    obj.name = 'kobe'
    names.push('klay')
    
    console.log(obj) // { name: 'kobe', age: 30 }
    console.log(names) // [ 'curry', 'kobe', 'klay' ]
    
    // 注意:不能直接给引用类型重新赋值
    /*
      obj = {} // TypeError: Assignment to constant variable.
      names = [] // TypeError: Assignment to constant variable.
    */
    
  • 注意:与var不同的是,let和const是不允许重复声明变量的。并且使用let和const声明的变量是具有自己的块级作用域的。var和let可以不设置初始值,const必须设置。

有关作用域提升的问题:

使用过var关键字的人都知道,var声明的变量是会进行作用域提升的,如果使用let和const声明的变量,是不允许在声明之前对其进行访问的,会直接报错。

console.log(message) // undefined
var message = 'hello world'
console.log(message) // ReferenceError: Cannot access 'message' before initialization
let message = 'hello world'

为什么var声明的变量有作用域提升,而let和const没有呢?

  • 作用域提升:在声明变量的作用域中,如果这个变量可以在声明之前被访问,那么就可以称之为作用域提升。
  • 在JavaScript执行之前,会先对我们声明的变量进行收集创建,普通变量的默认值都为undefined,只有等到执行赋值代码时,该变量才会被真正赋值,所以在声明之前访问就为undefined。
  • 那let和const不能访问,是不是let和const声明的变量没有在代码执行前被收集创建呢?到底let和const有没有作用域提升?
    • 其实let和const声明的变量在代码执行前是有被收集创建的,只是不能访问而已,所以就不能称之为作用域提升,在ECMA262对let和const的描述中,这些变量会被创建在包含它们的词法环境被实例化时,但是是不可以访问它们的,直到词法绑定被求值(也就是变量被赋值之后);
    • 在使用var声明的变量,是会添加到window上的,而let和const声明的变量是不会被添加到window上的,实际上是被添加到了变量环境中进行存储;
    • 暂时性死区:在代码中,使用let、const声明的变量,在声明之前变量是不可以被访问的,这种现象称之为temporal dead zone(TDZ);

总结:在上面的代码中,其实message在代码执行前就已经被创建了,在用let进行修饰后,js底层会对其进行约束,以至于在声明前不可以访问。

1.4.模板字符串

ES6允许使用字符串模板来嵌入JS的变量或者表达式来进行拼接。

使用``来编写字符串,可以${expression}来嵌入动态内容,里面可以写一个表达式。

const name = 'curry'
const age = 30

console.log(`My name is ${name}\nMy age is ${age}`)

标签模板字符串:模板字符串另外一种用法,可以用来调用函数,如果使用标签模板字符串,并且在调用的时候插入其他的变量。

  • 模板字符串被拆分了;
  • 第一个元素是数组,元素为被${}的字符串组合;
  • 后面的元素是一个个${}中传入的变量值;
function fn(x, y, z) {
  console.log(x, y, z)
}

const name = 'curry'
const age = 30

fn`sojfo${name}hghaooa${age}jgoajg` // [ 'sojfo', 'hghaooa', 'jgoajg' ] curry 30

应用:在react中编写css就有这么一个库叫styled-components,其原理就是使用的标签模块字符串。

1.5.函数的默认参数

在ES6之前,我们编写的函数参数是没有默认值的,而ES6给我们提供了默认参数,如果传入了该参数就使用传入的值,没有传入就使用默认值。

  • 在ES6之前,如果想实现默认参数的效果,就必须编写以下最终代码。

    function fn() {
      var m = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'aaa'
      var n = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'bbb'
      console.log(m, n)
    }
    
    fn() // aaa bbb
    fn(111, 222) // 111 222
    
  • 在ES6中,可以在声明函数时给其参数默认值。

    • 一般默认值写法:

      function fn(m = 'aaa', n = 'bbb') {
        console.log(m, n)
      }
      
    • 默认值搭配解构的使用:

      // 1.写法一:对象参数+解构
      function fn1({ name, age } = { name: 'curry', age: 30 }) {
        console.log(name, age)
      }
      fn1() // curry 30
      
      // 2.写法二:解构+默认值
      function fn2({ name = 'curry', age = 30 } = {}) {
        console.log(name, age)
      }
      fn2() // curry 30
      

注意:有默认值的参数一般会放到最后,在JS中函数都会有一个length属性,length长度代表函数参数个数,但是有默认值的参数是不会被计算在内的。但是如果将有默认值的参数放在中间,那么后面的参数也不会被计算在内的。

function fn1(x, y, z) {}
console.log(fn1.length) // 3

function fn2(x, y, z = 30) {}
console.log(fn2.length) // 2

function fn3(x, y = 30, z) {}
console.log(fn3.length) // 1

1.6.函数的剩余参数

ES6中引用了剩余参数,可以将不定数量的参数放入到一个数组中,如果最后一个参数是以...为前缀的,那么它会将剩余的参数放入到该参数中,并且作为一个数组。

剩余参数和arguments有什么区别呢?

  • 剩余参数只包含那些没有对应形参的实参,而arguments对象包含了传给函数的所有实参;
  • arguments对象不是一个真正的数组,而rest是一个真正的数组,可以进行数组的所有操作;
  • arguments是早期的ECMAScript中为了方便去获取所有的参数提供的一个数据结构,而剩余参数是ES6中提供并且希望代替arguments的;
function foo(m, n, ...args) {
  console.log(m, n, args)
  console.log(arguments) // [Arguments] { '0': 10, '1': 20, '2': 30, '3': 40, '4': 50 }
}

foo(10, 20, 30, 40, 50) // 10 20 [ 30, 40, 50 ]

1.7.箭头函数

相比于普通函数,箭头函数有一下几个特殊点:

  • 箭头函数内部是不绑定this的,会去它上一层作用域中找;
  • 箭头函数是没有显示原型的,所以不能作为构造函数,使用new操作符来调用;
  • 箭头函数没有自己的argments;
const fn = () => {
  console.log(this)
  // console.log(argments) // ReferenceError: argments is not defined
}

fn() // window对象
console.log(fn.prototype) // undefined

1.8.展开语法

展开语法(spread syntax)可以在函数调用或数组构造是,将数组表示或者字符串在语法层面展开,还可以在构造字面量对象时,将对象表达式按键值对的方式展开。

展开语法的主要使用场景:在函数调用时数组构造时使用:

const nums = [1, 2, 3]
const str = 'abc'

function fn(x, y, z) {
  console.log(x, y, z)
}

// 函数调用时
fn(...nums) // 1 2 3
fn(...str) // a b c

// 数组构造时
const newNums1 = [...nums, 4, 5]
const newNums2 = [...nums, ...str]
console.log(newNums1) // [ 1, 2, 3, 4, 5 ]
console.log(newNums2) // [ 1, 2, 3, 'a', 'b', 'c' ]

1.9.数值的表示

ES6中规范了二进制和八进制的写法。

const num1 = 188 // 十进制
const num2 = 0b101 // 二进制
const num3 = 0o567 // 八进制
const num4 = 0x8cf // 十六进制

// 打印转成对应的十进制数
console.log(num1, num2, num3, num4) // 188 5 375 2255

1.10.Symbol的使用

在ES6之前,对象属性名都是字符串形式,很容易造成属性名冲突,Symbol是ES6中新增的一个基本数据类型,可用来生成一个独一无二的值。

  • 作为属性名:

    const s1 = Symbol()
    const s2 = Symbol()
    const s3 = Symbol()
    
    // 对比调用Symbol生成的值
    console.log(s1 === s2) // false
    
    // Symbol值作为对象key
    // 写法一:定义对象字面量时使用
    const obj = {
      [s1]: 'aaaa'
    }
    // 写法二:对象新增属性
    obj[s2] = 'bbbb'
    // 写法三:通过Object.defineProperty()
    Object.defineProperty(obj, s3, {
      enumerable: true,
      configurable: true,
      writable: true,
      value: 'cccc'
    })
    
    console.log(obj) // { [Symbol()]: 'aaaa', [Symbol()]: 'bbbb', [Symbol()]: 'cccc' }
    
  • 作为属性值:

    const s1 = Symbol()
    
    const obj = {
      name: 'curry',
      age: s1
    }
    
    console.log(obj) // { name: 'curry', age: Symbol() }
    
  • 注意:Symbol作为属性名时,不能通过.方式来获取,也就是obj.s1,只能通过obj[s1]的方式获取。并且在通过Object.keys()获取对象所有的key时,是获取不到这些Symbol的,可以借助Object.getOwnPropertySymbols()来获取所有类型为Symbol的key。

    console.log(obj.s1) // undefined
    
    console.log(obj[s1]) // aaaa
    console.log(obj[s2]) // bbbb
    
    // 使用Object.keys获取对象所有key并打印,发现数组中是没有Symbol的key
    console.log(Object.keys(obj)) // []
    
    // 使用Object.getOwnPropertySymbols(obj)获取所有的Symbol的key
    console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol(), Symbol(), Symbol() ]
    

延伸:Symbol的作用就是给我们创建一个独一无二的值,如果我们想创建一个相同的Symbol可以怎么做呢?

  • 可以使用Symbol.for()来做到这一点;
  • 并且可以通过Symbol.keyFor()方法来获取对应的key;
// 通过Symbol.for(描述)创建并传入相同描述
const s1 = Symbol.for('aaaa')
const s2 = Symbol.for('aaaa')

console.log(s1 === s2) // true

// 拿到s1传入的值
const key = Symbol.keyFor(s1)
console.log(key) // aaaa

// 将拿到的值又传给s3
const s3 = Symbol.for(key)
console.log(s3 === s1) // true
console.log(s3 === s2) // true

1.11.Set和WeakSet的使用

在ES6之前,存储数据的结构主要有两种,分别是数组和对象。在ES6中新增了另外两种数据结构:Set、Map,以及它们另外形式的WeakSet、WeakMap。下面就先来看看Set和WeakSet。

(1)Set:用于保存数据,类似于数组,与数组不同的是Set中的元素是不能重复的。

  • Set的基本使用:

    // 1.创建一个Set结构
    const set = new Set()
    
    // 2.往set中添加元素
    set.add(30)
    set.add(30) // 相同的基本数据类型只会保留一个
    set.add('abc')
    set.add(undefined)
    set.add({})
    set.add({}) // 对象可以重复的原因是,对象存放的是内存地址
    set.add(NaN)
    set.add(NaN) // 为什么NaN也不能重复,因为在Set内部会视NaN为同一个东西
    
    console.log(set) // Set(6) { 30, 'abc', undefined, {}, {}, NaN }
    
  • Set常见的属性:

    • size:返回Set中元素的个数;

      console.log(set.size) // 6
      
  • Set常见的方法:

    • add(value):添加一个元素,返回set对象本身;
    • delete(value):从set中删除与value值相等的元素,返回boolean类型;
    • has(value):判断set中是否存在value这个元素,返回boolean;
    • clear():清空set中所有的元素,无返回值;
    • forEach(callback,[thisArg]):遍历set对象(Set的实例化对象是一个可迭代对象,所以也支持for...of遍历);
    // add
    console.log(set.add(30)) // Set(1) { 30 }
    console.log(set.add(60)) // Set(2) { 30, 60 }
    console.log(set.add(90)) // Set(2) { 30, 60, 90 }
    // delete
    console.log(set.delete(40)) // 未找到40,返回false
    console.log(set.delete(30)) // true
    // has
    console.log(set.has(60)) // true
    console.log(set.has(30)) // false
    // forEach
    set.forEach(item => {
      console.log(item) // 60 90
    })
    // for...of
    for (const item of set) {
      console.log(item) // 60 90
    }
    // clear
    set.clear()
    console.log(set) // Set(0) {}
    

(2)WeakSet:也要求内部元素不能重复。

  • WeakSet与Set的区别:

    • 区别一:WeakSet中只能存放对象类型,不能存放基本数据类型;
    • 区别二:WeakSet对元素的引用是弱引用,如果没有其他引用对该元素进行引用,是可以被GC回收的;
    • 区别三:WeakSet由于是弱引用的原因,所以是不能进行遍历的,存储到WeakSet中的对象是没办法通过遍历获取的;
    • 什么是弱引用?:简单理解就是对对象类型的引用是可以看成没有的,但是却可以访问其对象内部的数据;
  • WeakSet常见方法:add(value)、delete(value)、has(value),没有clear和forEach;

  • WeakSet的基本使用:

    const wSet = new WeakSet()
    // 只能存放对象类型
    // wSet.add(10) // TypeError: Invalid value used in weak set
    wSet.add({ name: 'curry', age: 30 })
    wSet.add([1, 2, 3])
    

1.12.Map和WeakMap的使用

下面来讲讲Map和WeakMap,主要用于存储映射关系。

(1)Map:对于普通对象存储映射关系,只能使用字符串或者Symbol作为属性名,如果普通对象使用对象来作为key的话,会自动将对象转成字符串,但是Map允许我们使用对象作为key。

const obj1 = { name: 'curry', age: 30 }

const obj = {
  [obj1]: 'aaaa'
}
// 普通对象使用对象作为key,对象会自动转成字符串[object Object]
console.log(obj) // { '[object Object]': 'aaaa' }
  • Map的基本使用:

    const obj1 = { name: 'curry', age: 30 }
    const obj2 = { name: 'kobe', age: 24 }
    
    // 方式一:创建map后,通过set添加属性
    const map1 = new Map()
    map1.set(obj1, 'aaaa')
    map1.set(obj2, 'bbbb')
    
    // 方式二:创建map时,传入一个数组
    const map2 = new Map([
      [obj1, 'aaaa'],
      [obj2, 'bbbb']
    ])
    
    console.log(map1)
    console.log(map2)
    
  • Map常见的属性:

    • size:返回Map中元素的个数;

      console.log(map1.size) // 2
      
  • Map常见的方法:与Set很相似,设置和获取属性不太一样。

    • set(key, value):在Map中添加key和value,并且返回整个map对象;
    • get(key):根据key获取map中的value;
    • has(key):判断map中是否包含某一个key,返回boolean值;
    • delete(key):根据key删除一个键值对,返回boolean值;
    • clear():清空所有的属性;
    • forEach(callback, [thisArg]):遍历map(也可使用for...of遍历);
    const map = new Map()
    const obj = { name: 'curry', age: 30 }
    
    // set
    map.set(obj, 'aaaa')
    map.set('bbbb', 1234)
    map.set(11, 'cccc')
    // get
    console.log(map.get(obj)) // aaaa
    // delete
    console.log(map.delete(11)) // true
    console.log(map.delete(22)) // false
    // has
    console.log(map.has('bbbb')) // true
    console.log(map.has('abc')) // false
    // forEach
    map.forEach((value, key) => {
      console.log(key, value)
      /*
        { name: 'curry', age: 30 } aaaa
        bbbb 1234
      */
    })
    // clear
    map.clear()
    
    console.log(map) // Map(0) {}
    

    如果map使用for...of遍历,看一下遍历出来的值是什么样子的:

    // 这里取出的item是一个个数组,数组第一个元素是key,第二个元素是value
    for (const item of map) {
      console.log(item)
    }
    
    // 所以可以在取值的时候对item进行解构
    for (const [key, value] of map) {
      console.log(key, value)
    }
    

(2)WeakMap:也是以键值对的形式存在。

  • WeakMap和Map的区别:与Set和WeakSet的区别类似;
    • 区别一:WeakMap的key只能使用对象,不接受其他类型作为key;
    • 区别二:WeakMap的key对对象的引用是弱引用,如果没有其他引用指向这个对象,那么GC可以回收该对象;
    • 区别三:WeakMap和WeakSet一样也不能进行遍历;
  • WeakMap常见的方法:set(key, value)、get(key)、has(key)、delete(key),没有clear和forEach;

2.ES7相关知识点

2.1.数组的includes方法

在ES7之前,判断数组中包含某一个元素,一般可以用indexOf判断是否为-1,ES7给我们提供了includes方法来判断数组中是否包含指定元素,返回boolean类型;

arr.includes(valueToFind[, fromIndex])
  • valueToFind:需要查找的元素;
  • fromIndex:从指定索引开始查找,如果from为负值,就从array.length + fromIndex的位置开始查找(默认值为0);
const names = ['curry', 'kobe', NaN]

console.log(names.includes('curry')) // true
console.log(names.includes('klay')) // false
console.log(names.includes('curry', 1)) // false

console.log(names.indexOf(NaN)) // -1
console.log(names.includes(NaN)) // true

注意:includes方法是可以判断NaN是否存在的,因为includes的内部实现对NaN采用isNaN方法进行判断。

2.2.指数运算符

在ES7之前,计算数字的指数需要通过Math.pow方法来完成,在ES7中,增加了**运算符,可以来计算指数。

计算2的3次方:

const res1 = Math.pow(2, 3)
const res2 = 2 ** 3

console.log(res1, res2) // 8 8

3.ES8相关知识点

3.1.获取对象所有的value

我们知道可以使用Object.keys获取一个对象的所有key,ES8给我们提供了Object.values来获取所有的value值。

const obj = {
  name: 'curry',
  age: 30,
  team: '勇士'
}

// 传入一个对象
console.log(Object.values(obj)) // [ 'curry', 30, '勇士' ]
// 传入一个字符串,达到split的效果
console.log(Object.values('abc')) // [ 'a', 'b', 'c' ]

3.2.Object的entries方法

通过Object.entries()可以获取到一个二维数组,数组中会存放可枚举的键值对数组,数组元素分别为对象的key和value。

const obj = {
  name: 'curry',
  age: 30,
  team: '勇士'
}

// 传入一个对象
console.log(Object.entries(obj)) // [ [ 'name', 'curry' ], [ 'age', 30 ], [ 'team', '勇士' ] ]

for (const [key, value] of Object.entries(obj)) {
  console.log(key, value)
  /*
    name curry
    age 30
    team 勇士
  */
}

// 传入一个数组,下标作为第一个元素
console.log(Object.entries(['curry', 'kobe', 'klay'])) // [ [ '0', 'curry' ], [ '1', 'kobe' ], [ '2', 'klay' ] ]

// 传入一个字符串,下标作为第一个元素
console.log(Object.entries('abc')) // [ [ '0', 'a' ], [ '1', 'b' ], [ '2', 'c' ] ]

3.3.字符串的填充

如果需要对字符串进行前后填充,来实现某种展示格式,ES8中提供了padStart和padEnd方法,分别可以对字符串进行首位填充。

const str = 'ssssssssss'

// 如果指定填充的位数小于等于str的长度,就不会进行填充
// 指定的位数需大于str的长度,就会填充str长度减去指定位数
// 如下指定了15,最终填充的只有5位,因为str的长度为10
console.log(str.padStart(15, '*')) // *****ssssssssss
console.log(str.padEnd(15, '-')) // ssssssssss-----

应用场景:字符串的填充可以用于对身份证、银行卡进行位数隐藏。

const bankCard = '2034399002125581'
// 截取银行卡后四位数组
const lastFourNum = bankCard.slice(-4)
// 将前面数组全部填充为*
const finalBankCard = lastFourNum.padStart(bankCard.length, '*')
console.log(finalBankCard) // ************5581

3.4.Object的getOwnPropertyDescriptors方法

Object.getOwnPropertyDescriptors()方法用来获取一个对象的所有自身属性的描述符。

const obj = {}

Object.defineProperties(obj, {
  name: {
    configurable: false,
    writable: false,
    enumerable: false,
    value: 'curry'
  },
  age: {
    configurable: true,
    writable: true,
    enumerable: true,
    value: 30
  }
})

console.log(Object.getOwnPropertyDescriptors(obj))

4.ES9相关知识点

4.1.对象的展开语法

在前面讲了,数组是可以使用展开语法的,在ES9中,在构建对象字面量时,也可以使用展开语法了。

const obj1 = {
  name: 'curry',
  age: 30
}

const newObj1 = { ...obj1, team: '勇士' }
console.log(newObj1) // { name: 'curry', age: 30, team: '勇士' }

注意:对象的展开运算符只能实现浅拷贝,如果对象内部还包含引用类型的值,就会指向同一地址空间。

const obj = {
  name: 'curry',
  age: 30,
  friends: ['kobe', 'klay']
}

const newObj = { ...obj }
newObj.friends.push('james')
console.log(newObj) // { name: 'curry', age: 30, team: '勇士' }
console.log(obj) // { name: 'curry', age: 30, friends: [ 'kobe', 'klay', 'james' ] }

5.ES10相关知识点

5.1.flat和flatMap

(1)flat方法

按照一个可指定的深度递归遍历数组,并将所有的元素和遍历到的子数组合并为一个新数组返回。(可用于数组扁平化)

const nums = [1, 2, [3, 3], [4, [5, 5]]]

// 对数组进行降维处理
console.log(nums.flat(1)) // [ 1, 2, 3, 3, 4, [ 5, 5 ] ]
console.log(nums.flat(2)) // [ 1, 2, 3, 3, 4, 5, 5 ]
// 如果不知道数组嵌套了多少层,可直接指定Infinity
console.log(nums.flat(Infinity)) // [ 1, 2, 3, 3, 4, 5, 5 ]

(2)flatMap方法

首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。

  • flatMap先进行map操作,然后做flat操作;
  • flatMap中的flat操作深度为1;
const arr = [[1, 2, 3, 4], ['a', 'b','c', 'd']]

// 数组自动降一维
const newArr = arr.flatMap(item => {
  return item
})

console.log(newArr) // [ 1, 2, 3, 4, 'a', 'b', 'c', 'd' ]

5.2.Object的fromEntries方法

在前面讲了可以通过Object.entries()将一个对象转换成键值对数组,而Object.fromEntries()可以将键值对列表转换成对象。

const entries = [
  ['name', 'curry'],
  ['age', 30]
]

const obj = Object.fromEntries(entries)
console.log(obj) // { name: 'curry', age: 30 }

应用场景:解析url的query部分,将其转成一个对象。

const url = 'http://127.0.0.1:8000/search?name=curry&age=30'

const queryStr = url.slice(url.indexOf('?') + 1, url.length)
console.log(queryStr) // name=curry&age=30

// URLSearchParams:可用于处理url的查询字符串
const queryParams = new URLSearchParams(queryStr)
console.log(queryParams) // URLSearchParams { 'name' => 'curry', 'age' => '30' }

const paramObj = Object.fromEntries(queryParams)
console.log(paramObj) // { name: 'curry', age: '30' }

5.3.去除字符串首尾空格

去除一个字符串首尾空格可以通过trim()方法,而trimStart和trimEnd方法分别可以单独去除字符串前或后的空格。

// 这里以·代表空格
const str = '····ssss···'
console.log(str.trim()) // ssss
console.log(str.trimStart()) // ssss···  
console.log(str.trimEnd()) // ····ssss

5.4.Symbol的description

在前面讲了Symbol类型,在创建一个Symbol类型的值时,还可以传入指定的描述符。

const s1 = Symbol('aaaa')
const s2 = Symbol('bbbb')

console.log(s1.description) // aaaa
console.log(s2.description) // bbbb

6.ES11相关知识点

6.1.BigInt类型

在JavaScript中,如果数字过大,可能是不正确的,先看看JavaScript提供给我们的最大值和最小值是多少。

const maxNum = Number.MAX_SAFE_INTEGER
const minNum = Number.MIN_SAFE_INTEGER

console.log(maxNum) // 9007199254740991
console.log(minNum) // -9007199254740991

如果给最大值再加大数值,很明显数值是不正确的:

console.log(maxNum + 1) // 9007199254740992
console.log(maxNum + 2) // 9007199254740992

所以,ES11引入了新的数据类型BigInt,可用于表示很大的整数:

  • BigInt的写法需要在数值后面加上n
  • 只有数值同为BigInt类型才能进行运算(不能进行隐式转换),如果需要运算的数据不是BigInt类型就要加上n转成BigInt;
  • 也可使用BigInt()方法进行转类型;
const bigInt = 9007199254740991n
console.log(bigInt + 2n) // 9007199254740993n
console.log(bigInt + bigInt) // 18014398509481982n

6.2.空值合并操作符(??)

如果||不能对空字符串和0进行很好的判断,就可以使用??

const test1 = ''
const test2 = 0
const test3 = null
const test4 = undefined

const res1 = test1 || 'default'
const res2 = test2 || 'default'
console.log(res1) // default
console.log(res2) // default

// ??认为空字符串和0是真值的
const res3 = test1 ?? 'default'
const res4 = test2 ?? 'default'
console.log(res3) // ''
console.log(res4) // 0

// 只有当??前面的值为null或者undefined,才会使用后面的值
const res5 = test3 ?? 'default'
const res6 = test4 ?? 'default'
console.log(res5) // default
console.log(res6) // default

6.3.可选链

可选链(Optional Chaining)的主要作用就是让代码在进行null和undefined判断时更加清晰和简洁,确保我们访问对象属性是安全的(因为从undefined里面取值是会报错的)。

const obj = {
  name: 'curry',
  friend: {
    name: 'kobe',
    age: 24
  }
}

// 先判断obj中是否有friend属性,然后再判断friend中是否有name属性
console.log(obj?.friend?.name) // kobe

// console.log(obj.teammate.name) // TypeError: Cannot read property 'name' of undefined
// obj中没有teammate属性,所以就直接返回undefined,不会再从undefined中取name了,不会报错影响后面程序运行
console.log(obj?.teammate?.name) // undefined

6.4.globalThis

在ES11之前获取JavaScript的全局对象在不同的环境下的获取方式不同:

  • 浏览器中:this、window获取;
  • node中:global获取;

ES11中,对我们获取全局对象进行统一规范:

console.log(globalThis)

浏览器中:

node中:

7.ES12相关知识点

7.1.FinalizationRegistry

FinalizationRegistry对象可以让你在对象被垃圾回收时请求一个回调。

  • FinalizationRegistry提供了这样的一种方法:当一个在注册表中注册的对象被回收时,请求在某个时间点上调用一个清理回调;(清理回调有时被称为 finalizer )
  • FinalizationRegistry可实例化一个对象,可以借助该对象注册想要清理回调的对象,传入该对象和所含的值;
let obj1 = { name: 'curry', age: 30 }
let obj2 = { name: 'kobe', age: 24 }

// 实例化注册表
const register = new FinalizationRegistry(value => {
  console.log(`${value}对象被销毁了`)
})

// 注册obj1和obj2,并指定其值
register.register(obj1, 'obj1')
register.register(obj2, 'obj2')

// 将两个对象销毁
obj1 = null
obj2 = null

需要借助浏览器的GC来进行测试,当两个对象被真正回收了,就会调用清理回调,打印对应内容:

7.2.WeakRef

在前面讲了WeakSet和WeakMap中的元素对对象的引用都是弱引用,WeakRef就可以专门实现对一个对象进行弱引用,也就是说该对象被GC回收时,是不会看它是否有弱引用的,有没有弱引用一样被回收。

可以结合上面的FinalizationRegistry进行测试:

  • WeakRef创建的弱引用需通过deref()来访问对象属性;
let obj = { name: 'curry', age: 30 }

// 创建一个弱引用weakObj指向对象{ name: 'curry', age: 30 }
let weakObj = new WeakRef(obj)

// 拿到{ name: 'curry', age: 30 }对象
// console.log(weakObj.deref())
// console.log(weakObj.deref().name) // curry
// console.log(weakObj.deref().age) // 30

const register = new FinalizationRegistry(value => {
  console.log(`${value}对象被销毁了`)
})
register.register(obj, 'obj')

// 去掉obj对对象{ name: 'curry', age: 30 }的引用
obj = null

// 等对象回收后再打印
setTimeout(() => {
  // 注意:当弱引用的对象被GC回收后,weakObj.deref()的值为undefined
  // 如果不想报错,可以使用可选链或者逻辑与
  console.log(weakObj.deref()?.name)
  console.log(weakObj.deref() && weakObj.deref().name)
}, 10000)

7.3.逻辑赋值运算

ES12为我们提供了三种逻辑赋值运算:

  • ||=:逻辑或赋值运算;

    let msg = undefined
    msg ||= 'default'
    console.log(msg) // default
    /*
      等同于:
      msg = msg || 'default'
    */
    
  • &&=:逻辑与赋值运算;

    let obj = {
      name: 'curry',
      age: 30
    }
    // 在obj有值的情况下,将obj赋值为obj.name
    obj &&= obj.name
    console.log(obj) // curry
    /*
      等同于:
      obj = obj && obj.name
    */
    
  • ??=:逻辑空赋值运算;

    let msg = 0
    // 当msg为null或undefined情况下,将msg赋值为default,这里为0,所以值还是0
    msg ??= 'default'
    console.log(msg) // 0
    /*
      等同于:
      msg = msg ?? 'default'
    */
    

注意:该新增特性可能过新,可能出现node版本不支持的情况,可以在浏览器中打印查看。

7.4.数值分隔符

当数值过长时,ES12允许我们使用_进行连接,方便阅读。

const num = 100_000_000
console.log(num) //100000000

7.5.字符串替换

ES12提供了一个replaceAll方法,该返回一个新字符串,新字符串所有满足 pattern 的部分都已被replacement 替换。pattern可以是一个字符串或一个RegExpreplacement可以是一个字符串或一个在每次匹配被调用的函数。

const newStr = str.replaceAll(regexp|substr, newSubstr|function)

将所有的a替换为b:

const str = 'ababababab'

// 字符串
const newStr1 = str.replaceAll('a', 'b')
// 正则
const regex = /a/g
const newStr2 = str.replace(regex, 'b')

console.log(newStr1) // bbbbbbbbbb
console.log(newStr2) // bbbbbbbbbb

注意:该新增特性可能过新,可能出现node版本不支持的情况,可以在浏览器中打印查看。

本篇文字只总结了ES6-ES12部分简单知识点,还有一些其它ES6之后新增的特性,如Promise、迭代器、生成器、async、await等等,将会在后续的文章中进行整理。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK