ES6(2015)Proxy
Posted 优小U
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6(2015)Proxy相关的知识,希望对你有一定的参考价值。
在 ES6 标准中新增的一个非常强大的功能是 Proxy
,它可以自定义一些常用行为如查找、赋值、枚举、函数调用等。通过 Proxy
这个名称也可以看出来它包含了“代理”的含义,只要有“代理”的诉求都可以考虑使用 Proxy
来实现。
1. 基本语法
let p = new Proxy(target, handler)
参数 | 含义 |
---|---|
target | 用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理) |
handler | 一个对象,其属性是当执行一个操作时定义代理的行为的函数 |
第一个参数 target
就是用来代理的“对象”,被代理之后它是不能直接被访问的,而 handler
就是实现代理的过程。
2. 拦截操作场景
let obj = {
name: 'xiaoming',
age: 18
}
console.log(obj.telephone) // undefined
当我们读取 telephone 的时候返回的是 undefined,因为 obj 这个对象中没有这个属性。一般我们的做法是obj.telephone || ''
,这样能保证不会输出undefined
,但如果都是这样写有点不太美观。ES6 的 Proxy 可以让我们轻松的解决这一问题:
let obj = {
name: 'xiaoming',
age: 18
}
let handle = {
get (obj, key) {
return Reflect.has(obj, key) ? obj[key] : ''
}
}
let p = new Proxy(obj, handle)
console.log(p.telephone)
场景1:从服务端获取的数据希望是只读,不允许在任何一个环节被修改
// ES5 遍历设置只读
for (let [key] of Object.entries(response.data)) {
Object.defineProperty(response.data, key, {
writable: false
})
}
// ES6 Proxy
let data = new Proxy(response.data, {
set(obj, key, value) {
return false
}
})
场景2:校验,避免与业务逻辑耦合
// 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
})
场景3:对读写进行监控
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
)
3. 常用操作
get 拦截对象属性的读取
let arr = [7, 8, 9]
arr = new Proxy(arr, {
get(target, prop) {
return prop in target ? target[prop] : 'error'
}
})
console.log(arr[1]) // 8
console.log(arr[10]) // error
set 拦截对象属性的设置
let arr = []
arr = new Proxy(arr, {
set(target, prop, val) {
if (typeof val === 'number') {
target[prop] = val
return true
} else {
return false
}
}
})
arr.push(5)
arr.push(6)
console.log(arr[0], arr[1], arr.length) // 5 6 2
has 拦截propKey in proxy的操作
let range = {
start: 1,
end: 5
}
range = new Proxy(range, {
has(target, prop) {
return prop >= target.start && prop <= target.end
}
})
console.log(2 in range) // true
console.log(9 in range) // false
ownKeys 拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in循环,返回一个数组
let userinfo = {
username: 'xiecheng',
age: 34,
_password: '***'
}
userinfo = new Proxy(userinfo, {
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'))
}
})
console.log(Object.keys(userinfo)) // ["username", "age"]
deleteProperty 拦截delete proxy[propKey]的操作
let user = {
name: 'xiecheng',
age: 34,
_password: '***'
}
user = new Proxy(user, {
deleteProperty(target, prop) { // 拦截删除
if (prop.startsWith('_')) {
throw new Error('不可删除')
} else {
delete target[prop]
return true
}
}
})
delete user._password // Uncaught Error: 不可删除
apply 拦截 Proxy 实例作为函数调用的操作
let sum = (...args) => {
let num = 0
args.forEach(item => {
num += item
})
return num
}
sum = new Proxy(sum, {
apply(target, ctx, args) {
return target(...args) * 2
}
})
console.log(sum(1, 2)) // 6
console.log(sum.call(null, 1, 2, 3)) // 12
console.log(sum.apply(null, [1, 2, 3])) // 12
construct 拦截 Proxy 实例作为构造函数调用的操作
let User = class {
constructor(name) {
this.name = name
}
}
User = new Proxy(User, {
construct(target, args, newTarget) {
console.log('construct') // construct
return new target(...args)
}
})
console.log(new User('xiaoming')) // User {name: "xiaoming"}
以上是关于ES6(2015)Proxy的主要内容,如果未能解决你的问题,请参考以下文章
利用ES6中的Proxy和Reflect 实现简单的双向数据绑定