JavaScript红宝书第九章:代理与反射

前言

本文主要讲解代理与反射,那么好,本文正式开始。

代理

代理基础

代理是目标对象的替身,同时它又独立于目标对象,意思就是说,我们在操作对象的时候,直接操作或通过操作代理的方式间接操作目标对象。

创建空代理

使用proxy构造函数创建代理,proxy有两个必要参数:
1,目标对象。2.处理程序
语法格式:const proxyObj=new Proxy(target,handle)
举例:

			const obj={id:"target"};
			const handle={}
			const proxy=new Proxy(obj,handle)
			console.log(obj.id,proxy.id);//target target
			
			obj.id='tar'
			console.log(obj.id,proxy.id);//tar tar
			
			console.log(obj.hasOwnProperty('id'),proxy.hasOwnProperty('id'));//true true
			
			// console.log(obj instanceof object,proxy instanceof Proxy);
			
			console.log(obj===proxy);//false

在上述代码中,实现了一个简单的代理,这里用的程序处理handle为空,我们既可以用obj.id也可以用proxy.id都能得到target这个值。

可以用hasOwnProperty检测出对象的具体属性

Proxy.prototypeundefined,因为 Proxy 是一个构造函数,它的原型对象是通过 Object.create(null) 创建的。这意味着 Proxy.prototype 没有继承自任何其他对象,因此它是独立的。

可以用严格相等检测出代理和目标对象的不同。

定义捕获器

捕获器是代理的重要概念之一,它作用是可以在设置捕获的某个路径中进行程序处理。实现捕获。
举例:比如我们想要让目标对象输出时候输出1.

const obj={id:"target"}
const handle={
	get(){
		return 1;
	}
}
const proxy=new Proxy(obj,handle)
console.log(obj.id,proxy.id);//target 1

捕获器参数和反射API

每个捕获器都有参数,这里拿get举例,get捕获器有三个参数:
1.目标对象2.要查询属性3.代理对象
那么我们就可以基于它的这些参数特性,去二次封装它们的捕获器。

const obj={id:'target'}
		const handle={
			get(target,prototype,proxy){
				console.log(target === obj);//true
				console.log(prototype);// id
				console.log(proxy);//Proxy(Object) {id: 'target'}
				return 1;
			}
		}
		const proxy=new Proxy(obj,handle)
		console.log(proxy.id);

当我们知道一个捕获器的原始参数后,就可以封装,如果我们不知道原始参数时,可以用到全局Reflect方法,Reflect有和被拦截方法相同的名称和行为。

const obj={id:'target'}
const handle={
	get(){
		return Reflect.get(...arguments)
	}
}
const simplehandle={
	get:Reflect.get
}
const proxy=new Proxy(obj,handle)
const simpleProxy=new Proxy(obj,simplehandle)
const bestProxy=new Proxy(obj,Reflect)

console.log(proxy.id,simpleProxy.id,bestProxy.id);//target target target

捕获器不变式

捕获器为了避免出现过于反常行为,引入了不变式的概念。在使用程序处理中,必须遵循不变式的条件。否则将报错,比如目标对象有一个不可配置且不可写的属性,捕获器返回一个不同值时,报错TypeError。

const obj={}
			Object.defineProperty(obj,'id',{
				configurable:false,
				writable:false,
				value:'target'
			})
			const handle={
				get(){return 'tar'}
			}
			const proxy=new Proxy(obj,handle)
			console.log(proxy.id);//Uncaught TypeError: 

可撤销代理

为了让代理有足够自由度,同时我们也可以通过revocable()函数实现对代理的撤销,撤销后,代理将失去它原有的作用。

const obj={id:"target"}
const handle={
	get(){return 'tar'}
}
const {proxy,revoke}=Proxy.revocable(obj,handle)
console.log(proxy.id);//tar
revoke()
console.log(proxy.id);//ncaught TypeError: Cannot perform 'get' on a proxy that has been revoked

