在前端开发中,Proxy 对象用于定义基本操作的自定义行为,如属性查找、赋值、枚举、函数调用等。它可以作为某些内置对象的包装器,例如,对数组或函数的包装。今天,我们将深入探索 Proxy 的应用,并演示如何实现一级代理和多层级代理对象。
一级代理的简单实现
首先,我们来看一个简单的 Proxy 实现。在 TypeScript 中,Proxy 对象是这样创建的:
const proxy = new Proxy({}, {
get(target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
return Reflect.set(target, key, value, receiver);
}
})
在上述代码中,我们创建了一个空的 Proxy 对象,并定义了 get 和 set 陷阱函数。这些陷阱函数允许我们在访问或修改 Proxy 对象的属性时,执行自定义的操作。
代理的简单应用例子
接下来,我们通过一个简单的例子来展示 Proxy 的应用。假设我们有一个用户信息对象 userInfo,我们希望通过 Proxy 对象实现属性的间接访问和修改。
// 源数据
let userInfo = {
name: '张三',
age: 18,
sex: '男'
}
// 映射关系
const userMap = {
uid_1: 'name',
uid_2: 'age',
uid_3: 'sex',
}
// 代理
const userProxy = new Proxy(userInfo, {
get(target, key, receiver) {
return Reflect.get(target, userMap[key], receiver);
},
set(target, key, value, receiver) {
return Reflect.set(target, userMap[key], value, receiver);
}
})
userProxy['uid_1'] = '李四';
console.log(userInfo.name); // 李四
console.log(userProxy['uid_2']); // 18
在这个例子中,我们创建了一个 userProxy 对象,它代理了 userInfo 对象。通过 userMap 映射关系,我们可以使用 uid_1、uid_2、uid_3 这样的键来间接访问和修改 userInfo 对象的属性。
多层级代理对象的实现
在实际应用中,我们可能需要处理更复杂的对象结构,如嵌套对象或数组。这时,我们可以使用递归的方式来实现多层级代理对象。
以下是一个处理多层级代理对象的例子:
const data = {
age: 55,
'dync_-2': '我是',
extra: {
name: 2,
},
refs: {
abc: 'xxx'
}
}
const pathObj: any = {
uid_1: 'age',
uid_2: 'extra.name',
uid_3: 'refs.abc',
}
const formData = reactive(onlyReactive(data))
/*
自定义onlyReactive
*/
function onlyReactive(target: any) {
// 判断是否是对象:
if (target && typeof target === 'object' && !Array.isArray(target)) {
Object.keys(target).forEach(key => {
target[key] = onlyReactive(target[key])
})
const proxy = new Proxy(target, {
// _map: new WeakMap(), // 用来保存数据的内部属性
get(target, key: string, receiver) {
if (typeof key === 'string' && pathObj.hasOwnProperty(key) && key.indexOf('uid_') != -1) {
const parts = pathObj[key].split('.');
console.log(parts);
if (parts.length <= 1) {
return target[pathObj[key]];
}
let current = target
for (let i = 0; i < parts.length; i++) {
if (current[parts[i]] !== undefined) {
// current = Reflect.get(current, parts[i]);;
current = current[parts[i]];
} else {
return undefined; // 如果路径中的任何部分不存在,则返回undefined
}
}
return current;
} else {
return Reflect.get(target, key, receiver);
}
},
set(target, key: string, value, receiver) {
if (key in pathObj) {
// 获取路径
const path = pathObj[key];
// 设置嵌套属性值
console.log('path', path);
if (path.indexOf('refs') == -1) {
setValueByPath(target, path, value);
} else {
console.warn('只读的, 不能修改')
}
return true; // 表示设置成功
}
if (key.indexOf('dync_') != -1) {
console.warn('只读的, 不能修改')
return true;
}
// 如果不是通过pathObj来设置的属性,则直接设置
return Reflect.set(target, key, value, receiver);
},
// deleteProperty(target: any, key: any, receiver: any) {
// console.warn('只读的, 不能删除')
// return true
// },
})
return proxy
}
return target
}
// 辅助函数,用于通过路径设置值
function setValueByPath(obj: any, path: string, value: any) {
const pathArray = path.split('.');
let currentObj = obj;
const lastKey: any = pathArray.pop();
for (let i = 0; i < pathArray.length; i++) {
const key = pathArray[i];
if (!(key in currentObj)) {
currentObj[key] = {}; // 如果属性不存在,则创建它
}
currentObj = currentObj[key];
}
currentObj[lastKey] = value; // 设置最终的值
}
在这个例子中,我们定义了一个 onlyReactive 函数,它接受一个目标对象作为参数,并返回一个新的 Proxy 对象。在 Proxy 的 get 陷阱函数中,我们根据 pathObj 映射关系处理多层级属性的访问。如果属性不存在,我们返回 undefined