乐趣区

关于es6:ES6-系列之-Proxy

本文同步发表在 Github 集体博客:ES6 系列之 Proxy

前言

前几天模仿实现了 MobX 的两个函数 —— 手写实现 MobX 的 observable 和 autorun 办法,其中用到了 Proxy,所以打算再对 Proxy 深刻理解一下,做个笔记。

Proxy 是什么

Proxy 对象用于创立一个对象的代理,从而实现基本操作的拦挡和自定义(如属性查找、赋值、枚举、函数调用等)。能够了解成,在指标对象之前架设一层“拦挡”,外界对该对象的拜访,都必须先通过这层拦挡,因而提供了一种机制,能够对外界的拜访进行过滤和改写。

const p = new Proxy(target, handler)

  • target: 应用 Proxy 包装的指标对象(能够是任何类型的 JavaScript 对象,包含原生数组,函数,甚至另一个代理)。
  • handler: 一个通常以函数作为属性的对象,用来定制拦挡行为。

在反对 Proxy 的浏览器环境中,Proxy 是一个全局对象,能够间接应用。Proxy(target, handler)是一个构造函数,target是被代理的对象,最终返回一个代理对象。

为什么须要 Proxy

学习一样货色之前咱们先要想想为什么须要它,在我看来,个别几种状况。

  1. 被代理的对象不想间接被拜访
  2. 管制和批改被代理对象的行为(调用属性、属性赋值、办法调用等等),使之能够进行访问控制和减少性能。

API

API 概览如下:

  • get(target, propKey, receiver):拦挡对象属性的读取,比方 proxy.foo 和 proxy[‘foo’]。
  • set(target, propKey, value, receiver):拦挡对象属性的设置,比方 proxy.foo = v 或 proxy[‘foo’] = v,返回一个布尔值。
  • has(target, propKey):拦挡 propKey in proxy 的操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦挡 delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦挡 Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in 循环,返回一个数组。该办法返回指标对象所有本身的属性的属性名,而 Object.keys() 的返回后果仅包含指标对象本身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey):拦挡 Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的形容对象。
  • defineProperty(target, propKey, propDesc):拦挡 Object.defineProperty(proxy, propKey, propDesc)、
  • Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • 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 实例作为函数调用的操作,比方 proxy(…args)、proxy.call(object, …args)、proxy.apply(…)`。
  • construct(target, args):拦挡 Proxy 实例作为结构函数调用的操作,比方 new proxy(…args)。

最罕用的办法就是 getset

例子

get

const target = {
  name: 'jacky',
  sex: 'man',
}
const handler = {get(target, key) {console.log('获取名字 / 性别')
    return Reflect.get(target, key)
  },
}
const proxy = new Proxy(target, handler)
console.log(proxy.name)

运行,打印台输入:

获取名字 / 性别
jacky

在获取 name 属性是先进入 get 办法,在 get 办法外面打印了 获取名字 / 性别 ,而后通过Reflect.get(target, key) 的返回值拿到属性值,相当于target[key]

set

const target = {
  name: 'jacky',
  sex: 'man',
}
const handler = {get(target, key) {console.log('获取名字 / 性别')
    return Reflect.get(target, key)
  },
  set(target, key, value) {return Reflect.set(target, key, ` 强行设置为 ${value}`)
  },
}
const proxy = new Proxy(target, handler)
proxy.name = 'monkey'
console.log(proxy.name)

运行输入:

获取名字 / 性别
强行设置 monkey

设置 proxy.name = 'monkey',这是批改属性的值,则会触发到set 办法,而后咱们强行更改设置的值再返回,达到拦挡对象属性的设置的目标。


Reflect 对象与 Proxy 对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect.get(target, name, receiver):查找并返回 target 对象的 name 属性,如果没有该属性,则返回 undefined。Reflect.set(target, name, value, receiver):设置 target 对象的 name 属性等于 value。

this 指向

proxy 会扭转 target 中的 this 指向,一旦 Proxy 代理了 target,target 外部的 this 则指向了 Proxy 代理

const target = new Date('2021-01-03')
const handler = {get(target, prop) {if (prop === 'getDate') {return target.getDate(target)
    }
    return Reflect.get(target, prop)
  },
}
const proxy = new Proxy(target, handler)

console.log(proxy.getDate())

运行代码,会发现报错,提醒TypeError: proxy.getDate is not a function,即 this 不是 Date 对象的实例,这时须要咱们手动绑定原始对象即可解决:

const target = new Date('2021-01-03')
const handler = {get(target, prop) {if (prop === 'getDate') {return target.getDate.bind(target) // 绑定
    }
    return Reflect.get(target, prop)
  },
}
const proxy = new Proxy(target, handler)

console.log(proxy.getDate()) // 3

利用场景

  • 正告或阻止特定操作
  • get 办法取不到对应值能够返回咱们想指定的其它值
  • 数据校验。判断数据是否满足条件

等等 …

参考

  • https://es6.ruanyifeng.com/#docs/proxy

ps:

  • 集体技术博文 Github 仓库

感觉不错的话赏个 star,给我继续创作的能源吧!

退出移动版