数据劫持的好帮手 -Proxy

前言

小伙伴们大家好。在ES5中有个Object.defineProperty()函数可以用于对对象的属性进行劫持,或者给对象新增一个属性等操作。看过Vue2.0源码的小伙伴应该都知道,其中数据响应式原理就用到了Object.defineProperty这个方法对属性进行了劫持处理。但是我们今天的重点不是Object.defineProperty,而是ES6中为我们新提供的一个类 - Proxy。之所以先提到Object.defineProperty函数,是因为我们接下来要分享的Proxy提供的有些功能跟Object.defineProperty类似,甚至更加强大!

Proxy是个啥

  • Proxy这个词翻译过来是代理的意思。而在我们这里用它来表示“代理”某些操作,比如前言中提到的“数据劫持”。可以理解为“代理器”。利用Proxy可以在目标对象之前架设一层“拦截”,当外界对该对象访问时,都必须要先通过这层拦截。这个时候我们就可以利用这个拦截对外界的访问进行一些必要的验证和限制了。
  • Proxy是ES6中的一个构造函数,我们使用时需要通过new关键字来创建Proxy的实例
  • Proxy构造函数接收两个参数:target和handler,target表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截的行为,比如重写get或set函数。
  • 如果第二个参数handler不传或者传一个空对象,则相当于没有进行拦截,访问Proxy实例就相当于访问的是原目标对象target
  • Proxy的实例可以用来作为其他对象的原型
  • 同一个拦截器(即第二个参数handler)可以同时设置多个拦截操作
  • 需要注意的是:要使得Proxy起作用,必须要针对Proxy的实例进行对象的相关操作,而不能针对原目标对象target进行操作,否则拦截是无效的
let obj = {
    
    name: "Yannis"}
let proxy = new Proxy(obj,{
    
    
	get(target, key){
    
    
		console.log('get')
		return 'Alvin'
	},
	set(target,key,val){
    
    
		console.log('set')
		target[key] = val
	}
});
//操作原始对象,拦截规则不会生效
obj.name // 'Yannis'
obj.name = 'Alvin'
obj.name // 'Alvin'(obj的name已经被重新赋值)

//操作Proxy实例,拦截规则生效
proxy.name // 'Alvin' 'get'(拦截器输出)
proxy.name = 'Yannis' // 'set'(拦截器输出)
proxy.name //'Alvin' 'get' //这里总是输出“Alvin”因为在get拦截里return的是“Alvin”

//Proxy实例作为其他对象的原型对象
let proxy = new Proxy({
    
    },{
    
    
	get(){
    
    
		return "Yannis"
	}
});

let obj = Object.create(proxy);
obj.name;//'Yannis'
obj.count;//'Yannis'
obj.time;//'Yannis'
//obj上并没有这三个属性,但访问时都返回了'Yannis',这就是因为obj对象通过原型链找到了proxy,从而进入了拦截器中,所以不管访问obj的什么属性,始终都会返回'Yannis'

Proxy可拦截的操作

上面的代码中我们只是演示了Proxy中get和set的拦截操作,其实不仅这两个可以拦截,Proxy一共为我们提供了13个可拦截的操作函数:

  • get(target, key, receiver):拦截对象属性的读取操作
  • set(target, propKey, value, receiver):拦截对象属性的设置操作
  • has(target, propKey):拦截判断属性是否存在的操作,如propKey in proxy,返回一个布尔值。
  • deleteProperty(target, propKey):拦截删除属性的操作,如delete proxy[propKey]返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in循环,返回一个数组
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey)
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs)
  • preventExtensions(target):拦截Object.preventExtensions(proxy)
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy)
  • isExtensible(target):拦截Object.isExtensible(proxy)
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto)
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。

总结

关于Proxy就分享到这里。另外Vue3.0中对数据响应式做了优化,用的就是Proxy代替Object.defineProperty()。

喜欢的小伙伴欢迎点赞留言加关注哦!

猜你喜欢

转载自blog.csdn.net/lixiaosenlin/article/details/120890240
今日推荐