《十二》ES6+ 中的 Proxy

ES6 中新增了一个 Proxy 类,Proxy 的原意是代理,用在这里表示由它来代理某些操作,可以译为代理器。可以理解成,在目标对象之前架设一层拦截,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

通过 Object.defineProperty() 也可以实现监听对象属性的操作 。

let person = {name: 'Lee'}
let _name = person.name
Object.defineProperty(person, 'name', {
	get: function() {
		console.log('监听获取值')
		return _name
	},
	set: function(newVal) {
		console.log('监听设置值')
		_name = newVal
	}
})

console.log(person.name)
person.name = 'Mary'

请添加图片描述
但是是存在弊端的:首先,Object.defineProperty() 设计的初衷并不是为了去监听一个完整对象中所有的属性;其次,如果想监听更加丰富的操作,比如新增属性、删除属性、是否存在属性等,是无法做到的。

Vue2 就是通过 Object.defineProperty() 来实现响应式原理的,但在 Vue3 中改为了通过 Proxy 来实现响应式原理。

创建 Proxy 实例:

如果想要监听一个对象的相关操作,可以先通过 new Proxy() 来创建一个代理对象,之后对目标对象的所有操作,都通过代理对象来完成,代理对象可以监听对目标对象对象进行了哪些操作,并自动反馈给目标对象。

new Proxy() 接收两个参数,第一个参数表示所要拦截的目标对象;第二个参数是一个配置对象,用来定制代理行为,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。

var target = {};
var handler = {
  get: function(target, propKey) {
    return 35
  }
}
// 创建一个代理对象
var proxy = new Proxy(target,handler)

// 对目标对象的所有操作,都要通过代理对象来进行,代理对象会自动反馈给目标对象
proxy.name // 35
proxy.age // 35

如果配置对象是一个空对象,没有设置任何拦截,那就等同于直接通向源对象,访问代理对象就等同于访问目标对象。

var target = {};
var handler = {};
var proxy = new Proxy(target, handler)
proxy.name = 'Lee'
target.name // Lee

Proxy.revocable():

Proxy.revocable()方法返回一个可取消的Proxy实例。该方法返回一个对象,该对象的proxy属性是proxy实例,revoke属性是一个函数,可以取消Proxy实例。

let target = {};
let handler = {};

let {proxy, revoke} = Proxy.revocable(target, handler);

proxy.foo = 123;
proxy.foo // 123

revoke();
proxy.foo // TypeError: Revoked

Proxy.revocable()的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。

this问题:

虽然Proxy可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。

主要原因就是在Proxy代理的情况下,目标对象内部的this关键字会指向Proxy代理。

const target = {
  m: function () {
    console.log(this === proxy);
  }
};
const handler = {};

const proxy = new Proxy(target, handler);

target.m() // false
proxy.m()  // true

上面代码中,一旦proxy代理target.m,后者内部的this就是指向proxy而不是tarhet。

捕获器:

如果想要监听某些具体的操作,可以在配置对象中添加对应的捕获器。

  1. get():属性读取操作的捕获器。用来监听读取属性的操作,默认会接收到三个参数,依次为目标对象,被获取的属性名 key 和代理对象。
    let person = {name: 'Lee'}
    let proxy = new Proxy(person, {
    	// 读取属性时自动执行
    	get: function(target, key, receiver){
    		return target[key]
    	}
    })
    
    proxy.name //Lee
    
  2. set():属性设置操作的捕获器。用来监听设置属性的操作,默认会接收到四个参数,依次为目标对象、被设置属性名 key、被设置的属性值和代理对象。
    let person = {name: 'Lee'}
    let proxy = new Proxy(person, {
    	get: function(target, key, receiver){
    		return target[key]
    	},
    	// 设置属性时自动执行
    	set: function(target, key, value, receiver){
    		target[key] = value
    	},
    })
    
    proxy.name = 'Mary'
    proxy.name // Mary
    
  3. deleteProperty():delete 操作符的捕获器。用来监听删除属性的操作。
    let person = { name: 'Lee' }
    let proxy = new Proxy(person,  {
      // 删除属性时自动执行
      deleteProperty (target, key) {
        delete target[key]
      }
    })
    delete proxy.name
    
  4. has():in 操作符的捕获器。用来监听判断属性是否存在的操作。
  5. apply():函数调用操作的捕获器。用来监听函数的调用。
  6. construct():new 操作符的捕获器。用来监听 new 操作。
  7. ownKeys()Object.getOwnPropertyNames()Object.getOwnPropertySymbols() 方法的捕获器。
  8. defineProperty()Object.defineProperty() 方法的捕获器。
  9. getOwnPropertyDescriptor()Object.getOwnPropertyDescriptor() 方法的捕获器。返回值是一个属性描述对象或者 undefined。
  10. getPropertyOf()Object.getPropertyOf() 方法的捕获器。用来监听获取对象的隐式原型。返回值必须是对象或者null,否则报错。
  11. setPropertyOf()Object.setPropertyOf() 方法的捕获器。用来监听获取对象的隐式原型。返回值必须是布尔值,否则会被自动转为布尔值。
  12. isExtensible()Object.isExtensible() 方法的捕获器。返回值只能是布尔值,否则会被自动转为布尔值。
  13. preventExtensions()Object.preventExtensions() 方法的捕获器。返回值只能是布尔值,否则会被自动转为布尔值。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值