ES6知识点记录

标签模板字符串

查看

模板字符串的语法为``,其中用${}符号可以传入变量,最终拼接为一个字符串,但``还可以用于函数的调用,此用法称为标签模板字符串(Tagged Template Literals)。

通过标签模板字符串调用的函数,会将字符串放在一个数组中,位于函数参数的第一位,如果其中用到${}符号,会将其作为分割符,分割的字符串每一部分作为数组的一项值,而${}中的表达式结果会作为函数参数,从第二个参数依次传入。

1
2
3
4
5
6
7
8
function foo(...args){
console.log(args) // [[ 'this is ', ' tagged ', ' template ', ' literals' ], 1, 'bar', 'baz']
}

const bar = 'bar'
const baz = 'baz'

foo`this is ${1} tagged ${bar} template ${baz} literals`

使用场景:React的styled-componetns库中就用到了标签模板字符串。

函数的默认参数

查看

ES6之后函数的参数可以设置默认值,设置默认值的参数和剩余参数不会记录到Funciton.length中。

推荐将设置默认值的参数放在普通参数后面,剩余参数前面。

因为对于某些语言,设置默认值的参数放在普通参数前会报错。如python,C++等。

函数箭头函数的补充

查看

由于箭头函数没有显式原型prototype,所以不能作为构造函数,使用new创建对象。

箭头函数也不存在this,arguments,super参数。

Symbol的基本使用

查看

我们需要先思考一下Symbol的出现解决了哪些问题?

在ES6之前,对象的属性名都是字符串形式,很容易造成属性覆盖,尤其是在引入外部js文件是,我们很大可能不知道引入的对象存在的属性,一旦对其进行修改,可能会造成意想不到的bug。

而Symbol的出现,解决了这个问题。Symbol可以用来生成一个独一无二的值,并且可以作为字符串的键值。

Symbol的基本使用

1
2
const s1 = Symbol()
const s2 = Symbol('s2')

对于对象中的Symbol键值是不能通过Object.keys()获取的,但是可以通过Object.getOwnPropertySymbols()获取(只能获取属性为Symbol的键值),且返回的是一个可迭代对象,需要通过迭代才能获取到对应的值。

1
2
3
4
5
6
7
8
9
10
11
const obj = {
[Symbol()]: 1,
name: 'hello'
}

console.log(Object.keys(obj)) // ['name']
const symbolKeys = Object.getOwnPropertySymbols(obj)

for (const key of symbolKeys) {
console.log(key, obj[key]) // Symbol() 1
}

前面通过Symbol()创建的都是独一无二的值,但我们可不可以创建一个重复的Symbol值?

需要用到Symbol.for()方法,此方法可以传入一个描述参数,只有传入相同的描述参数,才会返回同一个值。且只有同样使用Symbol.for()创建的Symbol值,且描述参数相同,返回的值才相同。

1
2
3
4
5
6
7
const s1 = Symbol.for()
const s2 = Symbol.for()
const s3 = Symbol.for('1')
const s4 = Symbol.for('1')
console.log(s1 === s2) // true
console.log(s1 === s3) // false
console.log(s3 === s4) // true

并且我们可以通过Symbol.keyFor()方法来获取Symbol的值对应的描述参数,且只有通过Symbol.for()方法创建的Symbol,才能用Symbol.keyFor()方法获取到对应的描述参数。

如果是通过Symbol()创建的Symbol,则需要用创建的Symbol的description属性来获取对应的描述参数。此属性对用通过Symbol.for()方法创建的Symbol也可用。

1
2
3
4
5
const s1 = Symbol.for('111')
const s2 = Symbol.for('222')
const s3 = Symbol('333')
console.log(Symbol.keyFor(s1), Symbol.keyFor(s2), Symbol.keyFor(s3)) // 111 222 undefined
console.log(s1.description, s2.description, s3.description) // 111 222 333

Set、WeakSet、Map、WeakMap

查看

ES6新增了两种数据结构,分别是Set和Map,以及他们的另外形式WeakSet和WeakMap。

Set

Set和数组类似,可以用于保存数据,但是其中的元素不能重复,所以Set可以用于数组去重。

Set的属性和方法:

  • 属性

    • size:返回Set中元素的个数。
  • 方法

    • add(value):向Set中添加元素,返回Set本身。
    • delete(value):从Set中删除于value值相等的元素,返回boolean类型。
    • has(value):判断set中是否存在某个元素,返回boolean类型。
    • clear():清空Set中所有的元素,没有返回值。
    • forEach(callback, [, thisArg]):通过forEach遍历set。
  • Set支持使用for of的遍历。

WeakSet

与Set类似的有Weakset,也是内部不能重复的数据结构。

但区别为,WeakSet中只能存放对象,如其中存储的对象为弱引用,即如果没有其他引用对其中存储的某个对象有引用,那么GC可以对该对象回收。

  • 方法

    • add(value):向WeakSet中添加元素,返回WeakSet本身。
    • delete(value):从WeakSet中删除于value值相等的元素,返回boolean类型。
    • has(value):判断WeakSet中是否存在某个元素,返回boolean类型。

WeakSet不能遍历,因为对其中存储的仅仅是弱引用,不一定什么时候就被回收了,可能会获取不到。

