aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/javascript/guide/meta_programming
diff options
context:
space:
mode:
authorPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
committerPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
commit33058f2b292b3a581333bdfb21b8f671898c5060 (patch)
tree51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/web/javascript/guide/meta_programming
parent8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff)
downloadtranslated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip
initial commit
Diffstat (limited to 'files/zh-cn/web/javascript/guide/meta_programming')
-rw-r--r--files/zh-cn/web/javascript/guide/meta_programming/index.html264
1 files changed, 264 insertions, 0 deletions
diff --git a/files/zh-cn/web/javascript/guide/meta_programming/index.html b/files/zh-cn/web/javascript/guide/meta_programming/index.html
new file mode 100644
index 0000000000..723165c93f
--- /dev/null
+++ b/files/zh-cn/web/javascript/guide/meta_programming/index.html
@@ -0,0 +1,264 @@
+---
+title: 元编程
+slug: Web/JavaScript/Guide/Meta_programming
+tags:
+ - Guide
+ - JavaScript
+ - Proxy
+ - Reflect
+translation_of: Web/JavaScript/Guide/Meta_programming
+---
+<div>{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Iterators_and_Generators", "Web/JavaScript/Guide/Modules")}}</div>
+
+<p class="summary">从ECMAScript 2015 开始,JavaScript 获得了 {{jsxref("Proxy")}} 和 {{jsxref("Reflect")}} 对象的支持,允许你拦截并定义基本语言操作的自定义行为(例如,属性查找,赋值,枚举,函数调用等)。借助这两个对象,你可以在 JavaScript 元级别进行编程。</p>
+
+<h2 id="代理">代理</h2>
+
+<p>在 ECMAScript 6 中引入的 {{jsxref("Proxy")}} 对象可以拦截某些操作并实现自定义行为。例如获取一个对象上的属性:</p>
+
+<pre class="brush: js notranslate">let handler = {
+ get: function(target, name){
+ return name in target ? target[name] : 42;
+}};
+
+let p = new Proxy({}, handler);
+p.a = 1;
+
+console.log(p.a, p.b); // 1, 42
+</pre>
+
+<p><code>Proxy</code> 对象定义了一个目标(这里是一个空对象)和一个实现了 <code>get</code> 陷阱的 handler 对象。这里,代理的对象在获取未定义的属性时不会返回 <code>undefined</code>,而是返回 42。</p>
+
+<p>更多例子参见 {{jsxref("Proxy")}} 页面 。</p>
+
+<h3 id="术语">术语</h3>
+
+<p>在讨论代理的功能时会用到以下术语。</p>
+
+<dl>
+ <dt>{{jsxref("Global_Objects/Proxy/handler","handler")}}</dt>
+ <dd>包含陷阱的占位符对象。</dd>
+ <dt>traps</dt>
+ <dd>提供属性访问的方法。这类似于操作系统中陷阱的概念。</dd>
+ <dt>target</dt>
+ <dd>代理虚拟化的对象。它通常用作代理的存储后端。根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)。</dd>
+ <dt>invariants</dt>
+ <dd>实现自定义操作时保持不变的语义称为不变量。如果你违反处理程序的不变量,则会抛出一个 {{jsxref("TypeError")}}。</dd>
+</dl>
+
+<h2 id="句柄和陷阱">句柄和陷阱</h2>
+
+<p>以下表格中总结了 <code>Proxy</code> 对象可用的陷阱。详细的解释和例子请看<a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler">参考页</a>。</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th>Handler / trap</th>
+ <th>Interceptions</th>
+ <th>Invariants</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/getPrototypeOf", "handler.getPrototypeOf()")}}</td>
+ <td>{{jsxref("Object.getPrototypeOf()")}}<br>
+ {{jsxref("Reflect.getPrototypeOf()")}}<br>
+ {{jsxref("Object/proto", "__proto__")}}<br>
+ {{jsxref("Object.prototype.isPrototypeOf()")}}<br>
+ {{jsxref("Operators/instanceof", "instanceof")}}</td>
+ <td>
+ <ul>
+ <li><code>getPrototypeOf</code>方法一定返回一个对象或<code>null</code>.</li>
+ <li>如果 <code>target</code> 不可扩展,<code>Object.getPrototypeOf(proxy)</code> 必须返回和 <code>Object.getPrototypeOf(target)</code>一样的值。</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/setPrototypeOf", "handler.setPrototypeOf()")}}</td>
+ <td>{{jsxref("Object.setPrototypeOf()")}}<br>
+ {{jsxref("Reflect.setPrototypeOf()")}}</td>
+ <td>如果 <code>target</code> 不可扩展,<code>prototype</code> 参数必须与<code>Object.getPrototypeOf(target)</code>的值相同。</td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/isExtensible", "handler.isExtensible()")}}</td>
+ <td>{{jsxref("Object.isExtensible()")}}<br>
+ {{jsxref("Reflect.isExtensible()")}}</td>
+ <td><code>Object.isExtensible(proxy)</code> 必须返回和<code>Object.isExtensible(target)</code>一样的值。</td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/preventExtensions", "handler.preventExtensions()")}}</td>
+ <td>{{jsxref("Object.preventExtensions()")}}<br>
+ {{jsxref("Reflect.preventExtensions()")}}</td>
+ <td> 如果<code>Object.isExtensible(proxy)</code> 值为 <code>false,Object.preventExtensions(proxy)</code> 只返回<code>true。</code></td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/getOwnPropertyDescriptor", "handler.getOwnPropertyDescriptor()")}}</td>
+ <td>{{jsxref("Object.getOwnPropertyDescriptor()")}}<br>
+ {{jsxref("Reflect.getOwnPropertyDescriptor()")}}</td>
+ <td>
+ <ul>
+ <li><code>getOwnPropertyDescripton</code> 只能返回对象或者<code>undefined</code>.</li>
+ <li>A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object.</li>
+ <li>A property cannot be reported as non-existent, if it exists as an own property of the target object and the target object is not extensible.</li>
+ <li>A property cannot be reported as existent, if it does not exists as an own property of the target object and the target object is not extensible.</li>
+ <li>A property cannot be reported as non-configurable, if it does not exists as an own property of the target object or if it exists as a configurable own property of the target object.</li>
+ <li>The result of <code>Object.getOwnPropertyDescriptor(target)</code> can be applied to the target object using <code>Object.defineProperty</code> and will not throw an exception.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/defineProperty", "handler.defineProperty()")}}</td>
+ <td>{{jsxref("Object.defineProperty()")}}<br>
+ {{jsxref("Reflect.defineProperty()")}}</td>
+ <td>
+ <ul>
+ <li>A property cannot be added, if the target object is not extensible.</li>
+ <li>A property cannot be added as or modified to be non-configurable, if it does not exists as a non-configurable own property of the target object.</li>
+ <li>A property may not be non-configurable, if a corresponding configurable property of the target object exists.</li>
+ <li>If a property has a corresponding target object property then <code>Object.defineProperty(target, prop, descriptor)</code> will not throw an exception.</li>
+ <li>In strict mode, a <code>false</code> return value from the <code>defineProperty</code> handler will throw a {{jsxref("TypeError")}} exception.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/has", "handler.has()")}}</td>
+ <td>Property query: <code>foo in proxy</code><br>
+ Inherited property query: <code>foo in Object.create(proxy)</code><br>
+ {{jsxref("Reflect.has()")}}</td>
+ <td>
+ <ul>
+ <li>A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object.</li>
+ <li>A property cannot be reported as non-existent, if it exists as an own property of the target object and the target object is not extensible.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/get", "handler.get()")}}</td>
+ <td>Property access: <code>proxy[foo]</code>and <code>proxy.bar</code><br>
+ Inherited property access: <code>Object.create(proxy)[foo]</code><br>
+ {{jsxref("Reflect.get()")}}</td>
+ <td>
+ <ul>
+ <li>The value reported for a property must be the same as the value of the corresponding target object property if the target object property is a non-writable, non-configurable data property.</li>
+ <li>The value reported for a property must be undefined if the corresponding target object property is non-configurable accessor property that has undefined as its [[Get]] attribute.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/set", "handler.set()")}}</td>
+ <td>Property assignment: <code>proxy[foo] = bar</code> and <code>proxy.foo = bar</code><br>
+ Inherited property assignment: <code>Object.create(proxy)[foo] = bar</code><br>
+ {{jsxref("Reflect.set()")}}</td>
+ <td>
+ <ul>
+ <li>Cannot change the value of a property to be different from the value of the corresponding target object property if the corresponding target object property is a non-writable, non-configurable data property.</li>
+ <li>Cannot set the value of a property if the corresponding target object property is a non-configurable accessor property that has <code>undefined</code> as its [[Set]] attribute.</li>
+ <li>In strict mode, a <code>false</code> return value from the <code>set</code> handler will throw a {{jsxref("TypeError")}} exception.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/deleteProperty", "handler.deleteProperty()")}}</td>
+ <td>Property deletion: <code>delete proxy[foo]</code> and <code>delete proxy.foo</code><br>
+ {{jsxref("Reflect.deleteProperty()")}}</td>
+ <td>A property cannot be deleted, if it exists as a non-configurable own property of the target object.</td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/enumerate", "handler.enumerate()")}}</td>
+ <td>Property enumeration / for...in: <code>for (var name in proxy) {...}</code><br>
+ {{jsxref("Reflect.enumerate()")}}</td>
+ <td>The <code>enumerate</code> method must return an object.</td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/ownKeys", "handler.ownKeys()")}}</td>
+ <td>{{jsxref("Object.getOwnPropertyNames()")}}<br>
+ {{jsxref("Object.getOwnPropertySymbols()")}}<br>
+ {{jsxref("Object.keys()")}}<br>
+ {{jsxref("Reflect.ownKeys()")}}</td>
+ <td>
+ <ul>
+ <li>The result of <code>ownKeys</code> is a List.</li>
+ <li>The Type of each result List element is either {{jsxref("String")}} or {{jsxref("Symbol")}}.</li>
+ <li>The result List must contain the keys of all non-configurable own properties of the target object.</li>
+ <li>If the target object is not extensible, then the result List must contain all the keys of the own properties of the target object and no other values.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/apply", "handler.apply()")}}</td>
+ <td><code>proxy(..args)</code><br>
+ {{jsxref("Function.prototype.apply()")}} and {{jsxref("Function.prototype.call()")}}<br>
+ {{jsxref("Reflect.apply()")}}</td>
+ <td>There are no invariants for the <code>handler.apply</code> method.</td>
+ </tr>
+ <tr>
+ <td>{{jsxref("Global_Objects/Proxy/handler/construct", "handler.construct()")}}</td>
+ <td><code>new proxy(...args)</code><br>
+ {{jsxref("Reflect.construct()")}}</td>
+ <td>结果一定是一个<code>Object</code>。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="撤销_Proxy">撤销 <code>Proxy</code></h2>
+
+<p>{{jsxref("Proxy.revocable()")}} 方法被用来创建可撤销的 <code>Proxy</code> 对象。这意味着 proxy 可以通过 <code>revoke</code> 函数来撤销,并且关闭代理。此后,代理上的任意的操作都会导致{{jsxref("TypeError")}}。</p>
+
+<pre class="brush: js notranslate">var revocable = Proxy.revocable({}, {
+ get: function(target, name) {
+ return "[[" + name + "]]";
+ }
+});
+var proxy = revocable.proxy;
+console.log(proxy.foo); // "[[foo]]"
+
+revocable.revoke();
+
+console.log(proxy.foo); // TypeError is thrown
+proxy.foo = 1 // TypeError again
+delete proxy.foo; // still TypeError
+typeof proxy // "object", typeof doesn't trigger any trap</pre>
+
+<h2 id="反射">反射</h2>
+
+<p>{{jsxref("Reflect")}} 是一个内置对象,它提供了可拦截 JavaScript 操作的方法。该方法和{{jsxref("Global_Objects/Proxy/handler","代理句柄")}}类似,但 <code>Reflect</code> 方法并不是一个函数对象。</p>
+
+<p><code>Reflect</code> 有助于将默认操作从处理程序转发到目标。</p>
+
+<p>以 {{jsxref("Reflect.has()")}} 为例,你可以将 <a href="/en-US/docs/Web/JavaScript/Reference/Operators/in"><code>in</code> 运算符</a>作为函数:</p>
+
+<pre class="brush: js notranslate">Reflect.has(Object, "assign"); // true
+</pre>
+
+<h3 id="更好的_apply_函数">更好的 <code>apply</code> 函数</h3>
+
+<p>在 ES5 中,我们通常使用 {{jsxref("Function.prototype.apply()")}} 方法调用一个具有给定 <code>this</code> 值和 <code>arguments</code> 数组(或<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Indexed_collections#Working_with_array-like_objects">类数组对象</a>)的函数。</p>
+
+<pre class="brush: js notranslate">Function.prototype.apply.call(Math.floor, undefined, [1.75]);</pre>
+
+<p>使用 {{jsxref("Reflect.apply")}},这变得不那么冗长和容易理解:</p>
+
+<pre class="brush: js notranslate">Reflect.apply(Math.floor, undefined, [1.75]);
+// 1;
+
+Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);
+// "hello"
+
+Reflect.apply(RegExp.prototype.exec, /ab/, ['confabulation']).index;
+// 4
+
+Reflect.apply(''.charAt, 'ponies', [3]);
+// "i"</pre>
+
+<h3 id="检查属性定义是否成功">检查属性定义是否成功</h3>
+
+<p>使用 {{jsxref("Object.defineProperty")}},如果成功返回一个对象,否则抛出一个 {{jsxref("TypeError")}},你将使用 {{jsxref("Statements/try...catch","try...catch")}} 块来捕获定义属性时发生的任何错误。因为 {{jsxref("Reflect.defineProperty")}} 返回一个布尔值表示的成功状态,你可以在这里使用 {{jsxref("Statements/if...else","if...else")}} 块:</p>
+
+<pre class="brush: js notranslate">if (Reflect.defineProperty(target, property, attributes)) {
+ // success
+} else {
+ // failure
+}
+</pre>
+
+<p>{{Previous("Web/JavaScript/Guide/Iterators_and_Generators")}}</p>