js的Proxy和Reflect的应用场景
proxy基本使用方式
/** * target: 表示要代理的目标,可以是object, array, function类型 * handler: 是一个对象,可以编写各种代理的方法 */ const proxy = new Proxy(target, handler);
1. 跟踪属性访问
const user = { name: 'Jack' } const userPorxy = new Proxy(user, { get(target, prop) { console.log(`Getting ${prop}`); return Reflect.get(...arguments); }, set(target, prop, val) { console.log(`Setting ${prop} to ${val}`); return Reflect.set(...arguments); } }); userPorxy.name; // Getting name userPorxy.name = 'Wango'; // Setting name to Wango
2. 隐藏属性
const user = { name: 'Wango', age: 24, addr: 'Chongqing, China', income: 'classified', email: 'example@mail.com' } const hiddenProps = ['addr', 'income', 'email']; const userProxy = new Proxy(user, { get(target, prop) { if (hiddenProps.includes(prop)) { return undefined; } return Reflect.get(...arguments); }, has(target, prop) { if (hiddenProps.includes(prop)) { return false; } return Reflect.has(...arguments); } }); console.log(userProxy.name); // Wango console.log(userProxy.addr); // undefined console.log('age' in userProxy); // true console.log('income' in userProxy); // false console.log('email' in userProxy); // false // 但是for...in依旧可以枚举属性 for (let key in userProxy) { console.log(key); }
3. 属性验证
const user = { name: 'Wango', age: 24 } const userPorxy = new Proxy(user, { set(target, prop, val) { if (prop === 'age') { if (typeof val !== 'number') { throw new TypeError('A number expected!'); } } return Reflect.set(...arguments); } }); userPorxy.age = 33; console.log(userPorxy.age); // 33 userPorxy.age = '100'; // TypeError: A number expected!
4. 函数和构造函数参数验证
function add(...args) { return args.reduce((a, b) => a + b); } const addProxy = new Proxy(add, { apply(target, thisArg, args) { for (let i = 0; i < args.length; i++) { if (typeof args[i] !== 'number') { throw new TypeError('Non-number argument provided'); } } return Reflect.apply(...arguments); } }); console.log(addProxy(1, 2, 3, 4, 5)); // 15 console.log(addProxy(1, 2, 3, 4, '5')); // TypeError: Non-number argument provided class User { constructor(id) { this.id = id; } } const UserProxy = new Proxy(User, { construct(target, args, newTarget) { if (args[0] === undefined) { throw new Error('User cannot be instantiated without id'); } return Reflect.construct(...arguments); } }); const u1 = new UserProxy('Wango'); const u2 = new UserProxy(); // Error: User cannot be instantiated without id
5. 数据绑定和可观察对象
被代理的类绑定到一个全局实例集合,让所有创建的实例都被添加到这个集合中
const userList = []; class User { constructor(name) { this.name = name; } } const UserProxy = new Proxy(User, { construct() { const newUser = Reflect.construct(...arguments); userList.push(newUser); return newUser; } }); new UserProxy('Wango'); new UserProxy('Jack'); new UserProxy('Lily'); console.log(userList); // [User, User, User]
把集合绑定到一个事件分派程序,每次插入新实例时都会发送消息
function emit(newVal) { console.log(newVal); } const userList = []; const userListProxy = new Proxy(userList, { set(target, prop, val, receiver) { const result = Reflect.set(...arguments); // if (result) { // emit(Reflect.get(target, prop, receiver)); // } // 加个判断,对length的修改不发送消息 if (prop !== 'length' && result) { emit(Reflect.get(target, prop, receiver)); } return result; } }); // push会对userList进行两次set操作, // 第一次新增一个元素,第二次修改length的值 userListProxy.push('Wango'); // Wango // 1 userListProxy.push('Lily'); // Lily // 2
6. 代理数组
const arr = [1, 2, 3, 4]; const arrProxy = new Proxy(arr, { get(target, key, receiver) { console.log('arrProxy.get', target, key); return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { console.log('arrProxy.set', target, key, value); return Reflect.set(target, key, value, receiver); }, deleteProperty(target, key) { console.log('arrProxy.deleteProperty', target, key); return Reflect.deleteProperty(target, key); }, });
7. 统计函数被调用的上下文和次数
const countExecute = (fn) => { let count = 0; return new Proxy(fn, { apply(target, ctx, args) { ++count; console.log('ctx上下文:', ctx); console.log(`${fn.name} 已被调用 ${count} 次`); return Reflect.apply(target, ctx, args); }, }); }; const getSum = (...args) => { if (!args.every((item) => typeof item === 'number')) { throw new TypeError('参数应当均为number类型'); } return args.reduce((sum, item) => sum + item, 0); }; const useSum = countExecute(getSum); useSum(1, 2, 3); // getSum 已被调用 1 次 useSum.apply(window, [2, 3, 4]); // getSum 已被调用 2 次 useSum.call(person, 3, 4, 5); // getSum 已被调用 3 次
8. 实现一个防抖功能
const throttleByProxy = (fn, rate) => { let lastTime = 0; return new Proxy(fn, { apply(target, ctx, args) { const now = Date.now(); if (now - lastTime > rate) { lastTime = now; return Reflect.apply(target, ctx, args); } }, }); }; const logTimeStamp = () => console.log(Date.now()); window.addEventListener('scroll', throttleByProxy(logTimeStamp, 300));
9. 实现观察者模式
我们在这里实现一个最简单类 mobx 观察者模式。
const list = new Set(); const observe = (fn) => list.add(fn); const observable = (obj) => { return new Proxy(obj, { set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); list.forEach((observer) => observer()); return result; }, }); }; const person = observable({ name: 'wenzi', age: 20 }); const App = () => { console.log(`App -> name: ${person.name}, age: ${person.age}`); }; observe(App);
person就是使用 Proxy 创建出来的代理对象,每当 person 中的属性发生变化时,就会执行 App()函数。这样就实现了一个简单的响应式状态管理。
出处:https://www.cnblogs.com/hycstar/p/14591158.html?ivk_sa=1024320u
https://zxbcw.cn/post/212296/