JavaScript的代理(Proxy)

本文探讨了JavaScript中代理的原理,如何通过代理实现对象的响应式行为,并介绍了可撤销代理的概念、创建及撤销过程。重点讲述了代理与目标对象的交互以及Proxy.prototype的特性。

代理是目标对象的抽象。

  • 目前理解为,代理就是给目标对象绑定一个指针。或者是JavaScript中的代理的原型绑定到了目标对象的原型上,但是代理的原型是undefined。
  • 当对象改变或者代理改变,两者都会互相改变。一种响应式的行为。
  • 可撤销代理bug: 代理变量名必须为proxy,撤销方法变量名必须为revoke
const target = {
    id: 'target'
};

const handler = {};

定义一个普通的proxy代理, 为了不与可撤销代理的指定变量名冲突,普通代理尽量不要用proxy命名

const proxyObj = new Proxy(target, handler); 

id属性会访问同一个值

console.log('target: ', target.id, '\n', 'proxyObj: ', proxyObj.id);
  • 给目标属性赋值会反映在两个对象上
  • 因为两个对象访问的是同一个值。 准确的来说,应该是访问同一个内存地址
target.id = 'target modify';
console.log('target: ', target.id, '\n', 'proxyObj: ', proxyObj.id);
  • 相反的,给代理属性赋值也会反映在两个对象上
  • 因为这个赋值会转移到目标对象
proxyObj.id = 'proxy modify';
console.log('target: ', target.id, '\n', 'proxyObj: ', proxyObj.id);
  • hasOwnProperty() 方法在两个地方都会应用到目标对象
console.log('target hasOwnProperty: ', target.hasOwnProperty('id'));
console.log('proxyObj hasOwnProperty: ', proxyObj.hasOwnProperty('id'));
  • Proxy.prototype是undefined
  • 因此不能使用instanceof操作符
console.log( target instanceof Proxy); // TypeError: Function has non-object prototype 'undefined' in instanceof check
console.log(proxy instanceof Proxy); // TypeError: Function has non-object prototype 'undefined' in instanceof check
  • 严格相等可以用来区分代理和目标
console.log('target equals proxy: ', target === proxyObj); // false
  • 可撤销代理bug: 代理变量名必须为proxy,撤销方法变量名必须为revoke
  • 可撤销代理创建
const targetObj = {
    name: 'dabing'
}

const handlerObj = {
    get() {
        return 'intercepted';
    }
}
  • 定义其可撤销代理时,对应的两个变量名,必须为proxy与 revoke
const { proxy, revoke } = Proxy.revocable(targetObj, handlerObj); 

console.log('targetObj : ', targetObj.name);
console.log('proxyObj : ', proxy.name);

revoke(); //撤销代理, 此操作是不可逆的。

console.log('proxyObj : ', proxy.name); // proxy引用不到目标对象,所以会报TypeError
  • 代理另一个代理
const target2 = {
    name: 'target object'
};

const firstProxy = new Proxy(target2, {
    get() {
        console.log('first Proxy');
        return Reflect.get(...arguments);
    }
});

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

console.log("************************************************************");
// console.log(target2.name);
// console.log(firstProxy.name);
console.log(secondProxy.name);
### JavaScript ES6 Proxy 的使用方法及示例 #### 基础概念 Proxy 是 ES6 中引入的一种机制,能够拦截并自定义对目标对象的操作行为。通过 `new Proxy()` 构造器创建一个代理对象,该对象可以捕获特定的行为(如属性访问、赋值等),并对这些行为进行修改或扩展。 #### 创建基础的 Proxy 对象 以下是创建一个简单的 Proxy 对象的示例: ```javascript let targetObject = { name: "Alice", age: 25, }; let handler = { get(target, property, receiver) { console.log(`正在获取属性 ${property}`); return Reflect.get(target, property, receiver); }, set(target, property, value, receiver) { console.log(`正在设置属性 ${property} 为 ${value}`); return Reflect.set(target, property, value, receiver); } }; let proxy = new Proxy(targetObject, handler); console.log(proxy.name); // 输出:正在获取属性 name \n Alice proxy.age = 30; // 输出:正在设置属性 age 为 30 console.log(proxy.age); // 输出:正在获取属性 age \n 30 ``` 上述代码展示了如何通过 `get` 和 `set` 方法来拦截属性的读取和写入操作[^1]。 #### 高级用法 除了基本的 `get` 和 `set` 拦截外,Proxy 还支持多种其他的拦截操作,例如 `apply`, `construct`, `has`, `deleteProperty` 等。下面是一个更复杂的例子,展示如何拦截删除属性的操作: ```javascript let person = { firstName: "John", lastName: "Doe" }; let advancedHandler = { deleteProperty(target, property) { if (confirm(`确认要删除属性 "${property}" 吗?`)) { const result = Reflect.deleteProperty(target, property); if (result) { console.log(`${property} 属性已被成功删除`); } else { console.error(`无法删除属性 ${property}`); } return result; } return false; } }; let advancedProxy = new Proxy(person, advancedHandler); // 尝试删除属性 delete advancedProxy.firstName; // 浏览器会弹出确认框询问是否删除 console.log(advancedProxy); // 如果删除成功,则不会显示 firstName ``` 此示例中,当尝试删除某个属性时,程序会提示用户确认删除动作,并根据用户的响应决定是否执行删除操作[^3]。 #### 使用 Proxy 作为原型对象 还可以将 Proxy 实例设为其他对象的原型,从而影响继承链上的行为。如下所示: ```javascript let prototypeProxy = new Proxy({}, { get(target, property) { return `${property} 被访问`; } }); let childObject = Object.create(prototypeProxy); console.log(childObject.time); // 输出:time 被访问 console.log(childObject.location); // 输出:location 被访问 ``` 在这个场景下,任何未定义于子对象中的属性都会触发其原型上定义的 `get` 捕获器逻辑[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值