场景1
我们经常读取一个对象的 key-value:
let o = {
name: 'xiaoming',
age: 20
}
console.log(o.name) // xiaoming
console.log(o.age) // 20
console.log(o.from) // undefined
当我们读取 from 的时候返回的是 undefined,因为 o 这个对象中没有这个 key-value。想想看我们在读取数据的时候,这个数据经常是聚合的,当大家没有按照规范来的时候或者数据缺失的情况下,经常会出现这种“乌龙”现象。
如果我们不想在调用 key 的时候返回 undefined,之前的做法是这样的:
console.log(o.from || '')
如果我们对所有代码都是这种写法,那阅读性和观赏性就不得而知了。值得庆幸的是,ES6 的 Proxy 可以让我们轻松的解决这一问题:
let o = {
name: 'xiaoming',
age: 20
}
let handler = {
get(obj, key) {
return Reflect.has(obj, key) ? obj[key] : ''
}
}
let p = new Proxy(o, handler)
console.log(p.from)
这个代码是想表达如果 o 对象有这个 key-value 则直接返回,如果没有一律返回 ''
,当然这里是自定义,大家可以根据自己的需要来写适合自己业务的规则。
刚才对数据的“读操作”进行了拦截,接下来我们描述下“写操作”进行拦截。
场景 2
从服务端获取的数据希望是只读,不允许在任何一个环节被修改。
// response.data 是 JSON 格式的数据,来自服务端的响应
// 在 ES5 中只能通过遍历把所有的属性设置为只读
for (let [key] of Object.entries(response.data)) {
Object.defineProperty(response.data, key, {
writable: false
})
}
如果我们使用 Proxy 就简单很多了:
let data = new Proxy(response.data, {
set(obj, key, value) {
return false
}
})
场景 3
对于数据交互而言,校验是不可或缺的一个环境,传统的做法是将校验写在了业务逻辑里,导致代码耦合度较高。如果大家使用 Proxy 就可以将代码设计的非常灵活。
// Validator.js
export default (obj, key, value) => {
if (Reflect.has(key) && value > 20) {
obj[key] = value
}
}
import Validator from './Validator'
let data = new Proxy(response.data, {
set: Validator
})
场景 4
如果对读写进行监控,可以这样写:
let validator = {
set(target, key, value) {
if (key === 'age') {
if (typeof value !== 'number' || Number.isNaN(value)) {
throw new TypeError('Age must be a number')
}
if (value <= 0) {
throw new TypeError('Age must be a positive number')
}
}
return true
}
}
const person = {
age: 27
}
const proxy = new Proxy(person, validator)
proxy.age = 'foo'
// <- TypeError: Age must be a number
proxy.age = NaN
// <- TypeError: Age must be a number
proxy.age = 0
// <- TypeError: Age must be a positive number
proxy.age = 28
console.log(person.age)
// <- 28
// 添加监控
window.addEventListener(
'error',
e => {
console.log(e.message) // Uncaught TypeError: Age must be a number
},
true
)