可以解决的问题

使用 defineProperty 只能重定义属性的读取(get)和设置(set)行为,到了 ES6,提供了 Proxy,可以重定义更多的行为,比如 in、delete、函数调用等更多行为。

语法

var proxy = new Proxy(target, handler);

proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

  1. var proxy = new Proxy({}, {
  2. get: function(obj, prop) {
  3. console.log('设置 get 操作')
  4. return obj[prop];
  5. },
  6. set: function(obj, prop, value) {
  7. console.log('设置 set 操作')
  8. obj[prop] = value;
  9. }
  10. });
  11. proxy.time = 35; // 设置 set 操作
  12. console.log(proxy.time); // 设置 get 操作 // 35

除了 get 和 set 之外,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。

  1. // 使用 has 方法隐藏某些属性,不被 in 运算符发现
  2. var handler = {
  3. has (target, key) {
  4. if (key[0] === '_') {
  5. return false;
  6. }
  7. return key in target;
  8. }
  9. };
  10. var target = { _prop: 'foo', prop: 'foo' };
  11. var proxy = new Proxy(target, handler);
  12. console.log('_prop' in proxy); // false

Proxy 实例也可以作为其他对象的原型对象。

  1. var proxy = new Proxy({}, {
  2. get: function(target, propKey) {
  3. return 35;
  4. }
  5. });
  6. let obj = Object.create(proxy);
  7. obj.time // 35
  8. // 上面代码中,proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截。

proxy 实现优雅的校验器

  1. const formData = {
  2. name: 'xuxi',
  3. age: 120,
  4. label: ['react', 'vue', 'node', 'javascript']
  5. }
  6. // 校验器
  7. const validators = {
  8. name(v) {
  9. // 检验name是否为字符串并且长度是否大于3
  10. return typeof v === 'string' && v.length > 3
  11. },
  12. age(v) {
  13. // 检验age是否为数值
  14. return typeof v === 'number'
  15. },
  16. label(v) {
  17. // 检验label是否为数组并且长度是否大于0
  18. return Array.isArray(v) && v.length > 0
  19. }
  20. }
  21. // 代理校验对象
  22. function proxyValidator(target, validator) {
  23. return new Proxy(target, {
  24. set(target, propKey, value, receiver) {
  25. if(target.hasOwnProperty(propKey)) {
  26. let valid = validator[propKey]
  27. if(!!valid(value)) {
  28. return Reflect.set(target, propKey, value, receiver) //这里可以针对验证通过的值再做其他操作
  29. }else {
  30. // 一些其他错误业务...
  31. // throw Error(`值验证错误${propKey}:${value}`)
  32. console.log(`值验证错误${propKey}:${value}`)
  33. }
  34. }
  35. }
  36. })
  37. }
  38. let formObj = proxyValidator(formData, validators)
  39. formObj.name = 333; // Uncaught Error: 值验证错误name:f
  40. formObj.age = 'ddd' // Uncaught Error: 值验证错误age:f