一、Proxy是什么?
1.1 理解Proxy
MDN定义:Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。
通俗的讲Proxy是一个对象操作的拦截器,拦截对目标对象的操作,进行一些自定义的行为,一种分层的思想有点类似spring的AOP。
1.2 Proxy怎么用
let p = new Proxy(target, handler);
语法非常简单,只有两个参数,很好理解
target
用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler
一个对象,其属性是当执行一个操作时定义代理的行为的函数。
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
let obj = Object.create(proxy);
obj.time // 35
无操作转发代理
代理对象p会将所有应用到它的操作转发到目标对象target上,可以直接对代理对象进行操作,操作会转发到目标对象上。
let target = {};
let p = new Proxy(target, {});
p.a = 37; // 操作转发到目标
console.log(target.a); // 37. 操作已经被正确地转发
1.3 可以拦截的操作
一共有 13 种可代理操作,每种操作的代号(属性名/方法名)和触发这种操作的方式列举如下。注意,如果没有定义某种操作,那么这种操作会被转发到目标对象身上。
- handler.getPrototypeOf()
在读取代理对象的原型时触发该操作,比如在执行 Object.getPrototypeOf(proxy) 时。 - handler.setPrototypeOf()
在设置代理对象的原型时触发该操作,比如在执行 Object.setPrototypeOf(proxy, null) 时。 - handler.isExtensible()
在判断一个代理对象是否是可扩展时触发该操作,比如在执行 Object.isExtensible(proxy) 时。 - handler.preventExtensions()
在让一个代理对象不可扩展时触发该操作,比如在执行 Object.preventExtensions(proxy) 时。 - handler.getOwnPropertyDescriptor()
在获取代理对象某个属性的属性描述时触发该操作,比如在执行 Object.getOwnPropertyDescriptor(proxy, “foo”) 时。 - handler.defineProperty()
在定义代理对象某个属性时的属性描述时触发该操作,比如在执行 Object.defineProperty(proxy, “foo”, {}) 时。 - handler.has()
在判断代理对象是否拥有某个属性时触发该操作,比如在执行 “foo” in proxy 时。 - handler.get()
在读取代理对象的某个属性时触发该操作,比如在执行 proxy.foo 时。 - handler.set()
在给代理对象的某个属性赋值时触发该操作,比如在执行 proxy.foo = 1 时。 - handler.deleteProperty()
在删除代理对象的某个属性时触发该操作,比如在执行 delete proxy.foo 时。 - handler.ownKeys()
在获取代理对象的所有属性键时触发该操作,比如在执行 Object.getOwnPropertyNames(proxy) 时。 - handler.apply()
当目标对象为函数,且被调用时触发。 - handler.construct()
在给一个目标对象为构造函数的代理对象构造实例时触发该操作,比如在执行new proxy() 时。
1.4 this的指向
需要特别注意代理对象和拦截操作中的this指向问题
一旦对象被代理之后,他的this就指向了代理对象
二、Proxy能干什么?
Proxy 各种常用的拦截都能干什么,常用的四种操作
2.1 get()
get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身。所有的属性调用都会进入同一个get即使是没有的属性。
- 可以创造一些本来没有的属性
- 可以在取数据时对数据进行验证和转换
- 可以自定义一些语法糖操作
- get返回一个函数的话可以把一个属性转换成方法
var base = {
a:100,
small:"hello world!!"
}
var proxy = new Proxy(base,{
get(target, property,receiver){
//属性转换成函数
if("fn" === property ){
return function(value){
console.log(value)
}
}
if("ghost" === property ){
return "ghost"
}
if("fn" === property ){
return function(value){
console.log(value)
}
}
//自定义语法糖
if(property.includes("_")){
const direct = property.split("_")[1]
const propertyBase = property.split("_")[0]
switch (direct){
case "Big":
return receiver[propertyBase].toLocaleUpperCase()
default:
break;
}
}
if(!(property in target)){
throw new ReferenceError("Property \"" + property + "\" does not exist.");//验证属性值
}
return target[property]
}
})
console.log(proxy.a)//输出100 正常访问
proxy.fn("fn")//输出fn 属性转换为方法
console.log(proxy.small_Big)//输出 HELLO WORLD!! 自定义语法糖
console.log(proxy.ghost)//输出ghost 创建属性
console.log(proxy.ghost_Big)//输出GHOST receiver的使用
console.log(proxy.b)//数据验证 抛出错误
2.2 set()
set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
- 可以用来验证属性是否符合要求
- 可以用来更改数据格式
- 可以用来监听数据更改事件
- 屏蔽一些赋值操作比如以"_"开头的私有变量
var base = {
a:100,
small:"hello world!!"
}
const A_Change_Event="aChangeEvent";
window.addEventListener(A_Change_Event,(e)=>{
console.log(e.data.value)
})
var proxy = new Proxy(base,{
set(obj, property, value,receiver){
if("a" === property ){
if (!Number.isInteger(value)) {
throw new TypeError('The'+property+'is not an integer');
}
if (value > 100){
throw new RangeError('The '+property+' seems invalid');
}
obj[property] = value;
//事件
const dataChangeEvent = new Event(A_Change_Event);//创建一个事件
dataChangeEvent.data = {value}
window.dispatchEvent(dataChangeEvent)
}
}
})
proxy.a = 80;
console.log(proxy.a)
proxy.a = 120;
2.3 apply()
函数对象也可以作为代理的目标对象
apply方法拦截函数的调用、call和apply操作。
apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
最简单的例子
var target = function () { return 'I am the target'; };
var handler = {
apply: function () {
return 'I am the proxy';
}
};
var p = new Proxy(target, handler);
p()
// "I am the proxy"
2.4 construct ()
construct 拦截的是new操作
接受三个参数
- target:目标对象
- args:构造函数的参数对象
- newTarget:创造实例对象时,new命令作用的构造函数
三 案例
3.1拦截数组
// 对数组进行拦截处理
let arr = [
{
title: '活着',
author: '余华--'
},
{
title: '三体',
author: '刘慈欣--'
},
{
title: '雪国',
author: '川端康成--'
},
]
let proxy2 = new Proxy(arr, {
get(arr, key) {
const author = arr[key].author
arr[key].author = author.slice(-1, 2)
return arr[key]
}
})
console.log(proxy2[1])
3.2表单验证
class Validate {
max(value, len) {
return value.lengh <= len
}
min(value, len) {
return value.lengh >= len
}
idNumber(value) {
return /^\d+$/.test(value)
}
}
// 代理工厂
function proxyFactory(target) {
return new Proxy(target, {
get(target, key) {
return target[key]
},
set(target, key, el) {
const rule = el.getAttribute('rule')
const validate = new Validate()
let state = rule.split(',').every((item) => {
const info = item.split(':')
return validate[info[0]](el.value, info[1])
})
console.log(state)
return true
}
})
}
const proxy = proxyFactory(document.querySelectorAll('[validate]'))
proxy.forEach((item, i) => {
item.addEventListener('keyup', function () {
proxy[i] = this
})
})
标签:target,对象,代理,handler,Proxy,属性,proxy
From: https://www.cnblogs.com/coderz1/p/17049041.html