2-6 Proxy 代理

60 阅读3分钟

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'))
  • 对于当前操作进行代理,在使用相对于的方法处理