可以解决的问题
使用 defineProperty 只能重定义属性的读取(get)和设置(set)行为,到了 ES6,提供了 Proxy,可以重定义更多的行为,比如 in、delete、函数调用等更多行为。
语法
var proxy = new Proxy(target, handler);
proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
var proxy = new Proxy({}, {get: function(obj, prop) {console.log('设置 get 操作')return obj[prop];},set: function(obj, prop, value) {console.log('设置 set 操作')obj[prop] = value;}});proxy.time = 35; // 设置 set 操作console.log(proxy.time); // 设置 get 操作 // 35
除了 get 和 set 之外,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。
// 使用 has 方法隐藏某些属性,不被 in 运算符发现var handler = {has (target, key) {if (key[0] === '_') {return false;}return key in target;}};var target = { _prop: 'foo', prop: 'foo' };var proxy = new Proxy(target, handler);console.log('_prop' in proxy); // false
Proxy 实例也可以作为其他对象的原型对象。
var proxy = new Proxy({}, {get: function(target, propKey) {return 35;}});let obj = Object.create(proxy);obj.time // 35// 上面代码中,proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截。
proxy 实现优雅的校验器
const formData = {name: 'xuxi',age: 120,label: ['react', 'vue', 'node', 'javascript']}// 校验器const validators = {name(v) {// 检验name是否为字符串并且长度是否大于3return typeof v === 'string' && v.length > 3},age(v) {// 检验age是否为数值return typeof v === 'number'},label(v) {// 检验label是否为数组并且长度是否大于0return Array.isArray(v) && v.length > 0}}// 代理校验对象function proxyValidator(target, validator) {return new Proxy(target, {set(target, propKey, value, receiver) {if(target.hasOwnProperty(propKey)) {let valid = validator[propKey]if(!!valid(value)) {return Reflect.set(target, propKey, value, receiver) //这里可以针对验证通过的值再做其他操作}else {// 一些其他错误业务...// throw Error(`值验证错误${propKey}:${value}`)console.log(`值验证错误${propKey}:${value}`)}}}})}let formObj = proxyValidator(formData, validators)formObj.name = 333; // Uncaught Error: 值验证错误name:fformObj.age = 'ddd' // Uncaught Error: 值验证错误age:f
