实例解析ES6 Proxy使用场景
来源:http://www.w3cplus.com/javascript/use-cases-for-es6-proxies.html<p></p><div><br></div><div><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">ES6 中的箭头函数、数组解构、rest 参数等特性一经实现就广为流传,但类似 Proxy 这样的特性却很少见到有开发者在使用,一方面在于浏览器的兼容性,另一方面也在于要想发挥这些特性的优势需要开发者深入地理解其使用场景。就我个人而言是非常喜欢 ES6 的 Proxy,因为它让我们以简洁易懂的方式控制了外部对对象的访问。在下文中,首先我会介绍 Proxy 的使用方式,然后列举具体实例解释 Proxy 的使用场景。</p><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">Proxy,见名知意,其功能非常类似于设计模式中的代理模式,该模式常用于三个方面:</p><ul style="box-sizing: inherit; margin-top: 10px; margin-bottom: 25px; list-style: none outside none; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif; line-height: 20px;"><li style="box-sizing: inherit; margin-left: 0px; list-style: none outside none; line-height: 28px;">拦截和监视外部对对象的访问</li><li style="box-sizing: inherit; margin-left: 0px; list-style: none outside none; line-height: 28px;">降低函数或类的复杂度</li><li style="box-sizing: inherit; margin-left: 0px; list-style: none outside none; line-height: 28px;">在复杂操作前对操作进行校验或对所需资源进行管理</li></ul><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">在支持 Proxy 的浏览器环境中,Proxy 是一个全局对象,可以直接使用。<code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">Proxy(target, handler)</code> 是一个构造函数,<code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">target</code> 是被代理的对象,<code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">handlder</code> 是声明了各类代理操作的对象,最终返回一个代理对象。外界每次通过代理对象访问<code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">target</code> 对象的属性时,就会经过 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">handler</code> 对象,从这个流程来看,代理对象很类似 middleware(中间件)。那么 Proxy 可以拦截什么操作呢?最常见的就是 get(读取)、set(修改)对象属性等操作,完整的可拦截操作列表请点击<a href="http://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots" target="_blank" style="box-sizing: inherit; color: rgb(41, 180, 240); text-decoration: none; background: transparent;">这里</a>。此外,Proxy 对象还提供了一个 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">revoke</code> 方法,可以随时注销所有的代理操作。在我们正式介绍 Proxy 之前,建议你对 Reflect 有一定的了解,它也是一个 ES6 新增的全局对象,详细信息请参考 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect" target="_blank" style="box-sizing: inherit; color: rgb(41, 180, 240); text-decoration: none; background: transparent;">MDN Reflect</a>。</p><h2 style="box-sizing: inherit; margin-bottom: 15px; font-size: 1.4em; color: rgb(0, 163, 207); border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(221, 221, 221); padding-bottom: 5px; font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">Basic</h2><pre class="cs" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="cs" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> target = {name: <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'Billy Bob'</span>,
age: <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">15</span>
};
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> handler = {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">get</span>(target, key, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> today = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Date();
console.log(`GET request made <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">for</span> ${key} at ${today}`);
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">get</span>(target, key, proxy);
}
};
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> proxy = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Proxy(target, handler);
proxy.name;
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// => "GET request made for name at Thu Jul 21 2016 15:26:20 GMT+0800 (CST)"</span>
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// => "Billy Bob"</span>
</code></pre><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">在上面的代码中,我们首先定义了一个被代理的目标对象 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">target</code>,然后声明了包含所有代理操作的 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">handler</code> 对象,接下来使用<code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">Proxy(target, handler)</code> 创建代理对象 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">proxy</code>,此后所有使用 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">proxy</code> 对 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">target</code> 属性的访问都会经过 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">handler</code> 的处理。</p><h2 style="box-sizing: inherit; margin-bottom: 15px; font-size: 1.4em; color: rgb(0, 163, 207); border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(221, 221, 221); padding-bottom: 5px; font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">1. 抽离校验模块</h2><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">让我们从一个简单的类型校验开始做起,这个示例演示了如何使用 Proxy 保障数据类型的准确性:</p><pre class="cs" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="cs" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> numericDataStore = {
count: <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">0</span>,
amount: <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">1234</span>,
total: <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">14</span>
};
numericDataStore = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Proxy(numericDataStore, {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">set</span>(target, key, <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">typeof</span> <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span> !== <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'number'</span>) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">throw</span> Error(<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"Properties in numericDataStore can only be numbers"</span>);
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">set</span>(target, key, <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>, proxy);
}
});
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// 抛出错误,因为 "foo" 不是数值</span>
numericDataStore.count = <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"foo"</span>;
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// 赋值成功</span>
numericDataStore.count = <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">333</span>;
</code></pre><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">如果要直接为对象的所有属性开发一个校验器可能很快就会让代码结构变得臃肿,使用 Proxy 则可以将校验器从核心逻辑分离出来自成一体:</p><pre class="cs" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="cs" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;">function createValidator(target, validator) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Proxy(target, {
_validator: validator,
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">set</span>(target, key, <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (target.hasOwnProperty(key)) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> validator = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">this</span>._validator;
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (!!validator(<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>)) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">set</span>(target, key, <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>, proxy);
} <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">else</span> {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">throw</span> Error(`Cannot <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">set</span> ${key} to ${<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>}. Invalid.`);
}
} <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">else</span> {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">throw</span> Error(`${key} <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">is</span> not a valid property`)
}
}
});
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> personValidators = {
name(val) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">typeof</span> val === <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'string'</span>;
},
age(val) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">typeof</span> age === <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'number'</span> && age > <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">18</span>;
}
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">class</span> Person {
constructor(name, age) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">this</span>.name = name;
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">this</span>.age = age;
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> createValidator(<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">this</span>, personValidators);
}
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> bill = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Person(<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'Bill'</span>, <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">25</span>);
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// 以下操作都会报错</span>
bill.name = <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">0</span>;
bill.age = <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'Bill'</span>;
bill.age = <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">15</span>;
</code></pre><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">通过校验器和主逻辑的分离,你可以无限扩展 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">personValidators</code> 校验器的内容,而不会对相关的类或函数造成直接破坏。更复杂一点,我们还可以使用 Proxy 模拟类型检查,检查函数是否接收了类型和数量都正确的参数:</p><pre class="javascript" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="javascript" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> obj = {
pickyMethodOne: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(obj, str, num) { <span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">/* ... */</span> },
pickyMethodTwo: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(num, obj) { <span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">/*... */</span> }
};
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> argTypes = {
pickyMethodOne: [<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"object"</span>, <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"string"</span>, <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"number"</span>],
pickyMethodTwo: [<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"number"</span>, <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"object"</span>]
};
obj = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Proxy(obj, {
get: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(target, key, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> value = target;
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(...args) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> checkArgs = argChecker(key, args, argTypes);
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.apply(value, target, args);
};
}
});
<span class="function" style="box-sizing: inherit;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);"><span class="function" style="box-sizing: inherit;"><span class="keyword" style="box-sizing: inherit;">function</span></span></span><span class="function" style="box-sizing: inherit;"> <span class="title" style="box-sizing: inherit; color: rgb(38, 139, 210);"><span class="title" style="box-sizing: inherit;">argChecker</span></span><span class="params" style="box-sizing: inherit;"><span class="params" style="box-sizing: inherit;">(name, args, checkers)</span></span> {</span></span>
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">for</span> (<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> idx = <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">0</span>; idx < args.length; idx++) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> arg = args;
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> type = checkers;
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (!arg || <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">typeof</span> arg !== type) {
console.warn(`You are incorrectly implementing the signature of ${name}. Check param ${idx + <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">1</span>}`);
}
}
}
obj.pickyMethodOne();
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// > You are incorrectly implementing the signature of pickyMethodOne. Check param 1</span>
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// > You are incorrectly implementing the signature of pickyMethodOne. Check param 2</span>
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// > You are incorrectly implementing the signature of pickyMethodOne. Check param 3</span>
obj.pickyMethodTwo(<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"wopdopadoo"</span>, {});
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// > You are incorrectly implementing the signature of pickyMethodTwo. Check param 1</span>
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// No warnings logged</span>
obj.pickyMethodOne({}, <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"a little string"</span>, <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">123</span>);
obj.pickyMethodOne(<span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">123</span>, {});
</code></pre><h2 style="box-sizing: inherit; margin-bottom: 15px; font-size: 1.4em; color: rgb(0, 163, 207); border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(221, 221, 221); padding-bottom: 5px; font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">2. 私有属性</h2><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">在 JavaScript 或其他语言中,大家会约定俗成地在变量名之前添加下划线 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">_</code> 来表明这是一个私有属性(并不是真正的私有),但我们无法保证真的没人会去访问或修改它。在下面的代码中,我们声明了一个私有的 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">apiKey</code>,便于 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">api</code> 这个对象内部的方法调用,但不希望从外部也能够访问 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">api._apiKey</code>:</p><pre class="javascript" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="javascript" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> api = {
_apiKey: <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'123abc456def'</span>,
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">/* mock methods that use this._apiKey */</span>
getUsers: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(){},
getUser: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(userId){},
setUser: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(userId, config){}
};
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// logs '123abc456def';</span>
console.log(<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"An apiKey we want to keep private"</span>, api._apiKey);
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// get and mutate _apiKeys as desired</span>
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> apiKey = api._apiKey;
api._apiKey = <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'987654321'</span>;
</code></pre><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">很显然,约定俗成是没有束缚力的。使用 ES6 Proxy 我们就可以实现真实的私有变量了,下面针对不同的读取方式演示两个不同的私有化方法。第一种方法是使用 set / get 拦截读写请求并返回 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">undefined</code>:</p><pre class="cs" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="cs" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> api = {
_apiKey: <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'123abc456def'</span>,
getUsers: function(){ },
getUser: function(userId){ },
setUser: function(userId, config){ }
};
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> RESTRICTED = [<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'_apiKey'</span>];
api = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Proxy(api, {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">get</span>(target, key, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span>(RESTRICTED.indexOf(key) > -<span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">1</span>) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">throw</span> Error(`${key} <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">is</span> restricted. Please see api documentation <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">for</span> further info.`);
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">get</span>(target, key, proxy);
},
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">set</span>(target, key, <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span>(RESTRICTED.indexOf(key) > -<span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">1</span>) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">throw</span> Error(`${key} <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">is</span> restricted. Please see api documentation <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">for</span> further info.`);
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">get</span>(target, key, <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>, proxy);
}
});
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// 以下操作都会抛出错误</span>
console.log(api._apiKey);
api._apiKey = <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'987654321'</span>;
</code></pre><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">第二种方法是使用 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">has</code> 拦截 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">in</code> 操作:</p><pre class="javascript" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="javascript" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> api = {
_apiKey: <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'123abc456def'</span>,
getUsers: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(){ },
getUser: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(userId){ },
setUser: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(userId, config){ }
};
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> RESTRICTED = [<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'_apiKey'</span>];
api = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Proxy(api, {
has(target, key) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> (RESTRICTED.indexOf(key) > -<span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">1</span>) ?
<span class="literal" style="box-sizing: inherit;">false</span> :
Reflect.has(target, key);
}
});
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// these log false, and `for in` iterators will ignore _apiKey</span>
console.log(<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"_apiKey"</span> <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">in</span> api);
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">for</span> (<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> key <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">in</span> api) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (api.hasOwnProperty(key) && key === <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"_apiKey"</span>) {
console.log(<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"This will never be logged because the proxy obscures _apiKey..."</span>)
}
}
</code></pre><h2 style="box-sizing: inherit; margin-bottom: 15px; font-size: 1.4em; color: rgb(0, 163, 207); border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(221, 221, 221); padding-bottom: 5px; font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">3. 访问日志</h2><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">对于那些调用频繁、运行缓慢或占用执行环境资源较多的属性或接口,开发者会希望记录它们的使用情况或性能表现,这个时候就可以使用 Proxy 充当中间件的角色,轻而易举实现日志功能:</p><pre class="javascript" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="javascript" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> api = {
_apiKey: <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'123abc456def'</span>,
getUsers: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>() { <span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">/* ... */</span> },
getUser: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(userId) { <span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">/* ... */</span> },
setUser: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(userId, config) { <span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">/* ... */</span> }
};
<span class="function" style="box-sizing: inherit;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);"><span class="function" style="box-sizing: inherit;"><span class="keyword" style="box-sizing: inherit;">function</span></span></span><span class="function" style="box-sizing: inherit;"> <span class="title" style="box-sizing: inherit; color: rgb(38, 139, 210);"><span class="title" style="box-sizing: inherit;">logMethodAsync</span></span><span class="params" style="box-sizing: inherit;"><span class="params" style="box-sizing: inherit;">(timestamp, method)</span></span> {</span></span>
setTimeout(<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>() {
console.log(`${timestamp} - Logging ${method} request asynchronously.`);
}, <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">0</span>)
}
api = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Proxy(api, {
get: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(target, key, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> value = target;
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(...arguments) {
logMethodAsync(<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Date(), key);
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.apply(value, target, arguments);
};
}
});
api.getUsers();
</code></pre><h2 style="box-sizing: inherit; margin-bottom: 15px; font-size: 1.4em; color: rgb(0, 163, 207); border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(221, 221, 221); padding-bottom: 5px; font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">4. 预警和拦截</h2><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">假设你不想让其他开发者删除 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">noDelete</code> 属性,还想让调用 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">oldMethod</code> 的开发者了解到这个方法已经被废弃了,或者告诉开发者不要修改 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">doNotChange</code> 属性,那么就可以使用 Proxy 来实现:</p><pre class="cs" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="cs" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> dataStore = {
noDelete: <span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">1235</span>,
oldMethod: function() {<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">/*...*/</span> },
doNotChange: <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"tried and true"</span>
};
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> NODELETE = [<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'noDelete'</span>];
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> NOCHANGE = [<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'doNotChange'</span>];
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> DEPRECATED = [<span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'oldMethod'</span>];
dataStore = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Proxy(dataStore, {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">set</span>(target, key, <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (NOCHANGE.includes(key)) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">throw</span> Error(`Error! ${key} <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">is</span> immutable.`);
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">set</span>(target, key, <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">value</span>, proxy);
},
deleteProperty(target, key) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (NODELETE.includes(key)) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">throw</span> Error(`Error! ${key} cannot be deleted.`);
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.deleteProperty(target, key);
},
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">get</span>(target, key, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (DEPRECATED.includes(key)) {
console.warn(`Warning! ${key} <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">is</span> deprecated.`);
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">var</span> val = target;
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">typeof</span> val === <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'function'</span> ?
function(...args) {
Reflect.apply(target, target, args);
} :
val;
}
});
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// these will throw errors or log warnings, respectively</span>
dataStore.doNotChange = <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">"foo"</span>;
delete dataStore.noDelete;
dataStore.oldMethod();
</code></pre><h2 style="box-sizing: inherit; margin-bottom: 15px; font-size: 1.4em; color: rgb(0, 163, 207); border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(221, 221, 221); padding-bottom: 5px; font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">5. 过滤操作</h2><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">某些操作会非常占用资源,比如传输大文件,这个时候如果文件已经在分块发送了,就不需要在对新的请求作出相应(非绝对),这个时候就可以使用 Proxy 对当请求进行特征检测,并根据特征过滤出哪些是不需要响应的,哪些是需要响应的。下面的代码简单演示了过滤特征的方式,并不是完整代码,相信大家会理解其中的妙处:</p><pre class="javascript" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="javascript" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> obj = {
getGiantFile: <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(fileId) {<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">/*...*/</span> }
};
obj = <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">new</span> Proxy(obj, {
get(target, key, proxy) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">function</span>(...args) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> id = args[<span class="number" style="box-sizing: inherit; color: rgb(42, 161, 152);">0</span>];
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> isEnroute = checkEnroute(id);
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> isDownloading = checkStatus(id);
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> cached = getCached(id);
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (isEnroute || isDownloading) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> <span class="literal" style="box-sizing: inherit;">false</span>;
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">if</span> (cached) {
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> cached;
}
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">return</span> Reflect.apply(target, target, args);
}
}
});
</code></pre><h2 style="box-sizing: inherit; margin-bottom: 15px; font-size: 1.4em; color: rgb(0, 163, 207); border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(221, 221, 221); padding-bottom: 5px; font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">6. 中断代理</h2><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">Proxy 支持随时取消对 <code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">target</code> 的代理,这一操作常用于完全封闭对数据或接口的访问。在下面的示例中,我们使用了<code style="box-sizing: inherit; font-family: Menlo, Monaco, "Andale Mono", "lucida console", "Courier New", monospace; font-size: 1em; padding: 1px 3px; margin-right: 3px; margin-left: 3px; border: 1px solid rgb(204, 204, 204); background: rgb(221, 221, 221);">Proxy.revocable</code> 方法创建了可撤销代理的代理对象:</p><pre class="javascript" style="box-sizing: inherit; overflow: auto; font-family: monospace, monospace; padding: 0.5em; color: rgb(131, 148, 150); margin-bottom: 20px; line-height: 20px; background: rgb(0, 43, 54);"><code class="javascript" style="box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; display: block; padding: 0.5em; background-image: initial; background-attachment: initial; background-size: initial; background-origin: initial; background-clip: initial; background-position: initial; background-repeat: initial;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">let</span> sensitiveData = { username: <span class="string" style="box-sizing: inherit; color: rgb(42, 161, 152);">'devbryce'</span> };
<span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);">const</span> {sensitiveData, revokeAccess} = Proxy.revocable(sensitiveData, handler);
<span class="function" style="box-sizing: inherit;"><span class="keyword" style="box-sizing: inherit; color: rgb(133, 153, 0);"><span class="function" style="box-sizing: inherit;"><span class="keyword" style="box-sizing: inherit;">function</span></span></span><span class="function" style="box-sizing: inherit;"> <span class="title" style="box-sizing: inherit; color: rgb(38, 139, 210);"><span class="title" style="box-sizing: inherit;">handleSuspectedHack</span></span><span class="params" style="box-sizing: inherit;"><span class="params" style="box-sizing: inherit;">()</span></span>{</span></span>
revokeAccess();
}
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// logs 'devbryce'</span>
console.log(sensitiveData.username);
handleSuspectedHack();
<span class="comment" style="box-sizing: inherit; color: rgb(88, 110, 117); font-style: italic;">// TypeError: Revoked</span>
console.log(sensitiveData.username);
</code></pre><h2 style="box-sizing: inherit; margin-bottom: 15px; font-size: 1.4em; color: rgb(0, 163, 207); border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(221, 221, 221); padding-bottom: 5px; font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">Decorator</h2><p style="box-sizing: inherit; margin-bottom: 25px; font-size: 16px; line-height: 30px; color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif;">ES7 中实现的 Decorator,相当于设计模式中的装饰器模式。如果简单地区分 Proxy 和 Decorator 的使用场景,可以概括为:Proxy 的核心作用是控制外界对被代理者内部的访问,Decorator 的核心作用是增强被装饰者的功能。只要在它们核心的使用场景上做好区别,那么像是访问日志这样的功能,虽然本文使用了 Proxy 实现,但也可以使用 Decorator 实现,开发者可以根据项目的需求、团队的规范、自己的偏好自由选择。</p><blockquote style="box-sizing: inherit; padding: 15px 20px; margin-bottom: 25px; border-left-width: 5px; border-left-style: solid; border-left-color: rgb(0, 163, 207); color: rgb(64, 64, 64); font-family: "Microsoft YaHei", 微软雅黑, "Helvetica Neue", Helvetica, Arial, sans-serif; line-height: 20px; background: rgb(246, 246, 246);"><p style="box-sizing: inherit; font-size: 16px; line-height: 30px;">本文根据<a href="http://devbryce.com/author/bryce/" target="_blank" style="box-sizing: inherit; color: rgb(41, 180, 240); text-decoration: none; background: transparent;">@Bryce Johnson</a>的《<a href="http://devbryce.com/use-cases-for-es6-proxies/" target="_blank" style="box-sizing: inherit; color: rgb(41, 180, 240); text-decoration: none; background: transparent;">6 compelling use cases for ES6 proxies</a>》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:<a href="http://devbryce.com/use-cases-for-es6-proxies/" target="_blank" style="box-sizing: inherit; color: rgb(41, 180, 240); text-decoration: none; background: transparent;">http://devbryce.com/use-cases-for-es6-proxies/</a>。</p></blockquote></div>
页:
[1]