实用反射API

  1. 反射API与对象API
  • 反射API不限于只应用在捕获程序中。
  • 大多数反射API方法在Object类型中都有对应的方法。
  1. 反射API方法部分有状态标记,也就是说它能判断是否生效。
     Reflect.defineProperty()
     Reflect.preventExtensions()
     Reflect.setPrototypeOf()
     Reflect.set()
     Reflect.deleteProperty()
    以上方法都提供状态标记。

代理另一个代理

可以通过一个双重代理来代理目标对象,也就是经过两次程序处理。
举例:

const obj={id:'target'}
const handle={
	get(){
		console.log('first');
		return Reflect.get(...arguments);
	}
}
const firstProxy=new Proxy(obj,handle)

const secondHandle={
	get(){
		console.log('second');
		return Reflect.get(...arguments);
	}
}
const secondProxy=new Proxy(firstProxy,secondHandle);
console.log(secondProxy.id);//second first target

反射

代理捕获器与反射方法

代理可以捕获13中不同操作。

get()

get方法会在获取属性值时被调用,对应的反射方法是Reflect.get()
语法格式:Reflect.get(proxy, property, receiver)

set()

set方法会在更新属性值时被调用,对应的反射方法是Reflect.set()
语法格式:Reflect.set(proxy, property, value, receiver)

has()

has方法会在in操作符中被调用,主要判断左侧属性是否在右侧对象中,对应的反射方法是Reflect.has()

const obj={id:'target'}
const handle={
	has(){
		console.log('has()');
		return Reflect.has(...arguments)
	}
}
const proxy=new Proxy(obj,handle)
console.log('id' in proxy);//has() true

还有像defineProperty、getOwnPropertyDescriptor、deleteProperty、ownKeys、getPrototypeOf、setPrototypeOf、isExtensible、preventExtensions、apply、construct等方法,这里就不一一赘述了,这些方法都可以被捕获(劫持)并且可以二次封装。

代理模式

跟踪属性访问

可以监控属性何时或何处被访问,可以通过劫持get、set或has方法来实现,举例:

const obj={id:'target'}
const handle={
	get(target,a,proxy){
		console.log("被调用",a);
		return Reflect.get(...arguments);
	}
}
const proxy=new Proxy(obj,handle)
console.log(proxy.id);// 被调用 id target

隐藏属性

可以通过数据劫持后判断是否显示某个属性,如果不显示,则实现隐藏效果。举例:

const obj={id:'target',age:18}
const hidden=['age']

const handle={
	get(target,a,proxy){
		if(hidden.includes(a)){
			return undefined;
		}else{
			return Reflect.get(...arguments)
		}
	},
	has(target,a,proxy){
		if(hidden.includes(a)){
			return false;
		}else{
			return Reflect.get(...arguments)
		}
	}
}
const proxy=new Proxy(obj,handle)
console.log(proxy.id,proxy.age);//target undefined
console.log('id' in proxy,'age' in proxy);//true false

属性验证

可以判断验证属性格式是否正确,举例:

const obj={age:18}
const handle={
	set(target,property,value){
		if(typeof value!=='number'){
			return false;
		}else{
			return Reflect.set(...arguments);
		}
	}
}
const proxy=new Proxy(obj,handle)
proxy.age='1'
console.log(proxy.age);//18
proxy.age=1
console.log(proxy.age);//1

数据绑定

可以把一个数据绑定到另一个数据中,实现强关联。举例:

const arr=[]

class user{
	constructor(name){
		this.name_=name
	}
}

const handle={
	construct(){
		const newUser=Reflect.construct(...arguments);
		arr.push(newUser);
		return newUser;
	}
}

const proxy=new Proxy(user,handle);
new proxy('Hellon');
console.log(arr);//[user]

总结

代理就是对数据的劫持,并且劫持的过程中加一些操作,反射,就是对象方法的复制API。可以从头到尾复刻对象的某个API方法。以上就是,全部内容。

  • 31
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃巧克li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值