Object.defineProperty 和 ES6 的 Proxy 是 JavaScript 中两种不同的特性,它们有一些区别和不同的用途。
Object.defineProperty: 这是一个用于修改或定义对象属性的方法。它允许你在对象上定义新的属性,或者修改已存在的属性的特性(如可写性、可枚举性、可配置性等)。你可以使用 Object.defineProperty 方法来精确地控制属性的行为。这个方法适用于操作单个属性,并且只能监视已经存在的属性。
ES6 的 Proxy: 这是一个可以拦截对象操作的机制。Proxy 可以拦截并定义自定义的行为,例如访问属性、修改属性、删除属性等。通过使用 Proxy,你可以在对象上定义一个拦截器,捕获并处理对对象的各种操作。Proxy 是一种更为强大和灵活的机制,它可以用于创建一个代理对象,对代理对象的操作进行拦截,并在必要时自定义处理逻辑。
ES5
let obj = {}
let newVal = ''
Object.defineProperty(obj, 'name', {
get(){
console.log('get')
return newVal
},
set(){
console.log('set')
newVal = val
}
})
obj.name = 'es'
console.log(obj.name)
Proxy
- 第一个参数是当前需要拦截的,第二个是代理的配置 hock
let obj = {}
let p = new Proxy(obj, {}){}
p.name = 'UU'
console.log(obj.name) // UU
for(let key in obj){
console,log(key) // name
}
Proxy 常用的拦截方法
常用的方式
let arr = [7, 8, 9]
arr = new proxy(arr, {
get(target, prop){
// console.log(target, prop)
return prop in target ? target[prop] : 'error'
}
})
// console.log(arr[1]) // [7, 8, 9] "1"
console.log(arr[1]) // 8
console.log(arr[10]) // error
get ( 拦截对象属性的读取)
let dict = {
'hello': '你好',
'world': '世界'
}
dict = new Proxy(dict,{
get(target, prop) {
return prop in target ? target[prop] : prop
}
})
console.log(dict['world'])
console.log(dict['uu'])
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]) // 5 6
has 判断数值区间 返回布尔值
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)
ownKeys 用于对象循环遍历进行拦截操作
let obj = {
name: 'UU',
[Symbol('es')]: 'es6'
}
console.log(Object.getOwnPropertyNames(obj))
console.log(Object.getOwnPropertySymbols(obj))
console.log(Object.keys(obj))
for(let key in obj){
console.log(key)
}
过滤掉 _ 开头的属性
let userinfo = {
username: 'UU',
age: 4,
_password: '***'
}
userinfo = new Proxy(userinfo, {
oenKeys(target){
return Object.keys(target).filter(key => !key.startsWith('_'))
}
})
for(let key in userinfo){
console.log(key)
}
console.log(Object.keys(userinfo))
deleteProperty 对于删除对象里面某一个属性的时候进行拦截,返回布尔值
let user = {
name: 'UU',
age: 4,
_password: '***'
}
user = new Proxy(user, {
get(target, prop) {
if(prop.startsWith('_')){
throw new Error('不可访问')
} else {
return target[prop]
}
},
set(target, prop, val){
if(prop.startsWith('_')){
throw new Error('不可访问')
} else {
target[prop] = val
return true
}
}
deleteProperty(){ // 删除拦截
if(prop.startsWith('_')){
throw new Error('不可删除')
} else {
delete target[prop]
return true
}
},
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'))
}
})
user.age = 18
console.log(user.age) // 18
// user._password = 'xxx'
// console.log(user._password) // 报错
// 下面的写法会更加规范,并且可以打印报错
try{
user._password = 'xxx'
} catch(e){
console.log(e.message) // 不可访问
}
try{
delete user._password // 不可删除
} catch(e){
console.log(e.message)
}
apply (用于拦截函数的调用 以及call apply的调用)
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(null, 1, 2, 3)) // 12
console.log(sum.apply(null, \[1, 2, 3])) // 12
- 当使用 call 或者 apply 调用函数的时候,就会被 apply() 拦截,可以使用这个拦截方式去修改函数的返回值
construct (当我们new一个Class想得到一个实例化对象的时候就会经过construct 拦截器,返回一个对象)
let User = class {
constructor(name) {
this.name = name
}
}
User = new Proxy(User, {
construct(target, args, newTarget){
return new target(...args)
}
})
console.log(new User('uu'))
- 对于当前操作进行代理,在使用相对于的方法处理