前言

什么是代理?

上小学的时候,李小红来你家叫你出去玩,第一个回应的不是你自己,是你妈:“王小明在家写作业,今天不出去!”

上中学的时候,赵二虎带着小弟们放学在校门口等着揍你,走在前面的不是你自己,是二虎他爸:“考试没及格还学会装黑社会了!”拎起二虎就是一顿胖揍。

上了大学,躺在宿舍里的床上,好饿。出门买饭并交代好不要葱蒜多放辣最后还直接端到床上的不是你自己,是快递小哥。

这些都是代理。

什么是 JavaScript 代理?

用官方的洋文来说,是 Proxy:

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

通过 Proxy 我们可以拦截并改变一个对象的几乎所有的根本操作,包括但不限于属性查找、赋值、枚举、函数调用等等。

在生活中,通过代理我们可以自动屏蔽小红的邀请、自动赶走二虎的威胁、自动买好干净的饭端到床上。在 JavaScript 世界里,代理也可以帮你做类似的事情,接下来让我们一起琢磨一番。

初识代理:Hello World

以小学经历为例子,心里是喜欢小红的,于是我们定义:

const me = { name: '小明', like: '小红' }
复制代码

这个时候如果调用 console.log(me.like),结果必然是 小红。然而生活并不是这样,作为一个未成年人,总是有各种的代理人围绕在你身边,比如这样:

const meWithProxy = new Proxy(me, {get(target, prop) {if (prop === 'like') {return '学习';}return target[prop];}
});
复制代码

这个时候如果调用 console.log(me.like) 依然是 小红 ,因为真心不会说谎。但当我们调用 console.log(meWithProxy.like) 的时候,就会可耻的输出 学习 ,告诉大家说我们喜欢的是 学习

小试牛刀:不要停止我的音乐

刚才我们简单了解了代理能够拦截对象属性的获取,可以隐藏真实的属性值而返回代理想要返回的结果,那么对于对象属性的赋值呢?让我们一起来看看。

假设你正在听音乐:

const me = { name: '小明', musicPlaying: true }
复制代码

此时如果我们执行 me.musicPlaying = false 这样就轻而易举地停止了你的音乐,那么如果我们挂上代理人:

const meWithProxy = new Proxy(me, {set(target, prop, value) {if (prop === 'musicPlaying' && value !== true) {throw Error('任何妄图停止音乐的行为都是耍流氓!');}target[prop] = value;}
});
复制代码

这时候如果我们执行 me.musicPlaying = false,就会被毫不留情地掀了桌子:

> meWithProxy.musicPlaying = false
Error: 任何妄图停止音乐的行为都是耍流氓!at Object.set (repl:4:13)
>
复制代码

释放魔法:封装全宇宙所有 RESTful API

现在我们已经知道通过 Proxy 可以拦截属性的读写操作,那然后呢?没什么用?

仅仅是拦截属性的读写操作,的确没有太大的发挥空间,或许可以方便的做一些属性赋值校验工作等等。但是,或许你还没有意识到一个惊人的秘密:Proxy 在拦截属性读写操作时,并不在乎属性是否真的存在!

那么,也就是说:利用 Proxy,我们可以拦截并不存在的属性的读取。

再进一步思考:利用 Proxy,我们可以在属性读取的那一瞬间,动态构造返回结果。

然而,属性并不局限于字符串、布尔值,属性可以是对象、函数、任何东西。

至此,你想到了什么?

没想到?不要紧!根据刚才的分析,让我们一起通过下面 17 行代码,来封装全宇宙所有的 RESTful API !

