重学JavaScript[5] Proxy
在2022已经过了一半的时候重新捡起来这个系列...
# 代理
说到Proxy,联想到它的中文意思则是”代理“
何为代理?
- 小伙伴叫你出去玩,但你妈却告诉小伙伴”你在学习,不想出去玩“
- 大过年的,王阿姨给了你压岁钱¥500,但递交给你时被你妈装进了口袋
这两个例子中,”你妈“就充当了代理的作用,干涉你与外界的沟通
# Proxy
简单来说,Proxy只是挂在 globalThis
上的一个普通对象(也是个构造函数)
const you = { said: '我想出去玩' };
const yourMother = new Proxy(you, { get() { return '小明在学习,他不想出去玩' } });
console.log(you.said); // 我想出去玩
console.log(yourMother.said); // 小明在学习,他不想出去玩
当你在浏览器输入上述代码后,再查看 you
和 yourMother
这两个变量,发现 yourMother
包含了[[Handler]]和[[Target]],这是一个被Proxy包装过的对象
# 用法
看看API
const p = new Proxy(target, handler);
其中
target
是需要使用Proxy包装的目标对象(包括数组、函数)handler
也是一个对象,是Proxy操作的重头戏
hander可以包含以下属性
get
, set
, has
, apply
, construct
, ownKeys
, defineProperty
... (这里就不全列出来了,感兴趣可以去MDN (opens new window)上瞅瞅全部的13种拦截器)
这些属性分别可以对对象的属性读、属性写、in运算符、函数调用、new调用等操作进行拦截
在上面的”你妈不让你出去玩“惨剧中,你妈只是实现了”控制’,实际上,她也可以不改变你的行为,仅对你实施“监听”
const you = {}; // 你在感情方面是一张白纸
const yourMother = new Proxy(you, {
set(_, property, value) {
console.log(`有人给我儿子的${property}说:${value}`);
},
});
const ruhua = { name: '如花', love: p => (p.love = '我爱你') };
ruhua.love(yourMother); // 有人给我儿子的love说:我爱你
如花给你表白时,你妈将得知
当然,上面的代码还是有问题的,因为在配置 set
拦截器中,你妈拦截了如花对你的表白
// 运行了上面的代码后...
console.log(you); // {}
// 你的感情仍然是一张白纸
因此,如果想要不干涉你的感情,你妈应该在控制台悄悄记录后 return Reflect.set(...arguments);
扯远了...
如果想了解关于Reflect的事情,参考这篇 Reflect (还没写)
# 应用
Proxy 的应用绝非只是实现一个控制欲极强的妈妈这么简单
对代理模式、观察者模式和实现响应式与数据绑定来说,Proxy都是极佳的选择
Vue3.x 里大量使用了Proxy来进行依赖收集、实现
computed
和watch
等一段无聊的代码: 来源 (opens new window)
const www = new Proxy(new URL('https://www'), { get: function get(target, prop) { let o = Reflect.get(target, prop); if (typeof o === 'function') { return o.bind(target) } if (typeof prop !== 'string') { return o; } if (prop === 'then') { return Promise.prototype.then.bind(fetch(target)); } target = new URL(target); target.hostname += `.${prop}`; return new Proxy(target, { get }); } }); www.baidu.com.then(console.log) // 试试看