目的
ES6 里新增了很多概念及语法, 有很多我们日常开发都会用到, 比如数组对象的解构, 箭头函数, class 等等, 但是类似 Proxy 这样的特性却很少用到 (个人观点), 借这个机会, 简单的过一遍 Proxy 相关的概念及适用场景
准备
完成这样一个任务, 我们需要知道哪些知识点呢?
Proxy 的基本概念
API 基本设计及规范, 这里附上 Google 的 RESTFUL API 设计规范
内容
一 Proxy 的基本概念及用法 case
MDN 对它的定义: Proxy 对象用于定义基本操作的自定义行为 (如属性查找, 赋值, 枚举, 函数调用等)
简单来说, 就是对即将处理的目标对象, 在操作的中间过程实现一个代理转发, 实现对目标对象的属性或者方法进行拦截过滤甚至修改
语法说明:
const p = new Proxy(target, handler)
target 表示所要拦截的目标对象
handler 也是一个对象, 用来定制拦截行为
其中 handler 也包含了许多的属性和方法, 其中 get()set() construct()apply() has() 方法是我们常用的几个方法
技能一无中生有
get 方法 , 可以让我们轻松地对所操作的对象进行访问拦截修改, 即使它是不存的属性或者方法
举个栗子:
- const extend = obj => {
- return new Proxy(obj, {
- get(target, propertyKey) {
- console.log(`New People: "${propertyKey}"`)
- target[propertyKey] = 'Welcome to Teambition NG!'
- return target
- }
- })
- }
- const object = { L: 'Welcome to Teambition!' }
- const extended = extend(object)
- console.log('object', extended.W)
输出如下
- New People: "W"
- object { L: 'Welcome to Teambition!', W: 'Welcome to Teambition NG!' }
在这个例子中, 我们在访问属性之前调用了 get(), 使我们的对象除了原有的属性之外, 实现了类似抽象属性的概念: 继承了原有的属性和方法的同时, 派生及重载出新的 key/value
值得一提的是: 如果一个属性
不可配置 (configurable)
和
不可写 (writable)
, 则该属性不能被代理, 通过 Proxy 对象访问该属性会报错
- const target = Object.defineProperties({}, {
- foo: {
- value: 123,
- writable: false,
- configurable: false
- },
- });
- const handler = {
- get(target, propKey) {
- return 'abc';
- }
- };
- const proxy = new Proxy(target, handler);
- proxy.foo
- // TypeError: Invariant check failed
技能二偷梁换柱
- const extend = obj => {
- return new Proxy(obj, {
- set(target, propertyKey, value) {
- if (propertyKey === 'J') {
- target[propertyKey] = 'I agree'
- }
- return target
- }
- })
- }
- const object = { L: 'Welcome to Teambition!' }
- const extended = extend(object)
- extended.J = 'I refuse!'
- console.log('extended', extended)
输出:
extended { L: 'Welcome to Teambition!', J: 'I agree' }
可以看见, 在原有欢迎致辞对象中, 角色 J 原本给出的是拒绝的信息: I refuse!, 但经过代理器内部的筛选及过滤, 输出为 I agree 大家可以 YY 一下类似的场景, 是不是可以用 Proxy 方法来更便捷实现呢?
除了以上两个技能之外, Proxy 还有这些技能能被我们应用
参数验证
属性替换
属性查找
二用 20 行代码写一个 API SDK
实现这样一个 Demo, 我们只需要理解, 当我们在读取某个代理对象的属性或者方法的时候, Proxy 的 get 方法将被调用, 通过 get 方法我们可以动态改变代理对象的属性和方法 例如, 我们可以有一个代理, 当使用 api.getUsers() 调用代理时, 它可以直接在 API 中实现 获取某个用户 (GET: /users/ ), 或者是获取某个用户喜欢的事 (GET: /users/xxx/likes)
有了这个约定, 我们还可以实现一个 POST 请求, 并且能够动态传入 body, 比如
api.postItems({name:'Item name'})
将第一个参数作为请求体调用 POST / items
完整栗子:
- const { METHODS } = require('http')
- const api = new Proxy({},
- {
- get(target, propKey) {
- const method = METHODS.find(method =>
- propKey.startsWith(method.toLowerCase()))
- if (!method) return
- const path =
- '/' +
- propKey
- .substring(method.length)
- .replace(/([a-z])([A-Z])/g, '$1/$2')
- .replace(/\$/g, '/$/')
- .toLowerCase()
- return (...args) => {
- const finalPath = path.replace(/\$/g, () => args.shift())
- const queryOrBody = args.shift() || {}
- console.log(method, finalPath, queryOrBody)
- }
- }
- }
- )
- api.get()
- api.getUsers()
- api.getUsers$Likes('1234')
- api.getUsers$Likes('1234', { page: 2 })
- api.postItems({ name: 'Item name' })
- api.foobar()
输出:
- GET / {}
- GET /users {}
- GET /users/1234/likes {}
- GET /users/1234/likes { page: 2 }
- POST /items { name: 'Item name' }
- /Users/jiangwei/git/test/block/demo2.js:36
- api.foobar()
- ^
- TypeError: api.foobar is not a function
- at Object.<anonymous> (/Users/jiangwei/git/test/block/demo2.js:36:5)
- at Module._compile (module.js:643:30)
- at Object.Module._extensions..js (module.js:654:10)
- at Module.load (module.js:556:32)
- at tryModuleLoad (module.js:499:12)
- at Function.Module._load (module.js:491:3)
- at Function.Module.runMain (module.js:684:10)
- at startup (bootstrap_node.js:187:16)
- at bootstrap_node.js:608:3
这里我们代理的对象原本是一个空对象, 所有的方法都是通过 get 属性动态实现通过简单的 Proxy 相关属性和方法的运用, 结合正则, 解析出我们想要的数据结构并使用
参考文献:
- es6.ruanyifeng.com/#docs/proxy
- developer.mozilla.org/zh-CN/docs/
来源: https://juejin.im/entry/5ab3072a51882555745998e8