import axios from 'axios';
const api = new Proxy({}, {get(target, prop) {const method = /^[a-z]+/.exec(prop)[0];const path = '/' + prop.substring(method.length).replace(/([a-z])([A-Z])/g, '$1/$2').replace(/\$/g, '/$/').toLowerCase();return (...args) => { // <------ 返回动态构造的函数!const url = path.replace(/\$/g, () => args.shift());const options = args.shift() || {};console.log('Requesting: ', method, url, options);return axios({ method, url,  ...options });}}
});
复制代码

定义了 api 这个代理之后,我们就可以像下面这样调用:

api.get()
// GET /api.getUsers()
// 获取所有用户
// GET /usersapi.getUsers$Books(42)
// 获取 ID 为 42 的用户的所有书籍
// GET /users/42/booksapi.getUsers$Books(42, { params: { page: 2 } })
// 获取 ID 为 42 的用户的所有书籍的第二页
// GET /users/42/books?page=2api.postUsers({ data: { name: '小明' } })
// 创建名字为 小明 的用户
// POST /users Payload { name: '小明' }
复制代码

以上所有的函数都在你调用的那一瞬间,通过代理人的魔法之手动态生成,供我们随意取用。

简洁、优雅,哇~ 真是太棒啦!

终极魔幻:通读代理人的魔法秘笈

到此,我们仅仅使用 Proxy 改造了对象的属性获取、赋值操作,而对于 Proxy 来说,只是冰山一角。

Proxy 的基本语法如下:

new Proxy(target, handler)
复制代码

其中 target 是即将被代理的对象(比如:想要出门找小红玩耍的 me),handler 就是代理的魔法之手,用来拦截、改造 target 的行为。

对于 handler 对象,我们刚才仅仅用到了 getset 函数,而实际上一共有 13 种可代理的操作:

  • handler.getPrototypeOf()

    在读取代理对象的原型时触发该操作,比如在执行 Object.getPrototypeOf(proxy) 时。

  • handler.setPrototypeOf()

    在设置代理对象的原型时触发该操作,比如在执行 Object.setPrototypeOf(proxy, null) 时。

  • handler.isExtensible()

    在判断一个代理对象是否是可扩展时触发该操作,比如在执行 Object.isExtensible(proxy) 时。

  • handler.preventExtensions()

    在让一个代理对象不可扩展时触发该操作,比如在执行 Object.preventExtensions(proxy) 时。

  • handler.getOwnPropertyDescriptor()

    在获取代理对象某个属性的属性描述时触发该操作,比如在执行 Object.getOwnPropertyDescriptor(proxy, "foo") 时。

  • handler.defineProperty()

    在定义代理对象某个属性时的属性描述时触发该操作,比如在执行 Object.defineProperty(proxy, "foo", {}) 时。

  • handler.has()

    在判断代理对象是否拥有某个属性时触发该操作,比如在执行 "foo" in proxy 时。

  • handler.get()

    在读取代理对象的某个属性时触发该操作,比如在执行 proxy.foo 时。

  • handler.set()

    在给代理对象的某个属性赋值时触发该操作,比如在执行 proxy.foo = 1 时。

  • handler.deleteProperty()

    在删除代理对象的某个属性时触发该操作,比如在执行 delete proxy.foo 时。

  • handler.ownKeys()

    在获取代理对象的所有属性键时触发该操作,比如在执行 Object.getOwnPropertyNames(proxy) 时。

  • handler.apply()

    在调用一个目标对象为函数的代理对象时触发该操作,比如在执行 proxy() 时。

  • handler.construct()

    在给一个目标对象为构造函数的代理对象构造实例时触发该操作,比如在执行new proxy() 时。

对于以上 13 种可代理的操作,还需要读者自行研究并实践方可踏上终极魔幻之旅。

同学,我看好你。


参考链接:

  • Proxy - JavaScript | MDN

  • How to use JavaScript Proxies for Fun and Profit – DailyJS – Medium


关注微信公众号:创宇前端(KnownsecFED),码上获取更多优质干货!

JavaScript 魔幻代理相关推荐

  1. JavaScript事件代理和委托

    2019独角兽企业重金招聘Python工程师标准>>> 浏览器的事件冒泡 当事件发生后,这个事件就要开始传播.例如我们点击一个按钮时,就会产生一个click事件,但这个按钮本身不能处 ...

  2. javascript设计模式-代理模式

    代理的结构 代理模式的最基本形式就是对访问进行控制.代理对象和另一个对象实现的是同样的接口.代理对象只是节制对本体的访问. 代理对象不会在另一个对象上添加或修改方法,也不会简化对象的接口,所有对方法的 ...

  3. javascript事件代理(Event Delegation)

    看了几篇文章,放上来供参考 司徒正美的文章,Event Delegation Made Easy --------------------------------------------------- ...

  4. javascript事件代理

    一,事件介绍 1.事件用来实现js和html之间交互,网页中的每个元素都有一些事件属性可以触发事件处理函数. 2.一个完整的事件包含 (1)事件源 (html元素) (2)事件类型 (click,mo ...

  5. JavaScript:事件冒泡和事件委托

    2019独角兽企业重金招聘Python工程师标准>>> JavaScript事件代理和委托(Delegation) JavaScript事件冒泡和事件委托 JavaScript:通过 ...

  6. 深入浅出 Javascript 事件

    转载自:https://www.cnblogs.com/jingwhale/p/4656869.html 深入浅出 Javascript 事件 一.事件流 事件冒泡和事件捕获分别由微软和网景公司提出, ...

  7. 悟透JavaScript(美绘本)

    图书信息 作 者: 阿里软件资深架构师 李战 著 沉鱼 绘 出 版 社: 电子工业出版社 出版时间: 2008-12-1 页 数: 180页 开 本: 16开 I S B N : 9787121074 ...

  8. JavaScript常见面试题

    javascript面试题 1.你能描述一下渐进增强和优雅降级之间的不同吗? 优雅降级:Web站点在所有新式浏览器中都能正常工作,如果用户使用的是老式浏览器,则代码会检查以确认它们是否能正常工作.由于 ...

  9. JavaScript常见问题及答案

    1.你能描述一下渐进增强和优雅降级之间的不同吗? 优雅降级:Web站点在所有新式浏览器中都能正常工作,如果用户使用的是老式浏览器,则代码会检查以确认它们是否能正常工作.由于IE独特的盒模型布局问题,针 ...

最新文章

  1. spring boot读取yml配置集合,反射实战!
  2. 长亭技术专栏 安全攻防技术分享
  3. 计算机基础中怎么评价,浅谈职校计算机基础教学中的教学评价
  4. 【JavaScript脚本】——T1基本语法——重点笔记
  5. 【渝粤题库】陕西师范大学202961 教育社会学 作业(高起本、专升本)
  6. @RequestParam的作用
  7. 【 IT版 】啥是佩奇?
  8. 最短路径——Dijkstra算法扩展(hdu2066,poj1062)
  9. git 创建webpack项目_使用webpack手动创建一个完整项目的全过程
  10. 从编程语言进化史,看 Java、C、C++ 等语言的演变
  11. 电脑c盘怎么清理_电脑C盘内存不足?三分钟教你彻底清理C盘空间,瞬间多出10个G...
  12. python怎么读取excel-python 读取 Excel
  13. 运行java比较好的浏览器_国内好的pc浏览器评测
  14. 鱼雷武器控制系统半实物仿真系统ETest设计与实现
  15. Java 单点登录安全性如何保障?
  16. 关于“缓存着色(cache coloring, page coloring)”技术的相关资料介绍
  17. 字节跳动财务报表_【实锤】这一波疫情过后,字节跳动要上市了
  18. Nature综述|整合组学分析护航健康,推动精准医学时代的到来!
  19. pandas合并文件夹下的excel文件
  20. 请求和寻求帮助|Outreachy

热门文章

  1. jsp+springboot+ssm绘本馆活动报名系统javaEE图书借阅管理购买系统
  2. 百度好看视频3月起将提供10亿奖金 扶持原创作者
  3. 火狐FireFox和IE浏览器的title属性文本过长显示不全问题
  4. 118-C语言结构体——投票器的实现
  5. 会员积分(期末模拟)
  6. 授人以渔-在 SAP MM 物料显示界面上看到一个字段,如何查找哪张数据库表的哪个字段进行的存储
  7. 如何注册新加坡lol服务器,lol手游新加坡服怎么登录 新加坡服怎么注册[多图]
  8. Linux _ Shell编程 — 功能语句
  9. mysql性能优化2
  10. 零基础学SQL(十、子查询与多表关联)