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}`);
(...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'
}
const hiddenProps = ['addr', 'income', 'email'];
const userProxy = new Proxy(user, {
get(target, prop) {
if (hiddenProps.includes(prop)) {
return undefined;
}
(...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
// 但是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) {
duce((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');
}
struct(...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 = struct(...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) {
// (target, prop, receiver));
// }
// 加个判断,对length的修改不发送消息
if (prop !== 'length' && result) {
(target, prop, receiver));
}
return result;
}
});
// push会对userList进⾏两次set操作,js arguments
// 第⼀次新增⼀个元素,第⼆次修改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('', target, key);
(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类型');
}
duce((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 = w();
if (now - lastTime > rate) {
lastTime = now;
return Reflect.apply(target, ctx, args);
}
},
});
};
const logTimeStamp = () => console.w());
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()函数。这样就实现了⼀个简单的响应式状态管理。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论