aboutsummaryrefslogtreecommitdiff
path: root/files/ru/web/javascript/reference/global_objects/proxy/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'files/ru/web/javascript/reference/global_objects/proxy/index.html')
-rw-r--r--files/ru/web/javascript/reference/global_objects/proxy/index.html482
1 files changed, 482 insertions, 0 deletions
diff --git a/files/ru/web/javascript/reference/global_objects/proxy/index.html b/files/ru/web/javascript/reference/global_objects/proxy/index.html
new file mode 100644
index 0000000000..2d8e5ae557
--- /dev/null
+++ b/files/ru/web/javascript/reference/global_objects/proxy/index.html
@@ -0,0 +1,482 @@
+---
+title: Прокси
+slug: Web/JavaScript/Reference/Global_Objects/Proxy
+tags:
+ - ECMAScript6
+ - JavaScript
+ - NeedsUpdate
+ - Reference
+ - Объект
+ - Прокси
+translation_of: Web/JavaScript/Reference/Global_Objects/Proxy
+---
+<div><font><font>Объект </font></font><code>Proxy</code><font><font> позволяет создать прокси</font><font> для другого объекта, может перехватывать и переопределить основные операции для данного объекта.</font></font></div>
+
+<h2 id="Введение">Введение</h2>
+
+<p>Прокси используются программистами для объявления расширенной семантики JavaScript объектов. Стандартная семантика реализована в движке JavaScript, который обычно написан на низкоуровневом языке программирования, например C++. Прокси позволяют программисту определить поведение объекта при помощи JavaScript. Другими словами они являются <strong>инструментом метапрограммирования</strong>.</p>
+
+<p><strong>Примечание</strong>: реализация прокси в SpiderMonkey является прототипом, в котором прокси API и семантика не стабильны. Также, реализация в SpiderMonkey может не соответствовать последней версии спецификации. Она может быть изменена в любой момент и предоставляется исключительно как экспериментальная функция. <strong>Не полагайтесь на неё в производственном коде.</strong></p>
+
+<p>Эта страница описывает новый API (называемый «непосредственным проксированием»), который является частью Firefox 18. Для просмотра старого API (Firefox 17 и ниже) посетите страницу описания <a href="/en-US/docs/JavaScript/Old_Proxy_API" title="/en-US/docs/JavaScript/Old_Proxy_API">старого прокси API</a>.</p>
+
+<h2 id="Терминология">Терминология</h2>
+
+<dl>
+ <dt>механизм полного перехвата (или "intercession API")</dt>
+ <dd>Технический термин для этой функции.</dd>
+ <dt>прокси (proxy)</dt>
+ <dd>Объект, оборачивающий исходный объект.</dd>
+ <dt>обработчик (handler)</dt>
+ <dd>Объект-заменитель, содержащий ловушки. О<span class="VIiyi" lang="ru"><span class="ChMk0b JLqJ4b"><span>пределяет, какие операции будут перехвачены, также переопределяет перехваченные операции.</span></span></span></dd>
+ <dt>ловушки (traps)</dt>
+ <dd>Методы, которые предоставляют доступ к свойствам. Это аналогично концепции ловушек в операционных системах.</dd>
+ <dt>цель (target)</dt>
+ <dd>Исходный объект, который виртуализируется прокси. Он часто используется в качестве источника данных в прокси. Для него проверяются инварианты относительно расширяемости и настраиваемости свойств.</dd>
+</dl>
+
+<h2 id="Прокси">Прокси</h2>
+
+<p>Прокси - это новые объекты; невозможно выполнить "проксирование" существующего объекта. Пример создания прокси:</p>
+
+<pre class="brush: js notranslate">var p = new Proxy(target, handler);
+</pre>
+
+<p>Где:</p>
+
+<ul>
+ <li><code>target</code> — исходный объект (может быть объектом любого типа, включая массив, функцию и даже другой прокси объект).</li>
+ <li><code>handler</code> — объект-обработчик, методы (ловушки) которого определяют поведение прокси во время выполнения операции над ним.</li>
+</ul>
+
+<h2 id="Обработчик">Обработчик</h2>
+
+<p>Все ловушки опциональны. В случае, если ловушка не задана, то стандартным поведением будет перенаправление операции к объекту-цели.</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th>JavaScript код</th>
+ <th>Метод обработчика</th>
+ <th>Описание</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>Object.getOwnPropertyDescriptor(proxy, name)</code></td>
+ <td><code><strong>getOwnPropertyDescriptor</strong><br>
+ function(target, name) -&gt; PropertyDescriptor | undefined</code></td>
+ <td>Должен возвращать верный объект-описание свойства или <code>undefined</code>, чтобы показать, что свойство с именем <code>name</code> существует в эмулируемом объекте.</td>
+ </tr>
+ <tr>
+ <td>
+ <p><code>Object.getOwnPropertyNames(proxy)</code><br>
+ <code><code>Object.getOwnPropertySymbols(proxy)</code></code><br>
+ <code><code><code>Object.keys(proxy)</code></code></code></p>
+ </td>
+ <td><code><code><strong>ownKeys</strong></code> function(target) -&gt; [string | symbol]</code></td>
+ <td>Возвращает массив всех собственных (не унаследованных) имён свойств эмулируемого объекта.</td>
+ </tr>
+ <tr>
+ <td><code>Object.defineProperty(proxy,name,pd)</code></td>
+ <td><code><strong>defineProperty</strong> function(target, name, propertyDescriptor) -&gt; any</code></td>
+ <td>Задаёт новое свойство, атрибуты которого определяются предоставленным <code>propertyDescriptor</code>. Возвращаемое значение метода игнорируется.</td>
+ </tr>
+ <tr>
+ <td><code>delete proxy.name</code></td>
+ <td><code><strong>deleteProperty</strong> function(target, name) -&gt; boolean</code></td>
+ <td>Удаляет именованное свойство из прокси. Возвращает <code>true</code> в случае успешного удаления свойства <code>name</code>.</td>
+ </tr>
+ <tr>
+ <td><code>Object.preventExtensions(proxy)</code></td>
+ <td><code><strong>preventExtensions</strong> function(target) -&gt; boolean</code></td>
+ <td>Делает объект нерасширяемым. Возвращает <code>true</code> при успешном выполнении.</td>
+ </tr>
+ <tr>
+ <td><code>name in proxy</code></td>
+ <td><code><strong>has</strong> function(target, name) -&gt; boolean</code></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>
+ <p><code>proxy.name</code> (in the context of "getting the value")</p>
+
+ <p><code>receiver.name</code> (if <code>receiver</code> inherits from a proxy and does not override <code>name</code>)</p>
+ </td>
+ <td><code><strong>get</strong> function(target, name, receiver) -&gt; any</code></td>
+ <td><code>receiver</code> — это прокси или объект, унаследованный от прокси.</td>
+ </tr>
+ <tr>
+ <td>
+ <p><code>proxy.name = val</code> (in the context of "setting the value")</p>
+
+ <p><code>receiver.name = val</code> (if <code>receiver</code> inherits from a proxy and does not override <code>name</code>)</p>
+ </td>
+ <td><code><strong>set</strong> function(target, name, val, receiver) -&gt; boolean</code></td>
+ <td><code style="font-style: normal;">receiver</code> — это прокси или объект, унаследованный от прокси.</td>
+ </tr>
+ <tr>
+ <td>
+ <p><code>proxy(...args)<br>
+ proxy.apply(thisValue, args)<br>
+ proxy.call(thisValue, ...args)</code></p>
+ </td>
+ <td><code><strong>apply</strong> function(target, thisValue, args) -&gt; any</code></td>
+ <td><code>target</code> должен быть функцией.</td>
+ </tr>
+ <tr>
+ <td><code>new proxy(...args)</code></td>
+ <td><code><strong>construct</strong> function(target, args) -&gt; object</code></td>
+ <td><code>target</code> должен быть функцией.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Инварианты">Инварианты</h2>
+
+<p>Несмотря на то, что прокси предоставляют много возможностей пользователям, некоторые операции не перехватываются для сохранения постоянства языка:</p>
+
+<ul>
+ <li>Простой и строгий оператор равенства (<code>==</code>, <code>===</code>) не перехватывается. <code>p1 === p2</code> равны, только если <code>p1</code> и <code>p2</code> ссылаются на один и тот же прокси.</li>
+ <li>Текущая реализация <code>Object.getPrototypeOf(proxy)</code> всегда возвращает <code>Object.getPrototypeOf(target)</code>, потому что в ES2015 перехватчик getPrototypeOf пока не реализован.</li>
+ <li><code>typeof proxy</code> всегда возвращает  <code>typeof target</code>. В частности, <code>proxy</code> может быть использован как функция только если <code>target </code>является функцией.</li>
+ <li><code>Array.isArray(proxy)</code> всегда возвращает  <code>Array.isArray(target)</code>.</li>
+ <li><code>Object.prototype.toString.call(proxy)</code> всегда возвращает <code>Object.prototype.toString.call(target)</code>, потому что в  ES2015 перехватчик Symbol.toStringTag пока не реализован.</li>
+</ul>
+
+<h2 id="Примеры">Примеры</h2>
+
+<h3 id="Простой_пример">Простой пример</h3>
+
+<p>Объект, возвращающий значение <code>37</code>, в случае отсутствия свойства с указанным именем:</p>
+
+<pre class="brush: js notranslate">var handler = {
+ get: function(target, name){
+ return name in target?
+ target[name] :
+ 37;
+ }
+};
+
+var p = new Proxy({}, handler);
+p.a = 1;
+p.b = undefined;
+
+console.log(p.a, p.b); // 1, undefined
+console.log('c' in p, p.c); // false, 37
+</pre>
+
+<h3 id="Перенаправляющий_прокси">Перенаправляющий прокси</h3>
+
+<p>В данном примере мы используем JavaScript объект, к которому наш прокси направляет все запросы:</p>
+
+<pre class="brush: js notranslate">var target = {};
+var p = new Proxy(target, {});
+
+p.a = 37; // операция перенаправлена прокси
+
+console.log(target.a); // 37. Операция была успешно перенаправлена
+</pre>
+
+<h3 id="Проверка">Проверка</h3>
+
+<p>При помощи <code>Proxy</code> вы можете легко проверять передаваемые объекту значения:</p>
+
+<pre class="brush: js notranslate">let validator = {
+ set: function(obj, prop, value) {
+ if (prop === 'age') {
+ if (!Number.isInteger(value)) {
+ throw new TypeError('The age is not an integer');
+ }
+ if (value &gt; 200) {
+ throw new RangeError('The age seems invalid');
+ }
+ }
+
+ // Стандартное сохранение значения
+ obj[prop] = value;
+ }
+};
+
+let person = new Proxy({}, validator);
+
+person.age = 100;
+console.log(person.age); // 100
+person.age = 'young'; // Вызовет исключение
+person.age = 300; // Вызовет исключение
+</pre>
+
+<h3 id="Дополнение_конструктора">Дополнение конструктора</h3>
+
+<p>Функция прокси может легко дополнить конструктор новым:</p>
+
+<pre class="brush: js notranslate">function extend(sup, base) {
+    var descriptor = Object.getOwnPropertyDescriptor(
+        base.prototype, 'constructor',
+    );
+
+  const prototype = {...base.prototype}
+
+  base.prototype = Object.create(sup.prototype);
+  base.prototype = Object.assign(base.prototype, prototype);
+
+    var handler = {
+        construct: function(target, args) {
+            var obj = Object.create(base.prototype);
+            this.apply(target, obj, args);
+            return obj;
+        },
+        apply: function(target, that, args) {
+            sup.apply(that, args);
+            base.apply(that, args);
+        },
+    };
+    var proxy = new Proxy(base, handler);
+    descriptor.value = proxy;
+    Object.defineProperty(base.prototype, 'constructor', descriptor);
+    return proxy;
+}
+
+var Person = function(name) {
+    this.name = name;
+};
+
+var Boy = extend(Person, function(name, age) {
+    this.age = age;
+});
+
+Boy.prototype.sex = 'M';
+
+var Peter = new Boy('Peter', 13);
+console.log(Peter.sex); // "M"
+console.log(Peter.name); // "Peter"
+console.log(Peter.age); // 13
+</pre>
+
+<h3 id="Манипуляция_DOM_элементами">Манипуляция<span style="font-size: 1.71428571428571rem;"> DOM </span><span style="font-size: 1.71428571428571rem;">элементами</span></h3>
+
+<p>Иногда возникает необходимость переключить атрибут или имя класса у двух разных элементов:</p>
+
+<pre class="brush: js notranslate">let view = new Proxy({
+ selected: null
+},
+{
+ set: function(obj, prop, newval) {
+ let oldval = obj[prop];
+
+ if (prop === 'selected') {
+ if (oldval) {
+ oldval.setAttribute('aria-selected', 'false');
+ }
+ if (newval) {
+ newval.setAttribute('aria-selected', 'true');
+ }
+ }
+
+ // Стандартное сохранение значения
+ obj[prop] = newval;
+ }
+});
+
+let i1 = view.selected = document.getElementById('item-1');
+console.log(i1.getAttribute('aria-selected')); // 'true'
+
+let i2 = view.selected = document.getElementById('item-2');
+console.log(i1.getAttribute('aria-selected')); // 'false'
+console.log(i2.getAttribute('aria-selected')); // 'true'
+</pre>
+
+<h3 id="Изменение_значений_и_дополнительные_свойства">Изменение значений и дополнительные свойства</h3>
+
+<p>Прокси объект <code>products</code> проверяет передаваемые значения и преобразует их в массив в случае необходимости. Объект также поддерживает дополнительное свойство <code>latestBrowser</code> на чтение и запись.</p>
+
+<pre class="brush: js notranslate">let products = new Proxy({
+ browsers: ['Internet Explorer', 'Netscape']
+},
+{
+ get: function(obj, prop) {
+ // Дополнительное свойство
+ if (prop === 'latestBrowser') {
+ return obj.browsers[obj.browsers.length - 1];
+ }
+
+ // Стандартный возврат значения
+ return obj[prop];
+ },
+ set: function(obj, prop, value) {
+ // Дополнительное свойство
+ if (prop === 'latestBrowser') {
+ obj.browsers.push(value);
+ return;
+ }
+
+ // Преобразование значения, если оно не массив
+ if (typeof value === 'string') {
+ value = [value];
+ }
+
+ // Стандартное сохранение значения
+ obj[prop] = value;
+ }
+});
+
+console.log(products.browsers); // ['Internet Explorer', 'Netscape']
+products.browsers = 'Firefox'; // передаётся как строка (по ошибке)
+console.log(products.browsers); // ['Firefox'] &lt;- проблем нет, значение - массив
+
+products.latestBrowser = 'Chrome';
+console.log(products.browsers); // ['Firefox', 'Chrome']
+console.log(products.latestBrowser); // 'Chrome'
+</pre>
+
+<h3 id="Поиск_элемента_массива_по_его_свойству">Поиск элемента массива по его свойству</h3>
+
+<p>Данный прокси расширяет массив дополнительными возможностями. Как вы видите, вы можете гибко "задавать" свойства без использования <a href="/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperties"><code>Object.defineProperties</code></a>. Данный пример также может быть использован для поиска строки таблицы по её ячейке. В этом случае целью будет <a href="/en-US/docs/DOM/table.rows"><code>table.rows</code></a>.</p>
+
+<pre class="brush: js notranslate">let products = new Proxy([
+ { name: 'Firefox', type: 'browser' },
+ { name: 'SeaMonkey', type: 'browser' },
+ { name: 'Thunderbird', type: 'mailer' }
+],
+{
+ get: function(obj, prop) {
+ // Стандартное возвращение значения; prop обычно является числом
+ if (prop in obj) {
+ return obj[prop];
+ }
+
+ // Получение количества продуктов; псевдоним products.length
+ if (prop === 'number') {
+ return obj.length;
+ }
+
+ let result, types = {};
+
+ for (let product of obj) {
+ if (product.name === prop) {
+ result = product;
+ }
+ if (types[product.type]) {
+ types[product.type].push(product);
+ } else {
+ types[product.type] = [product];
+ }
+ }
+
+ // Получение продукта по имени
+ if (result) {
+ return result;
+ }
+
+ // Получение продуктов по типу
+ if (prop in types) {
+ return types[prop];
+ }
+
+ // Получение типов продуктов
+ if (prop === 'types') {
+ return Object.keys(types);
+ }
+
+ return undefined;
+ }
+});
+
+console.log(products[0]); // { name: 'Firefox', type: 'browser' }
+console.log(products['Firefox']); // { name: 'Firefox', type: 'browser' }
+console.log(products['Chrome']); // undefined
+console.log(products.browser); // [{ name: 'Firefox', type: 'browser' }, { name: 'SeaMonkey', type: 'browser' }]
+console.log(products.types); // ['browser', 'mailer']
+console.log(products.number); // 3
+</pre>
+
+<h3 id="Пример_использования_всех_перехватчиков">Пример использования всех перехватчиков</h3>
+
+<p>В данном примере, использующем все виды перехватчиков, мы попытаемся проксировать <em>не нативный</em> объект, который частично приспособлен для этого - <code>docCookies,</code> созданном в разделе <a href="https://developer.mozilla.org/en-US/docs/DOM/document.cookie#A_little_framework.3A_a_complete_cookies_reader.2Fwriter_with_full_unicode_support" title="https://developer.mozilla.org/en-US/docs/DOM/document.cookie#A_little_framework.3A_a_complete_cookies_reader.2Fwriter_with_full_unicode_support">"little framework" и опубликованном на странице <code>document.cookie</code></a>.</p>
+
+<pre class="brush: js notranslate">/*
+ var docCookies = ... получить объект "docCookies" можно здесь:
+  https://developer.mozilla.org/en-US/docs/DOM/document.cookie#A_little_framework.3A_a_complete_cookies_reader.2Fwriter_with_full_unicode_support
+*/
+
+var docCookies = new Proxy(docCookies, {
+  "get": function (oTarget, sKey) {
+    return oTarget[sKey] || oTarget.getItem(sKey) || undefined;
+  },
+  "set": function (oTarget, sKey, vValue) {
+    if (sKey in oTarget) { return false; }
+    return oTarget.setItem(sKey, vValue);
+  },
+  "deleteProperty": function (oTarget, sKey) {
+    if (sKey in oTarget) { return false; }
+    return oTarget.removeItem(sKey);
+  },
+  "enumerate": function (oTarget, sKey) {
+    return oTarget.keys();
+  },
+  "iterate": function (oTarget, sKey) {
+    return oTarget.keys();
+  },
+  "ownKeys": function (oTarget, sKey) {
+    return oTarget.keys();
+  },
+  "has": function (oTarget, sKey) {
+    return sKey in oTarget || oTarget.hasItem(sKey);
+  },
+  "hasOwn": function (oTarget, sKey) {
+    return oTarget.hasItem(sKey);
+  },
+  "defineProperty": function (oTarget, sKey, oDesc) {
+    if (oDesc &amp;&amp; "value" in oDesc) { oTarget.setItem(sKey, oDesc.value); }
+    return oTarget;
+  },
+  "getPropertyNames": function (oTarget) {
+    return Object.getPropertyNames(oTarget).concat(oTarget.keys());
+  },
+  "getOwnPropertyNames": function (oTarget) {
+    return Object.getOwnPropertyNames(oTarget).concat(oTarget.keys());
+  },
+  "getPropertyDescriptor": function (oTarget, sKey) {
+    var vValue = oTarget[sKey] || oTarget.getItem(sKey)
+    return vValue ? {
+      "value": vValue,
+      "writable": true,
+      "enumerable": true,
+      "configurable": false
+    } : undefined;
+  },
+  "getOwnPropertyDescriptor": function (oTarget, sKey) {
+    var vValue = oTarget.getItem(sKey);
+    return vValue ? {
+      "value": vValue,
+      "writable": true,
+      "enumerable": true,
+      "configurable": false
+    } : undefined;
+  },
+  "fix":  function (oTarget) {
+    return "not implemented yet!";
+  },
+});
+
+/* Проверка cookies */
+
+alert(docCookies.my_cookie1 = "First value");
+alert(docCookies.getItem("my_cookie1"));
+
+docCookies.setItem("my_cookie1", "Changed value");
+alert(docCookies.my_cookie1);</pre>
+
+<h2 id="See_also" name="See_also" style="line-height: 30px; font-size: 2.14285714285714rem;">Смотрите также</h2>
+
+<ul>
+ <li><a class="external" href="http://jsconf.eu/2010/speaker/be_proxy_objects.html">"Proxies are awesome" презентация Brendan Eich на JSConf</a> (<a class="external" href="http://www.slideshare.net/BrendanEich/metaprog-5303821">слайды</a>)</li>
+ <li><a class="external" href="http://wiki.ecmascript.org/doku.php?id=harmony:proxies">Страница предложения ECMAScript Harmony Proxy</a> и <a class="external" href="http://wiki.ecmascript.org/doku.php?id=harmony:proxies_semantics">страница ECMAScript Harmony proxy semantics</a></li>
+ <li><a class="external" href="http://soft.vub.ac.be/~tvcutsem/proxies/">Руководство по прокси</a></li>
+ <li><a href="/en-US/docs/JavaScript/Old_Proxy_API" title="/en-US/docs/JavaScript/Old_Proxy_API">Старая страница Proxy API</a></li>
+ <li><a href="/en-US/docs/JavaScript/Reference/Global_Objects/Object/watch"><code>Object.watch</code></a> - не стандартная возможность, поддерживается только в движке Gecko.</li>
+</ul>
+
+<h2 id="Лицензионные_примечания">Лицензионные примечания</h2>
+
+<p>Некоторое содержимое (текст, примеры) данной страницы было скопировано или адаптировано со страниц <a class="external" href="http://wiki.ecmascript.org/doku.php">вики ECMAScript</a>, имеющей лицензию <a class="external" href="http://creativecommons.org/licenses/by-nc-sa/2.0/">CC 2.0 BY-NC-SA</a></p>