ÐеÑа-пÑогÑаммиÑование
С пÑиÑ
одом ECMAScript 2015, в JavaScript Ð²Ð²ÐµÐ´ÐµÐ½Ñ Ð¾Ð±ÑекÑÑ Proxy и Reflect, позволÑÑÑие пеÑеÑ
ваÑиÑÑ Ð¸ пеÑеопÑеделиÑÑ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ ÑÑндаменÑалÑнÑÑ
пÑоÑеÑÑов ÑзÑка (ÑакиÑ
как поиÑк ÑвойÑÑв, пÑиÑвоение, иÑеÑиÑование, вÑзов ÑÑнкÑий и Ñак далее). С помоÑÑÑ ÑÑиÑ
двÑÑ
обÑекÑов Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе пÑогÑаммиÑоваÑÑ Ð½Ð° меÑа ÑÑовне JavaScript.
ÐбÑекÑÑ Proxy
ÐведÑннÑй в ECMAScript 6, обÑÐµÐºÑ 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.
ÐополниÑелÑнÑе пÑимеÑÑ Ð´Ð¾ÑÑÑÐ¿Ð½Ñ Ð² ÑпÑавоÑнике Proxy.
ТеÑминологиÑ
Ð ÑазговоÑе о ÑÑнкÑиÑÑ
обÑекÑа Proxy пÑÐ¸Ð¼ÐµÐ½Ð¸Ð¼Ñ ÑледÑÑÑие ÑеÑминÑ:
- handler (обÑабоÑÑик)
-
ÐбÑÐµÐºÑ - обÑÑÑка, ÑодеÑжаÑий в Ñебе ÑÑнкÑии-ловÑÑки.
- ловÑÑки (traps)
-
ÐеÑодÑ, ÑеализÑÑÑие доÑÑÑп к ÑвойÑÑвам. Ð Ñвоей конÑепÑии они аналогиÑÐ½Ñ Ð¼ÐµÑодам пеÑÐµÑ Ð²Ð°Ñа(hooking) в опеÑаÑионнÑÑ ÑиÑÑÐµÐ¼Ð°Ñ .
- ÑÐµÐ»Ñ (target)
-
ÐбÑекÑ, коÑоÑÑй обоÑаÑиваеÑÑÑ Ð² Proxy. ЧаÑÑо иÑполÑзÑеÑÑÑ Ð»Ð¸ÑÑ ÐºÐ°Ðº внÑÑÑеннее Ñ ÑанилиÑе Ð´Ð»Ñ Proxy обÑекÑа. ÐÑовеÑка на наÑÑÑение огÑаниÑений (invariants), ÑвÑзаннÑÑ Ñ Ð½ÐµÑаÑÑиÑÑемоÑÑÑÑ Ð¾Ð±ÑекÑа или неконÑигÑÑиÑÑемÑми ÑвойÑÑвами обÑекÑа пÑоизводиÑÑÑ Ð´Ð»Ñ ÐºÐ¾Ð½ÐºÑеÑной Ñели.
- неизменÑемÑе огÑаниÑÐµÐ½Ð¸Ñ (доÑловно Invariants - Ñе ÑÑо оÑÑаÑÑÑÑ Ð½ÐµÐ¸Ð·Ð¼ÐµÐ½Ð½Ñми)
-
ÐекоÑоÑÑе оÑобенноÑÑи Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ð±ÑекÑа, коÑоÑÑе Ð´Ð¾Ð»Ð¶Ð½Ñ Ð±ÑÑÑ ÑÐ¾Ñ ÑÐ°Ð½ÐµÐ½Ñ Ð¿Ñи ÑеализаÑии полÑзоваÑелÑÑкого Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð½Ð°Ð·Ð²Ð°Ð½Ñ invariants. ÐÑли в обÑабоÑÑике наÑÑÑÐµÐ½Ñ Ñакие огÑаниÑениÑ, бÑÐ´ÐµÑ Ð²ÑбÑоÑена оÑибка
TypeError.
ÐбÑабоÑÑики и ловÑÑки
Ð ÑледÑÑÑей ÑаблиÑе пеÑеÑиÑÐ»ÐµÐ½Ñ Ð»Ð¾Ð²ÑÑки, доÑÑÑпнÑе Ð´Ð»Ñ Ð¸ÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² обÑекÑе Proxy. СмоÑÑиÑе подÑобнÑе обÑÑÑÐ½ÐµÐ½Ð¸Ñ Ð¸ пÑимеÑÑ Ð² докÑменÑаÑии.
| ÐбÑабоÑÑик / ловÑÑка | ÐеÑÐµÑ Ð²Ð°ÑÑваемÑе меÑÐ¾Ð´Ñ | ÐеизменÑемÑе огÑаниÑÐµÐ½Ð¸Ñ |
|---|---|---|
handler.getPrototypeOf()
|
Object.getPrototypeOf()Reflect.getPrototypeOf()__proto__Object.prototype.isPrototypeOf()instanceof
|
|
handler.setPrototypeOf()
|
Object.setPrototypeOf()Reflect.setPrototypeOf()
|
еÑли Ñелевой обÑÐµÐºÑ target неÑаÑÑиÑÑем, знаÑение паÑамеÑÑа
prototype должно бÑÑÑ ÑавнÑм знаÑÐµÐ½Ð¸Ñ Ð²Ð¾Ð·Ð²ÑаÑÐ°ÐµÐ¼Ð¾Ð¼Ñ Ð¼ÐµÑодом
Object.getPrototypeOf(target).
|
handler.isExtensible()
|
Object.isExtensible()Reflect.isExtensible()
|
Object.isExtensible(proxy) должно возвÑаÑаÑÑ Ñоже знаÑение,
ÑÑо и Object.isExtensible(target).
|
handler.preventExtensions()
|
Object.preventExtensions()Reflect.preventExtensions()
|
Object.preventExtensions(proxy) возвÑаÑаеÑ
true ÑолÑко в Ñом ÑлÑÑае, еÑли
Object.isExtensible(proxy) Ñавно false.
|
handler.getOwnPropertyDescriptor()
|
Object.getOwnPropertyDescriptor()Reflect.getOwnPropertyDescriptor()
|
|
handler.defineProperty()
|
Object.defineProperty()Reflect.defineProperty()
|
|
handler.has()
|
Property query: foo in proxyInherited property query: foo in Object.create(proxy)Reflect.has()
|
|
handler.get()
|
Property access: proxy[foo]and proxy.barInherited property access: Object.create(proxy)[foo]Reflect.get()
|
|
handler.set()
|
Property assignment: proxy[foo] = bar and
proxy.foo = barInherited property assignment: Object.create(proxy)[foo] = barReflect.set()
|
|
handler.deleteProperty()
|
Property deletion: delete proxy[foo] and
delete proxy.fooReflect.deleteProperty()
|
СвойÑÑво не Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ñдалено, еÑли оно ÑÑÑеÑÑвÑÐµÑ Ð² Ñелевом обÑекÑе как ÑобÑÑвенное, неконÑигÑÑиÑÑемое ÑвойÑÑво. |
handler.enumerate()
|
Property enumeration / for...in:
for (var name in proxy) {...}Reflect.enumerate()
|
ÐеÑод enumerate
должен возвÑаÑаÑÑ Ð¾Ð±ÑекÑ.
|
handler.ownKeys()
|
Object.getOwnPropertyNames()Object.getOwnPropertySymbols()Object.keys()Reflect.ownKeys()
|
|
handler.apply()
|
proxy(..args)Function.prototype.apply() and
Function.prototype.call()Reflect.apply()
|
ÐгÑаниÑений неÑ. |
handler.construct()
|
new proxy(...args)Reflect.construct()
|
ÐбÑабоÑÑик должен возвÑаÑаÑÑ Object. |
ÐÑзÑваемÑй Proxy
ÐеÑод Proxy.revocable() ÑоздаÑÑ Ð¾ÑзÑваемÑй обÑÐµÐºÑ Proxy. Такой пÑокÑи обÑÐµÐºÑ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¾Ñозван ÑÑнкÑией revoke, коÑоÑÐ°Ñ Ð¾ÑклÑÑÐ°ÐµÑ Ð²Ñе ловÑÑки-обÑабоÑÑики. ÐоÑле ÑÑого лÑбÑе опеÑаÑии над пÑокÑи обÑекÑом вÑзовÑÑ Ð¾ÑÐ¸Ð±ÐºÑ 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 Ð½ÐµÑ Ð»Ð¾Ð²ÑÑек
РеÑлекÑиÑ
Reflect ÑÑо вÑÑÑоеннÑй обÑекÑ, пÑедоÑÑавлÑÑÑий меÑÐ¾Ð´Ñ Ð´Ð»Ñ Ð¿ÐµÑеÑ
ваÑÑваемÑÑ
опеÑаÑий JavaScript. ÐÑо Ñе же ÑамÑе меÑодÑ, ÑÑо имеÑÑÑÑ Ð² обÑабоÑÑикаÑ
Proxy. ÐбÑÐµÐºÑ Reflect не ÑвлÑеÑÑÑ ÑÑнкÑией.
Reflect Ð¿Ð¾Ð¼Ð¾Ð³Ð°ÐµÑ Ð¿Ñи пеÑеÑÑлке ÑÑандаÑÑнÑÑ
опеÑаÑий из обÑабоÑÑика к ÑÐµÐ»ÐµÐ²Ð¾Ð¼Ñ Ð¾Ð±ÑекÑÑ.
ÐапÑимеÑ, меÑод Reflect.has() ÑÑо ÑÐ¾Ñ Ð¶Ðµ опеÑаÑÐ¾Ñ in но в виде ÑÑнкÑии:
Reflect.has(Object, "assign"); // true
УлÑÑÑÐµÐ½Ð½Ð°Ñ ÑÑнкÑÐ¸Ñ apply
Ð ES5 обÑÑно иÑполÑзÑеÑÑÑ Ð¼ÐµÑод Function.prototype.apply() Ð´Ð»Ñ Ð²Ñзова ÑÑнкÑии в опÑеделÑнном конÑекÑÑе (Ñ Ð¾Ð¿ÑеделÑннÑм this) и Ñ Ð¿Ð°ÑамеÑÑами, заданнÑми в виде маÑÑива (или маÑÑива-подобного обÑекÑа).
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
С меÑодом 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"
ÐÑовеÑка ÑÑпеÑноÑÑи опÑÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ ÑвойÑÑва
ÐеÑод Object.defineProperty, в ÑлÑÑае ÑÑпеÑ
а опеÑаÑии, возвÑаÑÐ°ÐµÑ Ð¾Ð±ÑекÑ, а пÑи неÑдаÑе вÑзÑÐ²Ð°ÐµÑ Ð¾ÑÐ¸Ð±ÐºÑ TypeError. Ðз-за ÑÑого опÑеделение ÑвойÑÑв ÑÑебÑÐµÑ Ð¾Ð±ÑабоÑки блоком try...catch Ð´Ð»Ñ Ð¿ÐµÑеÑ
ваÑа возможнÑÑ
оÑибок. ÐеÑод Reflect.defineProperty, в ÑÐ²Ð¾Ñ Ð¾ÑеÑедÑ, возвÑаÑÐ°ÐµÑ ÑÑпеÑноÑÑÑ Ð¾Ð¿ÐµÑаÑии в виде бÑлева знаÑениÑ, благодаÑÑ ÑÐµÐ¼Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ иÑполÑзование пÑоÑÑого if...else ÑÑловиÑ:
if (Reflect.defineProperty(target, property, attributes)) {
// ÑÑпеÑ
} else {
// ÑÑо-Ñо поÑло не Ñак
}