跟踪属性访问
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
隐藏属性
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);
}
属性验证
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!
函数和构造函数参数验证
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
数据绑定与可观察对象
被代理的类绑定到一个全局实例集合,让所有创建的实例都被添加到这个集合中
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
参考资料:
《JavaScript高级程序设计(第4版)》