zl程序教程

您现在的位置是:首页 >  前端

当前栏目

【笔记】再学JavaScript ES(6-10)全版本语法——Proxy代理

JavaScriptES代理笔记 版本 10 语法 Proxy
2023-09-27 14:26:51 时间


Proxy

关于Proxy代理,最喜欢阮一峰大佬的描述:

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

嗯,说的相当明白了,下面来看几个使用场景

场景1:保护对象,使其对外不可改,密码不可见

ES5

let obj = {
  user: 'admin',
  password: '123456'
}

let _obj = obj
_obj.password = '******'

for (let [key] of Object.entries(_obj)) {
  Object.defineProperty(_obj, key, {
    writable: false
  })
}

_obj.user = 'Admin' // 由于数据设置了不可写,此步无效
console.log(_obj.user, _obj.password) // admin ******

ES6

let obj = {
  user: 'admin',
  password: '123456'
}

let pro = new Proxy(obj, {
  get (target, key) {
    if (key === 'password') {
      return '******'
    } else {
      return target[key] || ''
    }
  },
  set (target, key, value) {
    return false
  }
})

pro.user = 'Admin' // 由于数据通过代理设置了保护因此无法修改,此步无效
console.log(pro.user, pro.password) // admin ******

场景2:保护数据结构和限制密码位数并统一错误处理

// 监听错误
window.addEventListener('error', (e) => {
  console.log(e.message)
  // report('./')
}, true)

let obj2 = {
  user: 'admin',
  password: '123456'
}

let getter = (target, key) => {
  if (key === 'password') {
    return '******'
  } else {
    return target[key] || ''
  }
}
let setter = (target, key, value) => {
  if (Reflect.has(target, key)) {
    if (key === 'password') {
      if (value || value.length > 6) {
        throw new TypeError('密码超出6位,无效')
        // console.log('密码超出6位,无效')
        // return false
      }
    } else {
      target[key] = value
    }
  } else {
    console.log('无此属性,无效')
    return false
  }
}

let pro2 = new Proxy(obj2, {
  get: getter,
  set: setter
})

pro2.user = 'Admin' // 有效操作
pro2.password = '1234567' // 密码超出6位,无效
pro2.age = 18 // 无此属性,无效
console.log(pro2.user, pro2.password) // Admin ******

场景3:为每个用户生成一个id(唯一,只读)

class User {
  constructor () {
    this.proxy = new Proxy({
      id: Math.random().toString(36).slice(-8) // 转为36进制并截取后八位
    }, {})
  }
  get id () {
    return this.proxy.id
  }
}

let user1 = new User()
let user2 = new User()
for (let i = 0; i < 6; i++) {
  console.log(user1.id, user2.id)
}
user1.id = 'abc'
console.log(user1.id, user2.id) // user1的id没有变,说明只读

场景4:临时代理(可撤销)

可撤销的代理和普通代理不同,代理数据存放于它的proxy属性中,通过它的另一个属性revoke可执行代理撤销,其它使用方式不变

let obj = {
  user: 'admin',
  password: '123456'
}

// 可撤销的代理
let pro = Proxy.revocable(obj, {
  get (target, key) {
    if (key === 'password') {
      return '******'
    } else {
      return target[key] || ''
    }
  },
  set (target, key, value) {
    return false
  }
})

console.log(pro)
console.log(pro.proxy.user, pro.proxy.password) // admin ******
setTimeout(() => {
  pro.revoke()
  setTimeout(() => {
    console.log(pro.proxy.user) // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
  }, 100)
}, 1000)

Proxy 支持的拦截操作一览,一共 13 种:

  • get(target, propKey, receiver):拦截对象属性的读取。
  • set(target, propKey, value, receiver):拦截对象属性的设置。
  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames、Object.getOwnPropertySymbols、Object.keys、for…in循环,返回一个数组。
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor,返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty、Object.defineProperties,返回一个布尔值。
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作。
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作。

拓展: