上小学的时候,李小红来你家叫你出去玩,第一个回应的不是你本身,是你妈:“王小明在家写做业,今天不出去!”javascript
上中学的时候,赵二虎带着小弟们放学在校门口等着揍你,走在前面的不是你本身,是二虎他爸:“考试没及格还学会装黑社会了!”拎起二虎就是一顿胖揍。前端
上了大学,躺在宿舍里的床上,好饿。出门买饭并交代好不要葱蒜多放辣最后还直接端到床上的不是你本身,是快递小哥。java
这些都是代理。ios
用官方的洋文来讲,是 Proxy:axios
The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).api
经过 Proxy 咱们能够拦截并改变一个对象的几乎全部的根本操做,包括但不限于属性查找、赋值、枚举、函数调用等等。微信
在生活中,经过代理咱们能够自动屏蔽小红的邀请、自动赶走二虎的威胁、自动买好干净的饭端到床上。在 JavaScript 世界里,代理也能够帮你作相似的事情,接下来让咱们一块儿琢磨一番。app
以小学经历为例子,内心是喜欢小红的,因而咱们定义:函数
const me = { name: '小明', like: '小红' }
复制代码
这个时候若是调用 console.log(me.like)
,结果必然是 小红
。然而生活并非这样,做为一个未成年人,老是有各类的代理人围绕在你身边,好比这样:post
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)
>
复制代码
如今咱们已经知道经过 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 /users
api.getUsers$Books(42)
// 获取 ID 为 42 的用户的全部书籍
// GET /users/42/books
api.getUsers$Books(42, { params: { page: 2 } })
// 获取 ID 为 42 的用户的全部书籍的第二页
// GET /users/42/books?page=2
api.postUsers({ data: { name: '小明' } })
// 建立名字为 小明 的用户
// POST /users Payload { name: '小明' }
复制代码
以上全部的函数都在你调用的那一瞬间,经过代理人的魔法之手动态生成,供咱们随意取用。
简洁、优雅,哇~ 真是太棒啦!
到此,咱们仅仅使用 Proxy 改造了对象的属性获取、赋值操做,而对于 Proxy 来讲,只是冰山一角。
Proxy 的基本语法以下:
new Proxy(target, handler)
复制代码
其中 target
是即将被代理的对象(好比:想要出门找小红玩耍的 me
),handler
就是代理的魔法之手,用来拦截、改造 target
的行为。
对于 handler
对象,咱们刚才仅仅用到了 get
、set
函数,而实际上一共有 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 种可代理的操做,还须要读者自行研究并实践方可踏上终极魔幻之旅。
同窗,我看好你。
参考连接:
关注微信公众号:创宇前端(KnownsecFED),码上获取更多优质干货!