【ES6系列】Proxy

Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作。Vue3.0 中将会通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。

语法

let p = new Proxy(target, handler)

解释

参数含义必选
target用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)Y
handler一个对象,其属性是当执行一个操作时定义代理的行为的函数Y
// 读法示例
let o = {
  name: 'lee',
  price: 190
}

let d = new Proxy(o, {
  get(target, key) {
    if (key === 'price') {
      return target[key] + 20
    } else {
      return target[key]
    }
  }
})
console.log(d.price, d.name) // 210 "lee"

 场景一:读写操作拦截

// 场景一:读写操作拦截-Proxy
let o = {
  name: 'lee',
  price: 190
}
// 中介函数不直接暴露o
let d = new Proxy(o, {
  get(target, key) { // 读操作拦截-可读
    return target[key]
  },
  set(target, key, value) { // 写操作拦截-不可写
    return false
  }
})
d.price = 300 // 赋值操作被拦截
console.log(d.price, d.name) // 190 "lee"

// 场景一:读写操作拦截-ES5 中 Proxy 的实现过程
for (let [key] of Object.entries(o)) { //  Object.entries(o) 可o转换为键值对形式
  // 属性描述符
  Object.defineProperty(o, key, {
    writable: false
  })
}
o.price = 300 // 不可写
console.log(o.name, o.price) // lee 190

es5的实现方式和ES6的Proxy区别:ES5写法key被彻底锁死,不可写,只有通过再次修改属性描述符才可以修改属性值。而ES6的代理从用户角度来说是锁死的,但中介函数还可以操作。

场景二:用户交互操作,较验数据保护,数据规范

// 业务场景描述:1、不能修改数据结构,拦截无效数据 2、只能修改price且不能超过300 
let o = {
  name: 'lee',
  price: 190
}
let d = new Proxy(o, {
  get(target, key) {
    return target[key] || '' // 异常处理
  },
  set(target, key, value) {
    if (Reflect.has(target, key)) {
      if (key === 'price') {
        if (value > 300) { // 大于300 不允许写操作
          return false
        } else {
          target[key] = value
        }
      }
    } else { // 操作属性不是 price 不允许写操作
      return false
    }
  }
})
// d.price = 280
// console.log(d.price, d.name) // 280 "lee"
// d.price = 310
// console.log(d.price, d.name) // 190 "lee"
// d.price = 301
// d.name = 'chris'
// console.log(d.price, d.name) // 190 "lee"
d.age = 3
console.log(d.price, d.name, d.age) // 190 "lee" ""


// 上述功能模块化/解耦
let o = {
  name: 'lee',
  price: 190
}
let validator = (target, key, value) => {
  if (Reflect.has(target, key)) {
    if (key === 'price') {
      if (value > 300) { // 大于300 不允许写操作
        return false
      } else {
        target[key] = value
      }
    }
  } else { // 操作属性不是 price 不允许写操作
    return false
  }
}
let d = new Proxy(o, {
  get(target, key) {
    return target[key] || '' // 异常处理
  },
  set: validator
})
// d.price = 280
// console.log(d.price, d.name) // 280 "lee"
// d.price = 310
// console.log(d.price, d.name) // 190 "lee"
// d.price = 301
// d.name = 'chris'
// console.log(d.price, d.name) // 190 "lee"
d.age = 3
console.log(d.price, d.name, d.age) // 190 "lee" ""

场景三:用户异常行为监控、上报

let validator = (target, key, value) => {
  if (Reflect.has(target, key)) {
    if (key === 'price') {
      if (value > 300) { // 大于300 不允许写操作        
        throw new TypeError('price exceed 300') // 不满足规则,触发错误 
        // return false
      } else {
        target[key] = value
      }
    }
  } else { // 操作属性不是 price 不允许写操作
    return false
  }
}
let d = new Proxy(o, {
  get(target, key) {
    return target[key] || '' // 异常处理
  },
  set: validator
})
d.price = 310
console.log(d.price, d.name, d.age) // Uncaught TypeError: price exceed 300

场景四:组件化开发中异常组件瞄准

业务场景描述:声明一个类,每次生成实例都分配id,要求:每次生成的实例、不同实例之间的id随机且唯一,只读。

class Component {
  constructor() {
    this.proxy = new Proxy({
      id: Math.random().toString(36).slice(-8)
    }, {}) //生成一个随机串转换成36进制的字符串,并截止后八位
  }
  get id() {
    return this.proxy.id
  }
}
let com = new Component()
let com2 = new Component()
for (let i = 0; i < 10; i++) { // 读取十次实例对象的id
  console.log(com.id, com2.id) // dmi2frvf tkk9v1qh
}
com.id = 'abc';
console.log(com.id, com2.id) // dmi2frvf tkk9v1qh

撤销代理:

Proxy.revocable() 

创建一个临时代理或是可撤销的代理。返回的是代理数据和撤销的操作两部分内容。

let o = {
  name: 'lee',
  price: 190
}
// new Proxy()返回的数据是被代理的数据
// Proxy.revocable() 创建一个临时代理或是可撤销的代理。返回的是代理数据和撤销的操作两部分内容
let d = Proxy.revocable(o, {
  get(target, key) {
    if (key === 'price') {
      return target[key] + 20
    } else {
      return target[key]
    }
  }
})
console.log(d.proxy.price, d)
setTimeout(function () {
  d.revoke()
  setTimeout(function () {
    console.log(d.proxy.price, d) // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
  }, 100)
}, 1000)

思考:

1、组件初始化的时候都赋值一个可读且随机的ID,该怎么做?

2、临时代理有哪些应用场景呢?

3、如何把接口的数据用代理进行包装?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值