--- title: Мета-программирование slug: Web/JavaScript/Guide/Meta_programming tags: - Guide - JavaScript - Meta - Meta programming - Proxy - Reflect - Метапрограммирование translation_of: Web/JavaScript/Guide/Meta_programming ---
С приходом ECMAScript 2015, в JavaScript введены объекты {{jsxref("Proxy")}} и {{jsxref("Reflect")}}, позволяющие перехватить и переопределить поведение фундаментальных процессов языка (таких как поиск свойств, присвоение, итерирование, вызов функций и так далее). С помощью этих двух объектов Вы можете программировать на мета уровне JavaScript.
Введенный в ECMAScript 6, объект {{jsxref("Proxy")}} позволяет перехватить и определить пользовательское поведение для определенных операций. Например, получение свойства объекта:
var handler = { get: function(target, name) { return name in target ? target[name] : 42; }}; var p = new Proxy({}, handler); p.a = 1; console.log(p.a, p.b); // 1, 42
Объект Proxy
определяет target (в данном случае новый пустой объект) и handler - объект в котором реализована особая функция-ловушка get
. "Проксированный" таким образом объект, при доступе к его несуществующему свойству вернет не undefined,
а числовое значение 42.
Дополнительные примеры доступны в справочнике {{jsxref("Proxy")}}.
В разговоре о функциях объекта Proxy
применимы следующие термины:
В следующей таблице перечислены ловушки, доступные для использования в объекте Proxy
. Смотрите подробные объяснения и примеры в документации.
Обработчик / ловушка | Перехватываемые методы | Неизменяемые ограничения |
---|---|---|
{{jsxref("Global_Objects/Proxy/handler/getPrototypeOf", "handler.getPrototypeOf()")}} | {{jsxref("Object.getPrototypeOf()")}} {{jsxref("Reflect.getPrototypeOf()")}} {{jsxref("Object/proto", "__proto__")}} {{jsxref("Object.prototype.isPrototypeOf()")}} {{jsxref("Operators/instanceof", "instanceof")}} |
|
{{jsxref("Global_Objects/Proxy/handler/setPrototypeOf", "handler.setPrototypeOf()")}} | {{jsxref("Object.setPrototypeOf()")}} {{jsxref("Reflect.setPrototypeOf()")}} |
если целевой объект target нерасширяем, значение параметра prototype должно быть равным значению возвращаемому методом Object.getPrototypeOf(target) . |
{{jsxref("Global_Objects/Proxy/handler/isExtensible", "handler.isExtensible()")}} | {{jsxref("Object.isExtensible()")}} {{jsxref("Reflect.isExtensible()")}} |
Object.isExtensible(proxy) должно возвращать тоже значение, что и Object.isExtensible(target) . |
{{jsxref("Global_Objects/Proxy/handler/preventExtensions", "handler.preventExtensions()")}} | {{jsxref("Object.preventExtensions()")}} {{jsxref("Reflect.preventExtensions()")}} |
Object.preventExtensions(proxy) возвращает true только в том случае, если Object.isExtensible(proxy) равно false . |
{{jsxref("Global_Objects/Proxy/handler/getOwnPropertyDescriptor", "handler.getOwnPropertyDescriptor()")}} | {{jsxref("Object.getOwnPropertyDescriptor()")}} {{jsxref("Reflect.getOwnPropertyDescriptor()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/defineProperty", "handler.defineProperty()")}} | {{jsxref("Object.defineProperty()")}} {{jsxref("Reflect.defineProperty()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/has", "handler.has()")}} | Property query: foo in proxy Inherited property query: foo in Object.create(proxy) {{jsxref("Reflect.has()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/get", "handler.get()")}} | Property access: proxy[foo] and proxy.bar Inherited property access: Object.create(proxy)[foo] {{jsxref("Reflect.get()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/set", "handler.set()")}} | Property assignment: proxy[foo] = bar and proxy.foo = bar Inherited property assignment: Object.create(proxy)[foo] = bar {{jsxref("Reflect.set()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/deleteProperty", "handler.deleteProperty()")}} | Property deletion: delete proxy[foo] and delete proxy.foo {{jsxref("Reflect.deleteProperty()")}} |
Свойство не может быть удалено, если оно существует в целевом объекте как собственное, неконфигурируемое свойство. |
{{jsxref("Global_Objects/Proxy/handler/enumerate", "handler.enumerate()")}} | Property enumeration / for...in: for (var name in proxy) {...} {{jsxref("Reflect.enumerate()")}} |
Метод enumerate должен возвращать объект. |
{{jsxref("Global_Objects/Proxy/handler/ownKeys", "handler.ownKeys()")}} | {{jsxref("Object.getOwnPropertyNames()")}} {{jsxref("Object.getOwnPropertySymbols()")}} {{jsxref("Object.keys()")}} {{jsxref("Reflect.ownKeys()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/apply", "handler.apply()")}} | proxy(..args) {{jsxref("Function.prototype.apply()")}} and {{jsxref("Function.prototype.call()")}} {{jsxref("Reflect.apply()")}} |
Ограничений нет. |
{{jsxref("Global_Objects/Proxy/handler/construct", "handler.construct()")}} | new proxy(...args) {{jsxref("Reflect.construct()")}} |
Обработчик должен возвращать Object . |
Proxy
Метод {{jsxref("Proxy.revocable()")}} создает отзываемый объект Proxy
. Такой прокси объект может быть отозван функцией revoke
, которая отключает все ловушки-обработчики. После этого любые операции над прокси объектом вызовут ошибку {{jsxref("TypeError")}}.
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 proxy.foo = 1; // снова ошибка TypeError delete proxy.foo; // опять TypeError typeof proxy; // "object", для метода typeof нет ловушек
{{jsxref("Reflect")}} это встроенный объект, предоставляющий методы для перехватываемых операций JavaScript. Это те же самые методы, что имеются в {{jsxref("Global_Objects/Proxy/handler","обработчиках Proxy","","true")}}. Объект Reflect
не является функцией.
Reflect
помогает при пересылке стандартных операций из обработчика к целевому объекту.
Например, метод {{jsxref("Reflect.has()")}} это тот же оператор in
но в виде функции:
Reflect.has(Object, 'assign'); // true
apply
В ES5 обычно используется метод {{jsxref("Function.prototype.apply()")}} для вызова функции в определенном контексте (с определенным this)
и с параметрами, заданными в виде массива (или массиво-подобного объекта).
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
С методом {{jsxref("Reflect.apply")}} эта операция менее громоздка и более понятна:
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"
Метод {{jsxref("Object.defineProperty")}}, в случае успеха операции, возвращает объект, а при неудаче вызывает ошибку {{jsxref("TypeError")}}. Из-за этого определение свойств требует обработки блоком {{jsxref("Statements/try...catch","try...catch")}} для перехвата возможных ошибок. Метод {{jsxref("Reflect.defineProperty")}}, в свою очередь, возвращает успешность операции в виде булева значения, благодаря чему возможно использование простого {{jsxref("Statements/if...else","if...else")}} условия:
if (Reflect.defineProperty(target, property, attributes)) { // успех } else { // что-то пошло не так }
{{Previous("Web/JavaScript/Guide/Iterators_and_Generators")}}