Symbol
Set 和 Map 数据结构
Proxy
Reflect
10. Symbol
作为属性名的 Symbol
实例:消除魔术字符串
属性名的遍历
Symbol.for(),Symbol.keyFor()
实例:模块的 Singleton 模式
内置的 Symbol 值
Symbol,表示独一无二的值
注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性
1 | let s1 = Symbol('foo'); |
常用来设定唯一的属性或者方法名 : (注意,Symbol 值作为对象属性名时,不能用点运算符, 同理,在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中)
1 | let mySymbol = Symbol(); |
Symbol 作为属性名,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名
由于以 Symbol 值作为名称的属性,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。
另一个新的 API,Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。1
2
3
4
5
6
7
8let obj = {
[Symbol('my_key')]: 1,
enum: 2,
nonEnum: 3
};
Reflect.ownKeys(obj)
// ["enum", "nonEnum", Symbol(my_key)]
对象的Symbol.iterator属性,指向该对象的默认遍历器方法 :
1 | class Collection { |
symbol 还有11个其他的属性, 很少用得到
11. Set 和 Map 数据结构
Set 本身是一个构造函数,用来生成 Set 数据结构, Set 结构不会添加重复的值
1 | // 例一 |
WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中, 因此,WeakSet 适合 临时存放 一组对象
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适 (如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名)
WeakMap 就是为了解决这个问题而诞生的,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用, WeakMap结构有助于防止内存泄漏
1 | let map = new Map(); |
12. Proxy
Proxy 代理
1 | //定义被监听的目标对象 |
Proxy 代理器 : 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
1 | var obj = new Proxy({}, { |
1 | var proxy = new Proxy(target, handler) |
Proxy 支持的拦截操作一览(一共13个方法) :
1 get(target, propKey, receiver) 拦截对象属性的读取,比如proxy.foo和proxy[‘foo’]
2 set(target, propKey, value, receiver) 拦截对象属性的设置,比如proxy.foo = v或proxy[‘foo’] = v,返回一个布尔值; set方法的第四个参数receiver,指的是原始的操作行为所在的那个对象,一般情况下是proxy实例本身
3 has(target, propKey) 拦截propKey in proxy的操作,返回一个布尔值
4 getOwnPropertyDescriptor(target, propKey) 拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象
5 defineProperty(target, propKey, propDesc) 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值
6 apply(target, object, args) 拦截 Proxy 实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…)
7 construct(target, args) 拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)
如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则通过 Proxy 对象访问该属性会报错
1 | const target = Object.defineProperties({}, { |
has()
has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是 in 运算符
construct()
construct方法用于拦截new命令,可以接受两个参数
target:目标对象
args:构造函数的参数对象
newTarget:创造实例对象时,new命令作用的构造函数
1 | var p = new Proxy(function () {}, { |
defineProperty()
defineProperty方法拦截了Object.defineProperty操作
1 | var handler = { |
1 | let target = { |
Proxy.revocable()
Proxy.revocable方法返回一个可取消的 Proxy 实例
1 | let target = {}; |
this 问题
在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理
1 | // 下面是一个例子,由于this指向的变化,导致 Proxy 无法代理目标对象 |
实例:Web 服务的客户端
Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端, 同理,Proxy 也可以用来实现数据库的 ORM 层
1 | function createWebService(baseUrl) { |
13. Reflect
Reflect 概述: 用来使操作对象更加方便
1.将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上
2.修改某些Object方法的返回结果,让其变得更合理
3.让Object操作都变成函数行为
4.Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法
1 | var loggedObj = new Proxy(obj, { |
静态方法
Reflect对象一共有 13 个静态方法,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的
Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
1 | var myObject = { |
实例:使用 Proxy 实现观察者模式
下面,使用 Proxy 写一个观察者模式的最简单实现,即实现observable和observe这两个函数。思路是observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数
1 | const queuedObservers = new Set(); |