Map

Map用于存储映射关系,和对象类似,但对象的键只能为字符串或Symbol,如果设定键为对象,会将其转为字符串,导致键值变为{object Object】的形式,但Map中的键可以设置对象类型。

Map的基本使用

1
2
const map = new Map([['name', 'zhangsan'], ['age', 18], [() => { }, 22]])
console.log(map) // Map(3) {'name' => 'zhangsan', 'age' => 18, ƒ => 22}

常用方法:

  • 属性

    • size:返回Map中元素的个数。
  • 方法

    • set(key, value):向Map中添加元素,返回Map本身。
    • get(key):获取与key值相等的元素对应的value值,返回value值。
    • delete(key):从Map中删除与key值相等的元素,返回boolean类型。
    • has(key):判断Map中是否存在某个元素,返回boolean类型。
    • clear():清空Map中所有的元素,没有返回值。
    • forEach(callback, [, thisArg]):通过forEach遍历Map。

Map也是可迭代对象,支持for of的遍历。

WeakMap

WeakMap与Map类似,但其中存储的键值只能为对象类型,且也是弱引用。

常见方法:

  • set(key, value):向WeakMap中添加元素,返回WeakMap本身。

  • get(key):获取与key值相等的元素对应的value值,返回value值。

  • delete(key):从WeakMap中删除与key值相等的元素,返回boolean类型。

  • has(key):判断WeakMap中是否存在某个元素,返回boolean类型。

WeakSet与WeakMap的弱引用

对于WeakSet和WeakMap,是ES6之后推出的数据结构,用于实现对对象的存储,但对对象的引用是弱引用。

弱引用是即使存在对对象的引用,但当被引用对象不存在强引用时,会被标记为可回收状态,随后被GC(垃圾回收器)回收。

至于对对象不存在强引用的状态,可以分为两种:

  • 当对象离开作用域时

  • 但对对象的引用赋值为null时

假设不存在闭包,当代码块中创建了对象,并用WeakSet或WeakMap存储,当代码块执行完毕,EC弹出ECS,且代码块中的对象没有被其他变量引用,此时便标记为为回收状态,存在闭包时,如果对函数返回的闭包状态进行了变量接受,此时对其时强引用,只能手动赋值为null,此时才可被回收。

WeakSet和WeakMap的引用场景:

  • 跟踪对象状态

  • 添加临时对象

  • 减少内存泄漏

跟踪对象状态这一块,能做的操作很多,通过对其的get、set、has方法的组合使用,可以保证对象可用,类似的操作也可以用于避免DOM的事件重复绑定和缓存操作等。

FinalizationRegistry

查看

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

  • FinalizationRegistry 提供了这样的一种方法:当一个在注册表中注册的对象被回收时,请求在某个时间点上调用一个清理回调。(清理回调有时被称为 finalizer )。

  • 你可以通过调用register方法,注册任何你想要清理回调的对象,传入该对象和所含的值。

1
2
3
4
5
6
7
8
9
10
11
let obj = { name: 'obj' }

const weakSet = new WeakSet([obj])

const registry = new FinalizationRegistry((value) => {
console.log(`${value} is dead`)
})

registry.register(obj, 'obj')

obj = null

WeakRefs

查看

如果我们默认将一个对象赋值给另一个引用,那么这个对象是一个强引用。如果我们希望是一个弱引用的话,可以使用WeakRef。

1
2
3
4
5
6
7
8
9
10
let obj = { name: 'obj' }

let info = new WeakRef(obj)
const registry = new FinalizationRegistry((value) => {
console.log(`${value} is dead`)
})

registry.register(obj, 'obj')

obj = null

method.at()

查看

ES13引入了at方法,用于获取数组和字符串中指定位置的元素或字符。

1
2
3
4
5
const arr = ['1', '2', '3']
const str = '123'

console.log(arr.at(0), str.at(0)) // 1 1
console.log(arr.at(-1), str.at(-1)) // 3 3

Object.hasOwn(obj, propKey)

查看

ES13在Object中新增了hasOwn静态方法,用于判断一个对象中是否存在某个属性,不包括原型链。

1
2
3
4
5
6
7
const obj = { name: 'obj' }

Object.prototype.age = 18

console.log(obj.name, obj.age) // obj 18
console.log(Object.hasOwn(obj, 'name'), Object.hasOwn(obj, 'age')) // true false
console.log(obj.hasOwnProperty('name'), obj.hasOwnProperty('age')) // true false

Object.hasOwn和Object.prototype.hasOwnProperty的区别:

  • 一个是在静态方法上,一个是在原型对象上。

  • hasOwnProperty在原型对象上,很容易被对象内部重写,因为可能有人不知道有这个方法,从而自定义了一个,对后续造成了影响。

  • 对于隐式原型指向null的对象,hasOwnProperty无法进行判断。

    • 因为hasOwnProperty是通过原型链找到Object的原型对象调用的,一旦将对象的移除指向Objetc的原型对象时,那么hasOwnProperty就无法调用了。
1
2
3
4
5
const obj = { name: 'obj' }
obj.__proto__ = null

console.log(Object.hasOwn(obj, 'name')) // true
console.log(obj.hasOwnProperty('name')) // 报错