ES6的改版,主要是通过引入JAVA等静态语言的优秀思想来解决老版本的一些痼疾,如作用域,回调,继承和封装等问题。这些改革措施是非常成功的,ES6让JS真正变成了一种好用的语言。这里是关于Reflect和Proxy。
Set
之前的JS只提供了两种容器:数组
和对象
。es6又添加了另一种非常有用的数据结构:Set
。他就是集合
的意思,本身也是一种容器。
既然是容器,肯定有对应的增查改删的操作了。增查改删是容器必备的职能。那么这种新式容器,到底有什么特点呢?
拿他跟数组比较的话,Set
的最大的特点就是每个值都是唯一的,不能有重复值
。经常会遇到数组去重的问题吧?自从有了Set,就开始变得美好了。
1 | let container = new Set([1, 2, 2, 3, 4, 4]);//可以直接传入数组进行初始化 |
Set的构造方法可以直接传数组进行初始化,当然也可以这样声明:let mySet = new Set();
Set跟数组极为类似,二者可以互相转换着用。有了它,就可以轻松的解决数组去重问题了。
1 | //简单去重方案 |
这并不适用于含有重复对象的数组
,因为对象是比较的是内存地址。1
2
3console.log({}=={});//false,内存地址不同
let arr = [{},{},{}];
console.log([...new Set(arr)]);//对象是无法去重的
对象数组到底如何去重呢?
一种办法是你需要递归比较,两个对象的每一个值是否相等,不等的放入一个新数组中,相等的跳过。但是这个方法很普通,可以利用Set
的不重复特性,把对象先转成字符串存入Set
,去掉重复值后,再转回来就好。
1 | function unique(arr){ |
新数据结构:Map
这个是Map
容器,而不是前面的map()
方法,千万别弄混了(注意大小写)!Map
就是键值对容器,跟Object
非常类似。它有什么特别之处呢?
其实Object
有个很大的问题:就是键只能用字符串,而不能使用其他数据类型。
如果让键不限于字符串,而是各种数据类型呢?比如num?
1 | <!DOCTYPE html> |
一般来说,对象的键应该是唯一的。Object的键因为是字符串,所以没啥问题,但Map既然放开了类型限制,这就得注意了
1 |
|
因为对象比较的是内存地址,所以['a']
作为新开辟的空间,无法在Map容器
中找到。
再来看一下Map的增查改删,非常简单。
1 | //利用二维数组,可以构建Map |
获取的keys集合是MapIterator
类型的,这叫遍历器。顾名思义,就是专供遍历用的一种数据结构
Reflect是面镜子
Reflect
就是反射的意思,那究竟反射啥呢?
大家知道镜子可以反射吧?Reflect
就是一面镜子。那是谁照镜子呢?就是Object
。
也就是说,Object
把自己的属于语言内部的方法(比如Object.defineProperty)照到了Reflect
上。
1 | <!DOCTYPE html> |
Reflect
一共是13个静态方法,就是13种武器哈:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 |
|
他与Proxy
结合使用更合理。
Proxy
Proxy
就是拦截器,也可以译为代理器,跟设计模式的代理模式差不多。他跟Reflect
是一一对应的。
只要Proxy
对象有的方法,Reflect
对象上都有。当然咯,Proxy
没有的,Reflect
也有。
拦截器本身也是对象,他的主要作用在于拦截。看一下语法定义:var proxy = new Proxy(target, handler);
1.target就是目标对象,我们要给哪个对象加拦截?
2.handler就是处理对象,他是一系列拦截操作的集合。这些拦截操作,就是对应的Reflect具有的十三种静态方法(所谓的一一对应其实就是说的这个)。注意,只有这十三种武器,不支持其他的。
拿最简单的set方法为例:
1 | //形参说明: |
createValidator
这个函数,返回的是Proxy拦截器对象。这个拦截器对象,对原Person类的set操作做了修改,加入了一些验证方案,其实就是起到了包装增强的作用,让Person具有了可验证字段类型的功能。
再来看下Reflect.apply
的用法。这个方法的使用频率还是蛮高的
1 | //假如这个foo函数是同事张三写的 |
结语
Proxy
是用于修改语言内部行为的机制,因为Object
对象内部的方法大多属于这种情况,所以handler
的配置,又跟Reflect
紧密联系在一起(因为Reflect是Object的镜子嘛)。
应该说,Reflect
和Proxy
之间的关系还是不太容易理顺的。