1.解释JavaScript中的Proxy对象是什么以及它是如何工作的,用于哪些场景?
- Proxy 是ES6引入的一种新特性,允许我们创建一个代理对象来拦截并定制对另一个对象的基本操作。比如获取、设置、删除数据等。
- Proxy 通过一个构造函数创建,接受两个参数:
target:希望代理的目标对象
handler:一个对象,里面包含了一些“捕捉器”(traps),这些捕捉器定义了我们希望拦截的操作,以及我们想要如何处理这些操作。 - 当对代理对象操作时,这些操作会被代理捕捉器拦截,并根据捕捉器的定义进行处理。
- Proxy 的应用场景非常广泛,举两个例子:
Vue3中的响应式系统:
* Vue3源码中,为了做到监听对象的响应式,必须监听对象的改变,根据改变更新DOM;
* 在Vue2中使用的是Object.defineProperty,但它设计的并不是为了监听对象属性;
* 所以在Vue3中对于对象的监听使用Proxy代理对象; - 封装event-store:
可在多个页面、组件共享;
使用Proxy代理监听传入的对象,当数据发生改变时,可通知页面或者组件数据的改变;
其他页面、组件根据数据改变做出响应,比如更新页面、网络请求等;
总之,它提供了对 对象的操作进行拦截和定制的能力。
2.比较Proxy和Object.defineProperty之间的区别?
它们的设计目的是不一样的:
* Object.defineProperty 在设计时并不是为了做数据监听的,它的设计目的主要是为了提供对 对象属性的精细控制;
* Object.defineProperty 允许我们详细的定义或修改对象的属性描述符,包括属性的可写性(writable)、可枚举性(enumerable)、可配置性(configurable)等。通过这些描述符,我们可以对对象的属性行为进行更细粒度的控制;
* 而Proxy设计的目的就是为了帮助我们可以代理某一个对象,并监听、拦截对象的操作;
在功能上来说,确实都可以实现监听的功能:
* Object.defineProperty是ES5引入的,它可以通过设置getter和setter方法来拦截和自定义属性的访问和修改行为。但它仅限于对单个属性拦截,无法作用于整个对象。
* 另外它可以拦截的功能是有限的,不能拦截属性的删除、新增、遍历等操作。
* Proxy是ES6引入的,可以用于创建一个代理对象,全面拦截对另一个对象的操作。
* Proxy提供了多种捕捉器(如get、set、has、deleteProperty),这些捕捉器覆盖了几乎所有对象操作。
在性能方面,不同场景优势是不同的:
* 由于Object.defineProperty直接操作对象的属性并且不需要引入额外的中间对象,在单一属性的操作时通常性能更好
* Proxy由于引入了代理层,会在每次操作时触发捕捉器,这会带来一定的性能开销。
* 但Proxy的灵活性和全面性在很多情况下可以弥补性能上的损耗,尤其在需要全局监听对象行为的场景中。
应用场景:
* Object.defineProperty适用于只拦截和修改某个或者某几个属性的场景。不需要代理整个对象。兼容性更好支持ES5及以上
* Proxy适用于需要拦截和定制对象几乎所有操作的场景
* 总之,Object.defineProperty适用于简单、局部的属性控制,性能相对较好,兼容性更强。Proxy提供了更为强大的对象操作拦截能力。
3.Map和WeakMap有什么不同?
-
Map是一种键值对集合,可以使用任何类型的值作为键。
-
它的键是强引用的,意味着只要在Map中存在对某个对象的引用,那么这个对象就不会被垃圾回收。
-
只要这个对象是Map的键,即使不再使用它,就不会被回收,可能导致内存泄露。
-
Map可迭代,可通过for...of遍历。
-
WeakMap也是一种键值对集合,但它的键必须是对象,不能是基本类型。
-
WeakMap中的键是弱引用的,如果某个对象只是WeakMap的键,并没有其他强引用,它就会被垃圾回收机制回收。
-
这使得WeakMap非常适合存储那些临时对象。
-
WeakMap不可迭代,无法通过for...of或者其他方式遍历。
Vue中使用WeakMap作为组件属性对象的键,当组件销毁时,组件的属性对象自动销毁。
4.Reflect是什么?
- Reflect是ES6新增的API,它是一个对象,字面的意思是反射。
- 它主要提供了很多操作JavaScript对象的方法,有点像Object中操作对象的方法。
- 比如Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf()
- 比如Reflect.defineProperty(target, propertyKey, attributes) 类似于 Object.defineProperty()
既然Object中就有这些方法,为什么还要用到Reflect? - 因为早期ECMA规范中没有考虑到如何对对象本身的操作更规范,所以将这些API放到了Object上。
- 但Object作为一个构造函数,将这些操作放到它身上并不合适。
- 另外还包含一些类似in、 delete操作符,让JS看起来很奇怪。
- 所以在ES6中新增了Reflect,把这些操作集中在这上面。
- 另外在使用Proxy时,可以做到不直接操作原对象。