ÐбÑÐµÐºÑ Proxy «обоÑаÑиваеÑÑÑ» вокÑÑг дÑÑгого обÑекÑа и Ð¼Ð¾Ð¶ÐµÑ Ð¿ÐµÑеÑ
ваÑÑваÑÑ (и, пÑи желании, ÑамоÑÑоÑÑелÑно обÑабаÑÑваÑÑ) ÑазнÑе дейÑÑÐ²Ð¸Ñ Ñ Ð½Ð¸Ð¼, напÑÐ¸Ð¼ÐµÑ ÑÑение/запиÑÑ ÑвойÑÑв и дÑÑгие. Ðалее Ð¼Ñ Ð±Ñдем назÑваÑÑ Ñакие обÑекÑÑ Â«Ð¿ÑокÑи».
ÐÑокÑи иÑполÑзÑÑÑÑÑ Ð²Ð¾ Ð¼Ð½Ð¾Ð³Ð¸Ñ Ð±Ð¸Ð±Ð»Ð¸Ð¾ÑÐµÐºÐ°Ñ Ð¸ некоÑоÑÑÑ Ð±ÑаÑзеÑнÑÑ ÑÑеймвоÑÐºÐ°Ñ . Ð ÑÑой главе Ð¼Ñ Ñвидим много ÑлÑÑаев пÑÐ¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿ÑокÑи в ÑеÑении ÑеалÑнÑÑ Ð·Ð°Ð´Ð°Ñ.
СинÑакÑиÑ:
let proxy = new Proxy(target, handler);
targetâ ÑÑо обÑекÑ, Ð´Ð»Ñ ÐºÐ¾ÑоÑого нÑжно ÑделаÑÑ Ð¿ÑокÑи, Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ñем Ñгодно, вклÑÑÐ°Ñ ÑÑнкÑии.handlerâ конÑигÑÑаÑÐ¸Ñ Ð¿ÑокÑи: обÑÐµÐºÑ Ñ Â«Ð»Ð¾Ð²ÑÑками» («traps»): меÑодами, коÑоÑÑе пеÑÐµÑ Ð²Ð°ÑÑваÑÑ ÑазнÑе опеÑаÑии, напÑимеÑ, ловÑÑкаgetâ Ð´Ð»Ñ ÑÑÐµÐ½Ð¸Ñ ÑвойÑÑва изtarget, ловÑÑкаsetâ Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи ÑвойÑÑва вtargetи Ñак далее.
ÐÑи опеÑаÑиÑÑ
над proxy, еÑли в handler имееÑÑÑ ÑооÑвеÑÑÑвÑÑÑÐ°Ñ Â«Ð»Ð¾Ð²ÑÑка», Ñо она ÑÑабаÑÑваеÑ, и пÑокÑи Ð¸Ð¼ÐµÐµÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð¿Ð¾-ÑÐ²Ð¾ÐµÐ¼Ñ Ð¾Ð±ÑабоÑаÑÑ ÐµÑ, инаÑе опеÑаÑÐ¸Ñ Ð±ÑÐ´ÐµÑ ÑовеÑÑена над оÑигиналÑнÑм обÑекÑом target.
РкаÑеÑÑве наÑалÑного пÑимеÑа Ñоздадим пÑокÑи без вÑÑÐºÐ¸Ñ Ð»Ð¾Ð²ÑÑек:
let target = {};
let proxy = new Proxy(target, {}); // пÑÑÑой handler
proxy.test = 5; // запиÑÑваем в пÑокÑи (1)
alert(target.test); // 5, ÑвойÑÑво поÑвилоÑÑ Ð² target!
alert(proxy.test); // 5, Ð¼Ñ Ñакже можем пÑоÑиÑаÑÑ ÐµÐ³Ð¾ из пÑокÑи (2)
for(let key in proxy) alert(key); // test, иÑеÑаÑÐ¸Ñ ÑабоÑÐ°ÐµÑ (3)
Так как Ð½ÐµÑ Ð»Ð¾Ð²ÑÑек, Ñо вÑе опеÑаÑии на proxy пÑименÑÑÑÑÑ Ðº оÑигиналÑÐ½Ð¾Ð¼Ñ Ð¾Ð±ÑекÑÑ target.
- ÐапиÑÑ ÑвойÑÑва
proxy.test=ÑÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ Ð·Ð½Ð°Ñение наtarget. - ЧÑение ÑвойÑÑва
proxy.testвозвÑаÑÐ°ÐµÑ Ð·Ð½Ð°Ñение изtarget. - ÐÑеÑаÑÐ¸Ñ Ð¿Ð¾
proxyвозвÑаÑÐ°ÐµÑ Ð·Ð½Ð°ÑÐµÐ½Ð¸Ñ Ð¸Ð·target.
Ðак Ð¼Ñ Ð²Ð¸Ð´Ð¸Ð¼, без ловÑÑек proxy ÑвлÑеÑÑÑ Ð¿ÑозÑаÑной обÑÑÑкой над target.
Proxy â ÑÑо оÑобÑй, «ÑкзоÑиÑеÑкий», обÑекÑ, Ñ Ð½ÐµÐ³Ð¾ Ð½ÐµÑ ÑобÑÑвеннÑÑ
ÑвойÑÑв. С пÑÑÑÑм handler он пÑоÑÑо пеÑенапÑавлÑÐµÑ Ð²Ñе опеÑаÑии на target.
ЧÑÐ¾Ð±Ñ Ð°ÐºÑивиÑоваÑÑ Ð´ÑÑгие его возможноÑÑи, добавим ловÑÑки.
ЧÑо именно Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ими пеÑÐµÑ Ð²Ð°ÑиÑÑ?
ÐÐ»Ñ Ð±Ð¾Ð»ÑÑинÑÑва дейÑÑвий Ñ Ð¾Ð±ÑекÑами в ÑпеÑиÑикаÑии JavaScript еÑÑÑ Ñак назÑваемÑй «внÑÑÑенний меÑод», коÑоÑÑй на Ñамом низком ÑÑовне опиÑÑваеÑ, как его вÑполнÑÑÑ. ÐапÑимеÑ, [[Get]] â внÑÑÑенний меÑод Ð´Ð»Ñ ÑÑÐµÐ½Ð¸Ñ ÑвойÑÑва, [[Set]] â Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи ÑвойÑÑва, и Ñак далее. ÐÑи меÑÐ¾Ð´Ñ Ð¸ÑполÑзÑÑÑÑÑ ÑолÑко в ÑпеÑиÑикаÑии, Ð¼Ñ Ð½Ðµ можем обÑаÑиÑÑÑÑ Ð½Ð°Ð¿ÑÑмÑÑ Ðº ним по имени.
ÐовÑÑки как Ñаз пеÑÐµÑ Ð²Ð°ÑÑваÑÑ Ð²ÑÐ·Ð¾Ð²Ñ ÑÑÐ¸Ñ Ð²Ð½ÑÑÑÐµÐ½Ð½Ð¸Ñ Ð¼ÐµÑодов. ÐолнÑй ÑпиÑок меÑодов, коÑоÑÑе можно пеÑÐµÑ Ð²Ð°ÑÑваÑÑ, пеÑеÑиÑлен в ÑпеÑиÑикаÑии Proxy, а Ñакже в ÑаблиÑе ниже.
ÐÐ»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ внÑÑÑеннего меÑода в ÑÑой ÑаблиÑе Ñказана ловÑÑка, Ñо еÑÑÑ Ð¸Ð¼Ñ Ð¼ÐµÑода, коÑоÑÑй Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ добавиÑÑ Ð² паÑамеÑÑ handler пÑи Ñоздании new Proxy, ÑÑÐ¾Ð±Ñ Ð¿ÐµÑеÑ
ваÑÑваÑÑ Ð´Ð°Ð½Ð½ÑÑ Ð¾Ð¿ÐµÑаÑиÑ:
| ÐнÑÑÑенний меÑод | ÐовÑÑка | ЧÑо вÑзÑÐ²Ð°ÐµÑ |
|---|---|---|
[[Get]] |
get |
ÑÑение ÑвойÑÑва |
[[Set]] |
set |
запиÑÑ ÑвойÑÑва |
[[HasProperty]] |
has |
опеÑаÑÐ¾Ñ in |
[[Delete]] |
deleteProperty |
опеÑаÑÐ¾Ñ delete |
[[Call]] |
apply |
вÑзов ÑÑнкÑии |
[[Construct]] |
construct |
опеÑаÑÐ¾Ñ new |
[[GetPrototypeOf]] |
getPrototypeOf |
Object.getPrototypeOf |
[[SetPrototypeOf]] |
setPrototypeOf |
Object.setPrototypeOf |
[[IsExtensible]] |
isExtensible |
Object.isExtensible |
[[PreventExtensions]] |
preventExtensions |
Object.preventExtensions |
[[DefineOwnProperty]] |
defineProperty |
Object.defineProperty, Object.defineProperties |
[[GetOwnProperty]] |
getOwnPropertyDescriptor |
Object.getOwnPropertyDescriptor, for..in, Object.keys/values/entries |
[[OwnPropertyKeys]] |
ownKeys |
Object.getOwnPropertyNames, Object.getOwnPropertySymbols, for..in, Object.keys/values/entries |
JavaScript Ð½Ð°Ð»Ð°Ð³Ð°ÐµÑ Ð½ÐµÐºÐ¾ÑоÑÑе ÑÑÐ»Ð¾Ð²Ð¸Ñ â инваÑианÑÑ Ð½Ð° ÑеализаÑÐ¸Ñ Ð²Ð½ÑÑÑÐµÐ½Ð½Ð¸Ñ Ð¼ÐµÑодов и ловÑÑек.
ÐолÑÑинÑÑво из Ð½Ð¸Ñ ÐºÐ°ÑаÑÑÑÑ Ð²Ð¾Ð·Ð²ÑаÑаемÑÑ Ð·Ð½Ð°Ñений:
- ÐеÑод
[[Set]]должен возвÑаÑаÑÑtrue, еÑли знаÑение бÑло ÑÑпеÑно запиÑано, инаÑеfalse. - ÐеÑод
[[Delete]]должен возвÑаÑаÑÑtrue, еÑли знаÑение бÑло ÑÑпеÑно Ñдалено, инаÑеfalse. - â¦Ð¸ Ñак далее, Ð¼Ñ Ñвидим болÑÑе в пÑимеÑÐ°Ñ Ð½Ð¸Ð¶Ðµ.
ÐÑÑÑ Ð¸ дÑÑгие инваÑианÑÑ, напÑимеÑ:
- ÐеÑод
[[GetPrototypeOf]], пÑименÑннÑй к пÑокÑи, должен возвÑаÑаÑÑ Ñо же знаÑение, ÑÑо и меÑод[[GetPrototypeOf]], пÑименÑннÑй к оÑигиналÑÐ½Ð¾Ð¼Ñ Ð¾Ð±ÑекÑÑ. ÐÑÑгими Ñловами, ÑÑение пÑоÑоÑипа обÑекÑа пÑокÑи вÑегда должно возвÑаÑаÑÑ Ð¿ÑоÑоÑип оÑигиналÑного обÑекÑа.
ÐовÑÑки могÑÑ Ð¿ÐµÑÐµÑ Ð²Ð°ÑÑваÑÑ Ð²ÑÐ·Ð¾Ð²Ñ ÑÑÐ¸Ñ Ð¼ÐµÑодов, но Ð´Ð¾Ð»Ð¶Ð½Ñ Ð²ÑполнÑÑÑ ÑказаннÑе ÑÑловиÑ.
ÐнваÑианÑÑ Ð³Ð°ÑанÑиÑÑÑÑ ÐºÐ¾ÑÑекÑное и поÑледоваÑелÑное поведение конÑÑÑÑкÑий и меÑодов ÑзÑка. ÐолнÑй ÑпиÑок инваÑианÑов можно найÑи в ÑпеÑиÑикаÑии, Ñ Ð¾ÑÑ ÑкоÑее вÑего Ð²Ñ Ð½Ðµ наÑÑÑиÑе ÑÑи ÑÑловиÑ, еÑли ÑолÑко не ÑобеÑÑÑеÑÑ Ð´ÐµÐ»Ð°ÑÑ ÑÑо-Ñо ÑовÑем Ñж ÑÑÑанное.
ТепеÑÑ Ð´Ð°Ð²Ð°Ð¹Ñе поÑмоÑÑим, как ÑÑо вÑÑ ÑабоÑаеÑ, на ÑеалÑнÑÑ Ð¿ÑимеÑÐ°Ñ .
ÐнаÑение по ÑмолÑÐ°Ð½Ð¸Ñ Ñ Ð»Ð¾Ð²ÑÑкой «get»
ЧаÑе вÑего иÑполÑзÑÑÑÑÑ Ð»Ð¾Ð²ÑÑки на ÑÑение/запиÑÑ ÑвойÑÑв.
ЧÑÐ¾Ð±Ñ Ð¿ÐµÑеÑ
ваÑиÑÑ Ð¾Ð¿ÐµÑаÑÐ¸Ñ ÑÑениÑ, handler должен имеÑÑ Ð¼ÐµÑод get(target, property, receiver).
Ðн ÑÑабаÑÑÐ²Ð°ÐµÑ Ð¿Ñи попÑÑке пÑоÑиÑаÑÑ ÑвойÑÑво обÑекÑа, Ñ Ð°ÑгÑменÑами:
targetâ ÑÑо оÑигиналÑнÑй обÑекÑ, коÑоÑÑй пеÑедавалÑÑ Ð¿ÐµÑвÑм аÑгÑменÑом в конÑÑÑÑкÑоÑnew Proxy,propertyâ Ð¸Ð¼Ñ ÑвойÑÑва,receiverâ еÑли ÑвойÑÑво обÑекÑа ÑвлÑеÑÑÑ Ð³ÐµÑÑеÑом, Ñоreceiverâ ÑÑо обÑекÑ, коÑоÑÑй бÑÐ´ÐµÑ Ð¸ÑполÑзован какthisпÑи его вÑзове. ÐбÑÑно ÑÑо Ñам обÑÐµÐºÑ Ð¿ÑокÑи (или наÑледÑÑÑий Ð¾Ñ Ð½ÐµÐ³Ð¾ обÑекÑ). ÐÑÑмо ÑейÑÐ°Ñ Ð½Ð°Ð¼ не понадобиÑÑÑ ÑÑÐ¾Ñ Ð°ÑгÑменÑ, подÑобнее ÑазбеÑÑм его позже.
ÐавайÑе пÑименим ловÑÑÐºÑ get, ÑÑÐ¾Ð±Ñ ÑеализоваÑÑ Â«Ð·Ð½Ð°ÑÐµÐ½Ð¸Ñ Ð¿Ð¾ ÑмолÑаниÑ» Ð´Ð»Ñ ÑвойÑÑв обÑекÑа.
ÐапÑимеÑ, Ñделаем ÑиÑловой маÑÑив, Ñак ÑÑÐ¾Ð±Ñ Ð¿Ñи ÑÑении из него неÑÑÑеÑÑвÑÑÑего ÑлеменÑа возвÑаÑалÑÑ 0.
ÐбÑÑно пÑи ÑÑении из маÑÑива неÑÑÑеÑÑвÑÑÑего ÑвойÑÑва возвÑаÑаеÑÑÑ undefined, но Ð¼Ñ Ð¾Ð±ÐµÑнÑм обÑÑнÑй маÑÑив в пÑокÑи, коÑоÑÑй пеÑеÑ
ваÑÑÐ²Ð°ÐµÑ Ð¾Ð¿ÐµÑаÑÐ¸Ñ ÑÑÐµÐ½Ð¸Ñ ÑвойÑÑва из маÑÑива и возвÑаÑÐ°ÐµÑ 0, еÑли Ñакого ÑлеменÑа неÑ:
let numbers = [0, 1, 2];
numbers = new Proxy(numbers, {
get(target, prop) {
if (prop in target) {
return target[prop];
} else {
return 0; // знаÑение по ÑмолÑаниÑ
}
}
});
alert( numbers[1] ); // 1
alert( numbers[123] ); // 0 (Ð½ÐµÑ Ñакого ÑлеменÑа)
Ðак видно, ÑÑо оÑÐµÐ½Ñ Ð»ÐµÐ³ÐºÐ¾ ÑделаÑÑ Ð¿Ñи помоÑи ловÑÑки get.
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ Proxy Ð´Ð»Ñ ÑеализаÑии лÑбой логики возвÑаÑа знаÑений по ÑмолÑаниÑ.
ÐÑедÑÑавим, ÑÑо Ñ Ð½Ð°Ñ ÐµÑÑÑ Ð¾Ð±ÑекÑ-ÑловаÑÑ Ñ ÑÑазами на английÑком и Ð¸Ñ Ð¿ÐµÑеводом на иÑпанÑкий:
let dictionary = {
'Hello': 'Hola',
'Bye': 'Adiós'
};
alert( dictionary['Hello'] ); // Hola
alert( dictionary['Welcome'] ); // undefined
СейÑаÑ, еÑли ÑÑÐ°Ð·Ñ Ð² dictionary неÑ, пÑи ÑÑении возвÑаÑаеÑÑÑ undefined. Ðо на пÑакÑике оÑÑавлÑÑÑ ÑÑÐ°Ð·Ñ Ð½ÐµÐ¿ÐµÑеведÑннÑми лÑÑÑе, Ñем иÑполÑзоваÑÑ undefined. ÐоÑÑÐ¾Ð¼Ñ Ð´Ð°Ð²Ð°Ð¹Ñе Ñделаем Ñак, ÑÑÐ¾Ð±Ñ Ð¿Ñи оÑÑÑÑÑÑвии пеÑевода возвÑаÑалаÑÑ Ð¾ÑигиналÑÐ½Ð°Ñ ÑÑаза на английÑком вмеÑÑо undefined.
ЧÑÐ¾Ð±Ñ Ð´Ð¾ÑÑиÑÑ ÑÑого, обеÑнÑм dictionary в пÑокÑи, пеÑеÑ
ваÑÑваÑÑий опеÑаÑÐ¸Ñ ÑÑениÑ:
let dictionary = {
'Hello': 'Hola',
'Bye': 'Adiós'
};
dictionary = new Proxy(dictionary, {
get(target, phrase) { // пеÑеÑ
ваÑÑваем ÑÑение ÑвойÑÑва в dictionary
if (phrase in target) { // еÑли пеÑевод Ð´Ð»Ñ ÑÑÐ°Ð·Ñ ÐµÑÑÑ Ð² ÑловаÑе
return target[phrase]; // возвÑаÑаем его
} else {
// инаÑе возвÑаÑаем непеÑеведÑннÑÑ ÑÑазÑ
return phrase;
}
}
});
// ÐапÑоÑим пеÑевод пÑоизволÑного вÑÑÐ°Ð¶ÐµÐ½Ð¸Ñ Ð² ÑловаÑе!
// Ð Ñ
ÑдÑем ÑлÑÑае оно не бÑÐ´ÐµÑ Ð¿ÐµÑеведено
alert( dictionary['Hello'] ); // Hola
alert( dictionary['Welcome to Proxy']); // Welcome to Proxy (Ð½ÐµÑ Ð¿ÐµÑевода)
targetÐожалÑйÑÑа, обÑаÑиÑе внимание: пÑокÑи пеÑезапиÑÑÐ²Ð°ÐµÑ Ð¿ÐµÑеменнÑÑ:
dictionary = new Proxy(dictionary, ...);
ÐÑокÑи должен замениÑÑ Ñобой оÑигиналÑнÑй обÑÐµÐºÑ Ð¿Ð¾Ð²ÑÑдÑ. ÐикÑо не должен ÑÑÑлаÑÑÑÑ Ð½Ð° оÑигиналÑнÑй обÑÐµÐºÑ Ð¿Ð¾Ñле Ñого, как он бÑл пÑокÑиÑован. ÐнаÑе оÑÐµÐ½Ñ Ð»ÐµÐ³ÐºÐ¾ запÑÑаÑÑÑÑ.
ÐалидаÑÐ¸Ñ Ñ Ð»Ð¾Ð²ÑÑкой «set»
ÐопÑÑÑим, Ð¼Ñ Ñ Ð¾Ñим ÑделаÑÑ Ð¼Ð°ÑÑив иÑклÑÑиÑелÑно Ð´Ð»Ñ ÑиÑел. ÐÑли в него добавлÑеÑÑÑ Ð·Ð½Ð°Ñение иного Ñипа, Ñо ÑÑо должно пÑиводиÑÑ Ðº оÑибке.
ÐовÑÑка set ÑÑабаÑÑваеÑ, когда пÑоиÑÑ
Ð¾Ð´Ð¸Ñ Ð·Ð°Ð¿Ð¸ÑÑ ÑвойÑÑва.
set(target, property, value, receiver):
targetâ ÑÑо оÑигиналÑнÑй обÑекÑ, коÑоÑÑй пеÑедавалÑÑ Ð¿ÐµÑвÑм аÑгÑменÑом в конÑÑÑÑкÑоÑnew Proxy,propertyâ Ð¸Ð¼Ñ ÑвойÑÑва,valueâ знаÑение ÑвойÑÑва,receiverâ аналогиÑно ловÑÑкеget, ÑÑÐ¾Ñ Ð°ÑгÑÐ¼ÐµÐ½Ñ Ð¸Ð¼ÐµÐµÑ Ð·Ð½Ð°Ñение, ÑолÑко еÑли ÑвойÑÑво â ÑеÑÑеÑ.
ÐовÑÑка set должна веÑнÑÑÑ true, еÑли запиÑÑ Ð¿ÑоÑла ÑÑпеÑно, и false в пÑоÑивном ÑлÑÑае (бÑÐ´ÐµÑ ÑгенеÑиÑована оÑибка TypeError).
ÐавайÑе пÑименим ÐµÑ Ð´Ð»Ñ Ð¿ÑовеÑки новÑÑ Ð·Ð½Ð°Ñений:
let numbers = [];
numbers = new Proxy(numbers, { // (*)
set(target, prop, val) { // Ð´Ð»Ñ Ð¿ÐµÑеÑ
ваÑа запиÑи ÑвойÑÑва
if (typeof val == 'number') {
target[prop] = val;
return true;
} else {
return false;
}
}
});
numbers.push(1); // добавилоÑÑ ÑÑпеÑно
numbers.push(2); // добавилоÑÑ ÑÑпеÑно
alert("Ðлина: " + numbers.length); // 2
numbers.push("ÑеÑÑ"); // TypeError (ловÑÑка set на пÑокÑи веÑнÑла false)
alert("ÐнÑеÑпÑеÑаÑÐ¾Ñ Ð½Ð¸ÐºÐ¾Ð³Ð´Ð° не доÑ
Ð¾Ð´Ð¸Ñ Ð´Ð¾ ÑÑой ÑÑÑоки (из-за оÑибки в ÑÑÑоке вÑÑе)");
ÐбÑаÑиÑе внимание, ÑÑо вÑÑÑÐ¾ÐµÐ½Ð½Ð°Ñ ÑÑнкÑионалÑноÑÑÑ Ð¼Ð°ÑÑива по-пÑÐµÐ¶Ð½ÐµÐ¼Ñ ÑабоÑаеÑ! ÐнаÑÐµÐ½Ð¸Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÑÑÑÑÑ Ð¼ÐµÑодом push. СвойÑÑво length пÑи ÑÑом ÑвелиÑиваеÑÑÑ. ÐÐ°Ñ Ð¿ÑокÑи ниÑего не ломаеÑ.
Ðам не нÑжно пеÑеопÑеделÑÑÑ Ð¼ÐµÑÐ¾Ð´Ñ Ð¼Ð°ÑÑива push и unshift и дÑÑгие, ÑÑÐ¾Ð±Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÑÑÑ ÑÑда пÑовеÑÐºÑ Ð½Ð° Ñип, Ñак как внÑÑÑи ÑÐµÐ±Ñ Ð¾Ð½Ð¸ иÑполÑзÑÑÑ Ð¾Ð¿ÐµÑаÑÐ¸Ñ [[Set]], коÑоÑÐ°Ñ Ð¿ÐµÑеÑ
ваÑÑваеÑÑÑ Ð¿ÑокÑи.
Таким обÑазом, код оÑÑаÑÑÑÑ ÑиÑÑÑм и пÑозÑаÑнÑм.
trueÐак Ñказано Ñанее, нÑжно ÑоблÑдаÑÑ Ð¸Ð½Ð²Ð°ÑианÑÑ.
ÐÐ»Ñ set ÑеализаÑÐ¸Ñ Ð»Ð¾Ð²ÑÑки должна возвÑаÑаÑÑ true в ÑлÑÑае ÑÑпеÑной запиÑи ÑвойÑÑва.
ÐÑли забÑÑÑ ÑÑо ÑделаÑÑ Ð¸Ð»Ð¸ возвÑаÑиÑÑ Ð»Ñбое ложное знаÑение, ÑÑо пÑиведÑÑ Ðº оÑибке TypeError.
ÐеÑÐµÐ±Ð¾Ñ Ð¿Ñи помоÑи «ownKeys» и «getOwnPropertyDescriptor»
Object.keys, Ñикл for..in и болÑÑинÑÑво дÑÑгиÑ
меÑодов, коÑоÑÑе ÑабоÑаÑÑ Ñо ÑпиÑком ÑвойÑÑв обÑекÑа, иÑполÑзÑÑÑ Ð²Ð½ÑÑÑенний меÑод [[OwnPropertyKeys]] (пеÑеÑ
ваÑÑваемÑй ловÑÑкой ownKeys) Ð´Ð»Ñ Ð¸Ñ
полÑÑениÑ.
Такие меÑÐ¾Ð´Ñ ÑазлиÑаÑÑÑÑ Ð² деÑалÑÑ :
Object.getOwnPropertyNames(obj)возвÑаÑÐ°ÐµÑ Ð½Ðµ-ÑимволÑнÑе клÑÑи.Object.getOwnPropertySymbols(obj)возвÑаÑÐ°ÐµÑ ÑимволÑнÑе клÑÑи.Object.keys/values()возвÑаÑÐ°ÐµÑ Ð½Ðµ-ÑимволÑнÑе клÑÑи/знаÑÐµÐ½Ð¸Ñ Ñ Ñлагомenumerable(подÑобнее пÑо Ñлаги ÑвойÑÑв бÑло в главе Флаги и деÑкÑипÑоÑÑ ÑвойÑÑв).for..inпеÑебиÑÐ°ÐµÑ Ð½Ðµ-ÑимволÑнÑе клÑÑи Ñ Ñлагомenumerable, а Ñакже клÑÑи пÑоÑоÑипов.
â¦Ðо вÑе они наÑинаÑÑ Ñ ÑÑого ÑпиÑка.
РпÑимеÑе ниже Ð¼Ñ Ð¸ÑполÑзÑем ловÑÑÐºÑ ownKeys, ÑÑÐ¾Ð±Ñ Ñикл for..in по обÑекÑÑ, Ñавно как Object.keys и Object.values пÑопÑÑкали ÑвойÑÑва, наÑинаÑÑиеÑÑ Ñ Ð¿Ð¾Ð´ÑÑÑÐºÐ¸Ð²Ð°Ð½Ð¸Ñ _:
let user = {
name: "ÐаÑÑ",
age: 30,
_password: "***"
};
user = new Proxy(user, {
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'));
}
});
// ownKeys иÑклÑÑил _password
for(let key in user) alert(key); // name, заÑем: age
// аналогиÑнÑй ÑÑÑÐµÐºÑ Ð´Ð»Ñ ÑÑиÑ
меÑодов:
alert( Object.keys(user) ); // name,age
alert( Object.values(user) ); // ÐаÑÑ,30
Ðак видно, ÑабоÑаеÑ.
ÐпÑоÑем, еÑли Ð¼Ñ Ð¿Ð¾Ð¿ÑобÑем возвÑаÑиÑÑ ÐºÐ»ÑÑ, коÑоÑого в обÑекÑе на Ñамом деле неÑ, Ñо Object.keys его не вÑдаÑÑ:
let user = { };
user = new Proxy(user, {
ownKeys(target) {
return ['a', 'b', 'c'];
}
});
alert( Object.keys(user) ); // <пÑÑÑо>
ÐоÑемÑ? ÐÑиÑина пÑоÑÑа: Object.keys возвÑаÑÐ°ÐµÑ ÑолÑко ÑвойÑÑва Ñ Ñлагом enumerable. ÐÐ»Ñ Ñого, ÑÑÐ¾Ð±Ñ Ð¾Ð¿ÑеделиÑÑ, еÑÑÑ Ð»Ð¸ ÑÑÐ¾Ñ Ñлаг, он Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ ÑвойÑÑва вÑзÑÐ²Ð°ÐµÑ Ð²Ð½ÑÑÑенний меÑод [[GetOwnProperty]], коÑоÑÑй полÑÑÐ°ÐµÑ ÐµÐ³Ð¾ деÑкÑипÑоÑ. Рв данном ÑлÑÑае ÑвойÑÑво оÑÑÑÑÑÑвÑеÑ, его деÑкÑипÑÐ¾Ñ Ð¿ÑÑÑ, Ñлага enumerable неÑ, поÑÑÐ¾Ð¼Ñ Ð¾Ð½Ð¾ пÑопÑÑкаеÑÑÑ.
ЧÑÐ¾Ð±Ñ Object.keys возвÑаÑал ÑвойÑÑво, нÑжно либо ÑÑÐ¾Ð±Ñ ÑвойÑÑво в обÑекÑе ÑизиÑеÑки бÑло, пÑиÑÑм Ñ Ñлагом enumerable, либо пеÑеÑ
ваÑиÑÑ Ð²ÑÐ·Ð¾Ð²Ñ [[GetOwnProperty]] (ÑÑо Ð´ÐµÐ»Ð°ÐµÑ Ð»Ð¾Ð²ÑÑка getOwnPropertyDescriptor), и Ñам веÑнÑÑÑ Ð´ÐµÑкÑипÑÐ¾Ñ Ñ enumerable: true.
ÐÐ¾Ñ Ñак бÑÐ´ÐµÑ ÑабоÑаÑÑ:
let user = { };
user = new Proxy(user, {
ownKeys(target) { // вÑзÑваеÑÑÑ 1 Ñаз Ð´Ð»Ñ Ð¿Ð¾Ð»ÑÑÐµÐ½Ð¸Ñ ÑпиÑка ÑвойÑÑв
return ['a', 'b', 'c'];
},
getOwnPropertyDescriptor(target, prop) { // вÑзÑваеÑÑÑ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ ÑвойÑÑва
return {
enumerable: true,
configurable: true
/* ...дÑÑгие Ñлаги, возможно, "value: ..." */
};
}
});
alert( Object.keys(user) ); // a, b, c
ÐÑÑ Ñаз замеÑим, ÑÑо полÑÑение деÑкÑипÑоÑа нÑжно пеÑÐµÑ Ð²Ð°ÑÑваÑÑ ÑолÑко еÑли ÑвойÑÑво оÑÑÑÑÑÑвÑÐµÑ Ð² Ñамом обÑекÑе.
ÐаÑиÑÑннÑе ÑвойÑÑва Ñ Ð»Ð¾Ð²ÑÑкой «deleteProperty» и дÑÑгими
СÑÑеÑÑвÑÐµÑ ÑиÑоко ÑаÑпÑоÑÑÑанÑнное ÑоглаÑение о Ñом, ÑÑо ÑвойÑÑва и меÑодÑ, название коÑоÑÑÑ
наÑинаеÑÑÑ Ñ Ñимвола подÑÑÑÐºÐ¸Ð²Ð°Ð½Ð¸Ñ _, ÑледÑÐµÑ ÑÑиÑаÑÑ Ð²Ð½ÑÑÑенними. Рним не ÑледÑÐµÑ Ð¾Ð±ÑаÑаÑÑÑÑ ÑнаÑÑжи обÑекÑа.
Ðднако ÑÐµÑ Ð½Ð¸ÑеÑки ÑÑо вÑÑ Ñавно возможно:
let user = {
name: "ÐаÑÑ",
_password: "secret"
};
alert(user._password); // secret
ÐавайÑе пÑименим пÑокÑи, ÑÑÐ¾Ð±Ñ Ð·Ð°ÑиÑиÑÑ ÑвойÑÑва, наÑинаÑÑиеÑÑ Ð½Ð° _, Ð¾Ñ Ð´Ð¾ÑÑÑпа извне.
Ðам бÑдÑÑ Ð½ÑÐ¶Ð½Ñ ÑледÑÑÑие ловÑÑки:
getâ Ð´Ð»Ñ Ñого, ÑÑÐ¾Ð±Ñ ÑгенеÑиÑоваÑÑ Ð¾ÑÐ¸Ð±ÐºÑ Ð¿Ñи ÑÑении Ñакого ÑвойÑÑва,setâ Ð´Ð»Ñ Ñого, ÑÑÐ¾Ð±Ñ ÑгенеÑиÑоваÑÑ Ð¾ÑÐ¸Ð±ÐºÑ Ð¿Ñи запиÑи,deletePropertyâ Ð´Ð»Ñ Ñого, ÑÑÐ¾Ð±Ñ ÑгенеÑиÑоваÑÑ Ð¾ÑÐ¸Ð±ÐºÑ Ð¿Ñи Ñдалении,ownKeysâ Ð´Ð»Ñ Ñого, ÑÑÐ¾Ð±Ñ Ð¸ÑклÑÑиÑÑ Ñакие ÑвойÑÑва изfor..inи меÑодов ÑипаObject.keys.
ÐÐ¾Ñ ÑооÑвеÑÑÑвÑÑÑий код:
let user = {
name: "ÐаÑÑ",
_password: "***"
};
user = new Proxy(user, {
get(target, prop) {
if (prop.startsWith('_')) {
throw new Error("ÐÑказано в доÑÑÑпе");
} else {
let value = target[prop];
return (typeof value === 'function') ? value.bind(target) : value; // (*)
}
},
set(target, prop, val) { // пеÑеÑ
ваÑÑваем запиÑÑ ÑвойÑÑва
if (prop.startsWith('_')) {
throw new Error("ÐÑказано в доÑÑÑпе");
} else {
target[prop] = val;
return true;
}
},
deleteProperty(target, prop) { // пеÑеÑ
ваÑÑваем Ñдаление ÑвойÑÑва
if (prop.startsWith('_')) {
throw new Error("ÐÑказано в доÑÑÑпе");
} else {
delete target[prop];
return true;
}
},
ownKeys(target) { // пеÑеÑ
ваÑÑваем попÑÑÐºÑ Ð¸ÑеÑаÑии
return Object.keys(target).filter(key => !key.startsWith('_'));
}
});
// "get" не позволÑÐµÑ Ð¿ÑоÑиÑаÑÑ _password
try {
alert(user._password); // Error: ÐÑказано в доÑÑÑпе
} catch(e) { alert(e.message); }
// "set" не позволÑÐµÑ Ð·Ð°Ð¿Ð¸ÑаÑÑ _password
try {
user._password = "test"; // Error: ÐÑказано в доÑÑÑпе
} catch(e) { alert(e.message); }
// "deleteProperty" не позволÑÐµÑ ÑдалиÑÑ _password
try {
delete user._password; // Error: ÐÑказано в доÑÑÑпе
} catch(e) { alert(e.message); }
// "ownKeys" иÑклÑÑÐ°ÐµÑ _password из ÑпиÑка видимÑÑ
Ð´Ð»Ñ Ð¸ÑеÑаÑии ÑвойÑÑв
for(let key in user) alert(key); // name
ÐбÑаÑиÑе внимание на важнÑÑ Ð´ÐµÑÐ°Ð»Ñ Ð² ловÑÑке get на ÑÑÑоке (*):
get(target, prop) {
// ...
let value = target[prop];
return (typeof value === 'function') ? value.bind(target) : value; // (*)
}
ÐаÑем Ð´Ð»Ñ ÑÑнкÑии вÑзÑваÑÑ value.bind(target)?
ÐÑÑ Ð´ÐµÐ»Ð¾ в Ñом, ÑÑо меÑод Ñамого обÑекÑа, напÑÐ¸Ð¼ÐµÑ user.checkPassword(), должен имеÑÑ Ð´Ð¾ÑÑÑп к ÑвойÑÑÐ²Ñ _password:
user = {
// ...
checkPassword(value) {
// меÑод обÑекÑа должен имеÑÑ Ð´Ð¾ÑÑÑп на ÑÑение _password
return value === this._password;
}
}
ÐÑзов user.checkPassword() полÑÑÐ°ÐµÑ Ð¿ÑокÑиÑованнÑй обÑÐµÐºÑ user в каÑеÑÑве this (обÑÐµÐºÑ Ð¿ÐµÑед ÑоÑкой ÑÑановиÑÑÑ this), Ñак ÑÑо когда Ñакой вÑзов обÑаÑаеÑÑÑ Ðº this._password, ловÑÑка get вÑÑÑÐ¿Ð°ÐµÑ Ð² дейÑÑвие (она ÑÑабаÑÑÐ²Ð°ÐµÑ Ð¿Ñи лÑбом ÑÑении ÑвойÑÑва), и вÑбÑаÑÑваеÑÑÑ Ð¾Ñибка.
ÐоÑÑÐ¾Ð¼Ñ Ð¼Ñ Ð¿ÑивÑзÑваем конÑекÑÑ Ðº меÑодам обÑекÑа â оÑигиналÑнÑй обÑÐµÐºÑ target в ÑÑÑоке (*). Тогда иÑ
далÑнейÑие вÑÐ·Ð¾Ð²Ñ Ð±ÑдÑÑ Ð¸ÑполÑзоваÑÑ target в каÑеÑÑве this, без вÑÑкиÑ
ловÑÑек.
Такое ÑеÑение обÑÑно ÑабоÑаеÑ, но не ÑвлÑеÑÑÑ Ð¸Ð´ÐµÐ°Ð»ÑнÑм, поÑколÑÐºÑ Ð¼ÐµÑод Ð¼Ð¾Ð¶ÐµÑ Ð¿ÐµÑедаÑÑ Ð¾ÑигиналÑнÑй обÑÐµÐºÑ ÐºÑда-Ñо еÑÑ, и возможна пÑÑаниÑа: где изнаÑалÑнÑй обÑекÑ, а где â пÑокÑиÑованнÑй.
Ð ÑÐ¾Ð¼Ñ Ð¶Ðµ, обÑÐµÐºÑ Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑокÑиÑоваÑÑÑÑ Ð½ÐµÑколÑко Ñаз (Ð´Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑазлиÑнÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑей), и еÑли пеÑедаваÑÑ Ð¼ÐµÑÐ¾Ð´Ñ Ð¸ÑÑ Ð¾Ð´Ð½Ñй, Ñо могÑÑ Ð±ÑÑÑ Ð½ÐµÐ¾Ð¶Ð¸Ð´Ð°Ð½Ð½Ð¾ÑÑи.
Так ÑÑо везде иÑполÑзоваÑÑ Ñакой пÑокÑи не ÑÑоиÑ.
СовÑеменнÑе инÑеÑпÑеÑаÑоÑÑ JavaScript поддеÑживаÑÑ Ð¿ÑиваÑнÑе ÑвойÑÑва в клаÑÑаÑ
. ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ ÑакиÑ
ÑвойÑÑв Ð´Ð¾Ð»Ð¶Ð½Ñ Ð½Ð°ÑинаÑÑÑÑ Ñ Ñимвола #. Ðни подÑобно опиÑÐ°Ð½Ñ Ð² главе ÐÑиваÑнÑе и заÑиÑÑннÑе меÑÐ¾Ð´Ñ Ð¸ ÑвойÑÑва. ÐÐ»Ñ Ð½Ð¸Ñ
не нÑÐ¶Ð½Ñ Ð¿Ð¾Ð´Ð¾Ð±Ð½Ñе пÑокÑи.
ÐпÑоÑем, пÑиваÑнÑе ÑвойÑÑва имеÑÑ Ñвои недоÑÑаÑки. Ð ÑаÑÑноÑÑи, они не наÑледÑÑÑÑÑ.
«Рдиапазоне» Ñ Ð»Ð¾Ð²ÑÑкой «has»
ÐавайÑе поÑмоÑÑим еÑÑ Ð¿ÑимеÑÑ.
ÐÑедположим, Ñ Ð½Ð°Ñ ÐµÑÑÑ Ð¾Ð±ÑÐµÐºÑ range, опиÑÑваÑÑий диапазон:
let range = {
start: 1,
end: 10
};
ÐÑ Ð±Ñ Ñ
оÑели иÑполÑзоваÑÑ Ð¾Ð¿ÐµÑаÑÐ¾Ñ in, ÑÑÐ¾Ð±Ñ Ð¿ÑовеÑиÑÑ, ÑÑо некоÑоÑое ÑиÑло наÑ
одиÑÑÑ Ð² Ñказанном диапазоне.
ÐовÑÑка has пеÑеÑ
ваÑÑÐ²Ð°ÐµÑ Ð²ÑÐ·Ð¾Ð²Ñ in.
has(target, property)
targetâ ÑÑо оÑигиналÑнÑй обÑекÑ, коÑоÑÑй пеÑедавалÑÑ Ð¿ÐµÑвÑм аÑгÑменÑом в конÑÑÑÑкÑоÑnew Proxy,propertyâ Ð¸Ð¼Ñ ÑвойÑÑва
ÐÐ¾Ñ Ð´ÐµÐ¼Ð¾:
let range = {
start: 1,
end: 10
};
range = new Proxy(range, {
has(target, prop) {
return prop >= target.start && prop <= target.end
}
});
alert(5 in range); // true
alert(50 in range); // false
ÐÑлиÑно вÑглÑдиÑ, не пÑавда ли? РоÑÐµÐ½Ñ Ð¿ÑоÑÑо в ÑеализаÑии.
ÐбоÑаÑиваем ÑÑнкÑии: «apply»
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ обоÑаÑиваÑÑ Ð² пÑокÑи и ÑÑнкÑии.
ÐовÑÑка apply(target, thisArg, args) акÑивиÑÑеÑÑÑ Ð¿Ñи вÑзове пÑокÑи как ÑÑнкÑии:
targetâ ÑÑо оÑигиналÑнÑй обÑÐµÐºÑ (как Ð¼Ñ Ð¿Ð¾Ð¼Ð½Ð¸Ð¼, ÑÑнкÑÐ¸Ñ â ÑÑо обÑÐµÐºÑ Ð² ÑзÑке JavaScript),thisArgâ ÑÑо конÑекÑÑthis.argsâ ÑпиÑок аÑгÑменÑов.
ÐапÑимеÑ, давайÑе вÑпомним декоÑаÑÐ¾Ñ delay(f, ms), ÑозданнÑй нами в главе ÐекоÑаÑоÑÑ Ð¸ пеÑеадÑеÑаÑÐ¸Ñ Ð²Ñзова, call/apply.
Тогда Ð¼Ñ Ð¾Ð±Ð¾ÑлиÑÑ Ð±ÐµÐ· ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¿ÑокÑи. ÐÑзов delay(f, ms) возвÑаÑал ÑÑнкÑиÑ, коÑоÑÐ°Ñ Ð¿ÐµÑедавала вÑÐ·Ð¾Ð²Ñ f поÑле ms миллиÑекÑнд.
ÐÐ¾Ñ Ð¿ÑедÑдÑÑÐ°Ñ ÑеализаÑиÑ, на оÑнове ÑÑнкÑии:
function delay(f, ms) {
// возвÑаÑÐ°ÐµÑ Ð¾Ð±ÑÑÑкÑ, коÑоÑÐ°Ñ Ð²ÑзÑÐ²Ð°ÐµÑ ÑÑнкÑÐ¸Ñ f ÑеÑез ÑаймаÑÑ
return function() { // (*)
setTimeout(() => f.apply(this, arguments), ms);
};
}
function sayHi(user) {
alert(`ÐÑивеÑ, ${user}!`);
}
// поÑле обÑÑÑки вÑÐ·Ð¾Ð²Ñ sayHi бÑдÑÑ ÑÑабаÑÑваÑÑ Ñ Ð·Ð°Ð´ÐµÑжкой в 3 ÑекÑндÑ
sayHi = delay(sayHi, 3000);
sayHi("ÐаÑÑ"); // ÐÑивеÑ, ÐаÑÑ! (ÑеÑез 3 ÑекÑндÑ)
Ðак Ð¼Ñ Ñже видели, ÑÑо в Ñелом ÑабоÑаеÑ. ФÑнкÑиÑ-обÑÑÑка в ÑÑÑоке (*) вÑзÑÐ²Ð°ÐµÑ Ð½ÑжнÑÑ ÑÑнкÑÐ¸Ñ Ñ Ñказанной задеÑжкой.
Ðо наÑа ÑÑнкÑиÑ-обÑÑÑка не пеÑенапÑавлÑÐµÑ Ð¾Ð¿ÐµÑаÑии ÑÑениÑ/запиÑи ÑвойÑÑва и дÑÑгие. ÐоÑле обÑÑÑÑÐ²Ð°Ð½Ð¸Ñ Ð´Ð¾ÑÑÑп к ÑвойÑÑвам оÑигиналÑной ÑÑнкÑии, Ñаким как name, length, и дÑÑгим, бÑÐ´ÐµÑ Ð¿Ð¾ÑеÑÑн.
function delay(f, ms) {
return function() {
setTimeout(() => f.apply(this, arguments), ms);
};
}
function sayHi(user) {
alert(`ÐÑивеÑ, ${user}!`);
}
alert(sayHi.length); // 1 (в ÑÑнкÑии length - ÑÑо ÑиÑло аÑгÑменÑов в ÐµÑ Ð¾Ð±ÑÑвлении)
sayHi = delay(sayHi, 3000);
alert(sayHi.length); // 0 (в обÑÑвлении ÑÑнкÑии-обÑÑÑки Ð½Ð¾Ð»Ñ Ð°ÑгÑменÑов)
ÐÑокÑи кÑда более моÑнÑе в ÑÑом ÑмÑÑле, поÑколÑÐºÑ Ð¾Ð½Ð¸ пеÑенапÑавлÑÑÑ Ð²ÑÑ Ðº оÑигиналÑÐ½Ð¾Ð¼Ñ Ð¾Ð±ÑекÑÑ.
ÐавайÑе иÑполÑзÑем пÑокÑи вмеÑÑо ÑÑнкÑии-обÑÑÑки:
function delay(f, ms) {
return new Proxy(f, {
apply(target, thisArg, args) {
setTimeout(() => target.apply(thisArg, args), ms);
}
});
}
function sayHi(user) {
alert(`ÐÑивеÑ, ${user}!`);
}
sayHi = delay(sayHi, 3000);
alert(sayHi.length); // 1 (*) пÑокÑи пеÑенапÑавлÑÐµÑ ÑÑение ÑвойÑÑва length на иÑÑ
однÑÑ ÑÑнкÑиÑ
sayHi("ÐаÑÑ"); // ÐÑивеÑ, ÐаÑÑ! (ÑеÑез 3 ÑекÑндÑ)
РезÑлÑÑÐ°Ñ Ñакой же, но ÑейÑÐ°Ñ Ð½Ðµ ÑолÑко вÑзовÑ, но и дÑÑгие опеÑаÑии на пÑокÑи пеÑенапÑавлÑÑÑÑÑ Ðº оÑигиналÑной ÑÑнкÑии. Таким обÑазом, опеÑаÑÐ¸Ñ ÑÑÐµÐ½Ð¸Ñ ÑвойÑÑва sayHi.length возвÑаÑÐ°ÐµÑ ÐºÐ¾ÑÑекÑное знаÑение в ÑÑÑоке (*) поÑле пÑокÑиÑованиÑ.
ÐÑ Ð¿Ð¾Ð»ÑÑили лÑÑÑÑÑ Ð¾Ð±ÑÑÑкÑ.
СÑÑеÑÑвÑÑÑ Ð¸ дÑÑгие ловÑÑки: полнÑй ÑпиÑок еÑÑÑ Ð² наÑале ÑÑой главÑ. ÐÑполÑзоваÑÑ Ð¸Ñ Ð¼Ð¾Ð¶Ð½Ð¾ по аналогии Ñ Ð²ÑÑеопиÑаннÑми.
Reflect
Reflect â вÑÑÑоеннÑй обÑекÑ, ÑпÑоÑаÑÑий Ñоздание пÑокÑи.
Ранее Ð¼Ñ Ð³Ð¾Ð²Ð¾Ñили о Ñом, ÑÑо внÑÑÑенние меÑодÑ, Ñакие как [[Get]], [[Set]] и дÑÑгие, ÑÑÑеÑÑвÑÑÑ ÑолÑко в ÑпеÑиÑикаÑии, ÑÑо к ним нелÑÐ·Ñ Ð¾Ð±ÑаÑиÑÑÑÑ Ð½Ð°Ð¿ÑÑмÑÑ.
ÐбÑÐµÐºÑ Reflect Ð´ÐµÐ»Ð°ÐµÑ ÑÑо возможнÑм. Ðго меÑÐ¾Ð´Ñ â минималÑнÑе обÑÑÑки вокÑÑг внÑÑÑенниÑ
меÑодов.
ÐÐ¾Ñ Ð¿ÑимеÑÑ Ð¾Ð¿ÐµÑаÑий и вÑÐ·Ð¾Ð²Ñ Reflect, коÑоÑÑе делаÑÑ Ñо же Ñамое:
| ÐпеÑаÑÐ¸Ñ | ÐÑзов Reflect |
ÐнÑÑÑенний меÑод |
|---|---|---|
obj[prop] |
Reflect.get(obj, prop) |
[[Get]] |
obj[prop] = value |
Reflect.set(obj, prop, value) |
[[Set]] |
delete obj[prop] |
Reflect.deleteProperty(obj, prop) |
[[Delete]] |
new F(value) |
Reflect.construct(F, value) |
[[Construct]] |
| ⦠| ⦠| ⦠|
ÐапÑимеÑ:
let user = {};
Reflect.set(user, 'name', 'ÐаÑÑ');
alert(user.name); // ÐаÑÑ
Ð ÑаÑÑноÑÑи, Reflect позволÑÐµÑ Ð²ÑзваÑÑ Ð¾Ð¿ÐµÑаÑоÑÑ (new, deleteâ¦) как ÑÑнкÑии (Reflect.construct, Reflect.deleteProperty, â¦). ÐÑо инÑеÑеÑÐ½Ð°Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ, но здеÑÑ Ð½Ð°Ð¼ важно дÑÑгое.
ÐÐ»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ внÑÑÑеннего меÑода, пеÑеÑ
ваÑÑваемого Proxy, еÑÑÑ ÑооÑвеÑÑÑвÑÑÑий меÑод в Reflect, коÑоÑÑй Ð¸Ð¼ÐµÐµÑ Ñакое же Ð¸Ð¼Ñ Ð¸ Ñе же аÑгÑменÑÑ, ÑÑо и Ñ Ð»Ð¾Ð²ÑÑки Proxy.
ÐоÑÑÐ¾Ð¼Ñ Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ Reflect, ÑÑÐ¾Ð±Ñ Ð¿ÐµÑенапÑавиÑÑ Ð¾Ð¿ÐµÑаÑÐ¸Ñ Ð½Ð° иÑÑ
однÑй обÑекÑ.
Ð ÑÑом пÑимеÑе обе ловÑÑки get и set пÑозÑаÑно (как бÑдÑо иÑ
неÑ) пеÑенапÑавлÑÑÑ Ð¾Ð¿ÐµÑаÑии ÑÑÐµÐ½Ð¸Ñ Ð¸ запиÑи на обÑекÑ, пÑи ÑÑом вÑÐ²Ð¾Ð´Ñ ÑообÑение:
let user = {
name: "ÐаÑÑ",
};
user = new Proxy(user, {
get(target, prop, receiver) {
alert(`GET ${prop}`);
return Reflect.get(target, prop, receiver); // (1)
},
set(target, prop, val, receiver) {
alert(`SET ${prop}=${val}`);
return Reflect.set(target, prop, val, receiver); // (2)
}
});
let name = user.name; // вÑÐ²Ð¾Ð´Ð¸Ñ "GET name"
user.name = "ÐеÑÑ"; // вÑÐ²Ð¾Ð´Ð¸Ñ "SET name=ÐеÑÑ"
ÐдеÑÑ:
Reflect.getÑиÑÐ°ÐµÑ ÑвойÑÑво обÑекÑа.Reflect.setзапиÑÑÐ²Ð°ÐµÑ ÑвойÑÑво и возвÑаÑаеÑtrueпÑи ÑÑÐ¿ÐµÑ Ðµ, инаÑеfalse.
То еÑÑÑ, вÑÑ Ð¾ÑÐµÐ½Ñ Ð¿ÑоÑÑо â еÑли ловÑÑка Ñ
оÑÐµÑ Ð¿ÐµÑенапÑавиÑÑ Ð²Ñзов на обÑекÑ, Ñо доÑÑаÑоÑно вÑзваÑÑ Reflect.<меÑод> Ñ Ñеми же аÑгÑменÑами.
РболÑÑинÑÑве ÑлÑÑаев Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÑделаÑÑ Ð²ÑÑ Ñо же Ñамое и без Reflect, напÑимеÑ, ÑÑение ÑвойÑÑва Reflect.get(target, prop, receiver) можно замениÑÑ Ð½Ð° target[prop]. Ðо некоÑоÑÑе нÑанÑÑ Ð»ÐµÐ³ÐºÐ¾ ÑпÑÑÑиÑÑ.
ÐÑокÑи Ð´Ð»Ñ Ð³ÐµÑÑеÑа
РаÑÑмоÑÑим конкÑеÑнÑй пÑимеÑ, демонÑÑÑиÑÑÑÑий, Ñем лÑÑÑе Reflect.get, и заодно ÑазбеÑÑмÑÑ, заÑем в get/set нÑжен ÑÑеÑий аÑгÑÐ¼ÐµÐ½Ñ receiver, Ð¼Ñ ÐµÐ³Ð¾ Ñанее не иÑполÑзовали.
ÐопÑÑÑим, Ñ Ð½Ð°Ñ ÐµÑÑÑ Ð¾Ð±ÑÐµÐºÑ user Ñо ÑвойÑÑвом _name и геÑÑеÑом Ð´Ð»Ñ Ð½ÐµÐ³Ð¾.
Сделаем вокÑÑг user пÑокÑи:
let user = {
_name: "ÐоÑÑÑ",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) {
return target[prop];
}
});
alert(userProxy.name); // ÐоÑÑÑ
ÐовÑÑка get здеÑÑ Â«Ð¿ÑозÑаÑнаÑ», она возвÑаÑÐ°ÐµÑ ÑвойÑÑво иÑÑ
одного обÑекÑа и болÑÑе ниÑего не делаеÑ. ÐÐ»Ñ Ð½Ð°Ñего пÑимеÑа ÑÑого вполне доÑÑаÑоÑно.
ÐазалоÑÑ Ð±Ñ, вÑÑ Ð² поÑÑдке. Ðо давайÑе немного ÑÑложним пÑимеÑ.
ÐÑли Ð¼Ñ ÑнаÑледÑем Ð¾Ñ Ð¿ÑокÑиÑованного user обÑÐµÐºÑ admin, Ñо Ð¼Ñ Ñвидим, ÑÑо он ведÑÑ ÑÐµÐ±Ñ Ð½ÐµÐºÐ¾ÑÑекÑно:
let user = {
_name: "ÐоÑÑÑ",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) {
return target[prop]; // (*) target = user
}
});
let admin = {
__proto__: userProxy,
_name: "Ðдмин"
};
// ÐжидаеÑÑÑ: Ðдмин
alert(admin.name); // вÑводиÑÑÑ ÐоÑÑÑ (?!?)
ÐбÑаÑение к ÑвойÑÑÐ²Ñ admin.name должно возвÑаÑаÑÑ ÑÑÑÐ¾ÐºÑ "Ðдмин", а вÑÐ²Ð¾Ð´Ð¸Ñ "ÐоÑÑÑ"!
Ð ÑÑм дело? ÐÐ¾Ð¶ÐµÑ Ð±ÑÑÑ, Ð¼Ñ Ð´ÐµÐ»Ð°ÐµÐ¼ ÑÑо-Ñо не Ñак Ñ Ð½Ð°Ñледованием?
Ðо еÑли ÑбÑаÑÑ Ð¿ÑокÑи, Ñо вÑÑ Ð±ÑÐ´ÐµÑ ÑабоÑаÑÑ ÐºÐ°Ðº ожидаеÑÑÑ.
Ðа Ñамом деле, пÑоблема в пÑокÑи, в ÑÑÑоке (*).
-
ÐÑи ÑÑении
admin.name, Ñак как в обÑекÑеadminÐ½ÐµÑ ÑвойÑÑваname, оно иÑеÑÑÑ Ð² пÑоÑоÑипе. -
ÐÑоÑоÑипом ÑвлÑеÑÑÑ Ð¿ÑокÑи
userProxy. -
ÐÑи ÑÑении из пÑокÑи ÑвойÑÑва
nameÑÑабаÑÑÐ²Ð°ÐµÑ Ð»Ð¾Ð²ÑÑкаgetи возвÑаÑÐ°ÐµÑ ÐµÐ³Ð¾ из иÑÑ Ð¾Ð´Ð½Ð¾Ð³Ð¾ обÑекÑа какtarget[prop]в ÑÑÑоке(*).ÐÑзов
target[prop], еÑлиpropâ ÑÑо геÑÑеÑ, запÑÑÐºÐ°ÐµÑ ÐµÐ³Ð¾ код в конÑекÑÑеthis=target. ÐоÑÑÐ¾Ð¼Ñ ÑезÑлÑÑаÑом ÑвлÑеÑÑÑthis._nameиз иÑÑ Ð¾Ð´Ð½Ð¾Ð³Ð¾ обÑекÑаtarget, Ñо еÑÑÑ Ð¸Ð·user.
Ðменно Ð´Ð»Ñ Ð¸ÑпÑÐ°Ð²Ð»ÐµÐ½Ð¸Ñ ÑакиÑ
ÑиÑÑаÑий нÑжен receiver, ÑÑеÑий аÑгÑÐ¼ÐµÐ½Ñ Ð»Ð¾Ð²ÑÑки get. РнÑм Ñ
ÑаниÑÑÑ ÑÑÑлка на пÑавилÑнÑй конÑекÑÑ this, коÑоÑÑй нÑжно пеÑедаÑÑ Ð³ÐµÑÑеÑÑ. Рданном ÑлÑÑае ÑÑо admin.
Ðак пеÑедаÑÑ Ð³ÐµÑÑеÑÑ ÐºÐ¾Ð½ÑекÑÑ? ÐÐ»Ñ Ð¾Ð±ÑÑной ÑÑнкÑии Ð¼Ñ Ð¼Ð¾Ð³Ð»Ð¸ Ð±Ñ Ð¸ÑполÑзоваÑÑ call/apply, но ÑÑо же геÑÑеÑ, его не вÑзÑваÑÑ, пÑоÑÑо ÑиÑаÑÑ Ð·Ð½Ð°Ñение.
ÐÑо Ð¼Ð¾Ð¶ÐµÑ ÑделаÑÑ Reflect.get. ÐÑÑ Ð±ÑÐ´ÐµÑ ÑабоÑаÑÑ Ð²ÐµÑно, еÑли иÑполÑзоваÑÑ ÐµÐ³Ð¾.
ÐÐ¾Ñ Ð¸ÑпÑавленнÑй ваÑианÑ:
let user = {
_name: "ÐоÑÑÑ",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) { // receiver = admin
return Reflect.get(target, prop, receiver); // (*)
}
});
let admin = {
__proto__: userProxy,
_name: "Ðдмин"
};
alert(admin.name); // Ðдмин
СейÑÐ°Ñ receiver, ÑодеÑжаÑий ÑÑÑÐ»ÐºÑ Ð½Ð° коÑÑекÑнÑй this (Ñо еÑÑÑ Ð½Ð° admin), пеÑедаÑÑÑÑ Ð³ÐµÑÑеÑÑ Ð¿Ð¾ÑÑедÑÑвом Reflect.get в ÑÑÑоке (*).
Ðожно пеÑепиÑаÑÑ Ð»Ð¾Ð²ÑÑÐºÑ Ð¸ коÑоÑе:
get(target, prop, receiver) {
return Reflect.get(...arguments);
}
ÐеÑÐ¾Ð´Ñ Ð² Reflect имеÑÑ Ñе же названиÑ, ÑÑо и ÑооÑвеÑÑÑвÑÑÑие ловÑÑки, и пÑинимаÑÑ Ñакие же аÑгÑменÑÑ. ÐÑо бÑло ÑпеÑиалÑно задÑмано пÑи ÑазÑабоÑке ÑпеÑиÑикаÑии JavaScript.
Так ÑÑо return Reflect... даÑÑ Ð¿ÑоÑÑÑÑ Ð¸ безопаÑнÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð¿ÐµÑенапÑавиÑÑ Ð¾Ð¿ÐµÑаÑÐ¸Ñ Ð½Ð° оÑигиналÑнÑй обÑÐµÐºÑ Ð¸ пÑи ÑÑом пÑедоÑ
ÑанÑÐµÑ Ð½Ð°Ñ Ð¾Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½ÑÑ
оÑибок, ÑвÑзаннÑÑ
Ñ ÑÑим дейÑÑвием.
ÐгÑаниÑÐµÐ½Ð¸Ñ Ð¿ÑокÑи
ÐÑокÑи â ÑникалÑное ÑÑедÑÑво Ð´Ð»Ñ Ð½Ð°ÑÑÑойки Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ð±ÑекÑов на Ñамом низком ÑÑовне. Ðо они не идеалÑнÑ, еÑÑÑ Ð½ÐµÐºÐ¾ÑоÑÑе огÑаниÑениÑ.
ÐÑÑÑоеннÑе обÑекÑÑ: внÑÑÑенние ÑлоÑÑ
Ðногие вÑÑÑоеннÑе обÑекÑÑ, напÑÐ¸Ð¼ÐµÑ Map, Set, Date, Promise и дÑÑгие иÑполÑзÑÑÑ Ñак назÑваемÑе «внÑÑÑенние ÑлоÑÑ».
ÐÑо как ÑвойÑÑва, но ÑолÑко Ð´Ð»Ñ Ð²Ð½ÑÑÑеннего иÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² Ñамой ÑпеÑиÑикаÑиии. ÐапÑимеÑ, Map Ñ
ÑÐ°Ð½Ð¸Ñ ÑлеменÑÑ Ð²Ð¾ внÑÑÑеннем ÑлоÑе [[MapData]]. ÐÑÑÑоеннÑе меÑÐ¾Ð´Ñ Ð¾Ð±ÑаÑаÑÑÑÑ Ðº ÑлоÑам напÑÑмÑÑ, не ÑеÑез [[Get]]/[[Set]]. Таким обÑазом, пÑокÑи не Ð¼Ð¾Ð¶ÐµÑ Ð¿ÐµÑеÑ
ваÑиÑÑ Ð¸Ñ
.
ÐоÑÐµÐ¼Ñ ÑÑо Ð¸Ð¼ÐµÐµÑ Ð·Ð½Ð°Ñение? Ðни же вÑÑ Ñавно внÑÑÑенние!
ÐÑÑÑ Ð¾Ð´Ð¸Ð½ нÑанÑ. ÐÑли вÑÑÑоеннÑй обÑÐµÐºÑ Ð¿ÑокÑиÑÑеÑÑÑ, Ñо в пÑокÑи не бÑÐ´ÐµÑ ÑÑÐ¸Ñ Â«Ð²Ð½ÑÑÑÐµÐ½Ð½Ð¸Ñ ÑлоÑов», Ñак ÑÑо попÑÑка вÑзваÑÑ Ð½Ð° Ñаком пÑокÑи вÑÑÑоеннÑй меÑод пÑиведÑÑ Ðº оÑибке.
ÐÑимеÑ:
let map = new Map();
let proxy = new Proxy(map, {});
proxy.set('test', 1); // бÑÐ´ÐµÑ Ð¾Ñибка
ÐнÑÑÑи ÑÐµÐ±Ñ Ð¾Ð±ÑÐµÐºÑ Ñипа Map Ñ
ÑÐ°Ð½Ð¸Ñ Ð²Ñе даннÑе во внÑÑÑеннем ÑлоÑе [[MapData]]. ÐÑокÑи не Ð¸Ð¼ÐµÐµÑ Ñакого ÑлоÑа. ÐÑÑÑоеннÑй меÑод Map.prototype.set пÑÑаеÑÑÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп к ÑÐ²Ð¾ÐµÐ¼Ñ Ð²Ð½ÑÑÑÐµÐ½Ð½ÐµÐ¼Ñ ÑвойÑÑÐ²Ñ this.[[MapData]], но Ñак как this=proxy, Ñо не Ð¼Ð¾Ð¶ÐµÑ ÐµÐ³Ð¾ найÑи и завеÑÑаеÑÑÑ Ñ Ð¾Ñибкой.
Ð ÑÑаÑÑÑÑ, еÑÑÑ ÑпоÑоб иÑпÑавиÑÑ ÑÑо:
let map = new Map();
let proxy = new Proxy(map, {
get(target, prop, receiver) {
let value = Reflect.get(...arguments);
return typeof value == 'function' ? value.bind(target) : value;
}
});
proxy.set('test', 1);
alert(proxy.get('test')); // 1 (ÑабоÑаеÑ!)
СейÑÐ°Ñ Ð²ÑÑ ÑÑабоÑало, поÑÐ¾Ð¼Ñ ÑÑо get пÑивÑзÑÐ²Ð°ÐµÑ ÑвойÑÑва-ÑÑнкÑии, Ñакие как map.set, к оÑигиналÑÐ½Ð¾Ð¼Ñ Ð¾Ð±ÑекÑÑ map. Таким обÑазом, когда ÑеализаÑÐ¸Ñ Ð¼ÐµÑода set попÑÑаеÑÑÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп к внÑÑÑÐµÐ½Ð½ÐµÐ¼Ñ ÑлоÑÑ this.[[MapData]], Ñо вÑÑ Ð¿ÑойдÑÑ Ð±Ð»Ð°Ð³Ð¾Ð¿Ð¾Ð»ÑÑно.
Array не иÑполÑзÑÐµÑ Ð²Ð½ÑÑÑенние ÑлоÑÑÐажнÑм иÑклÑÑением ÑвлÑеÑÑÑ Ð²ÑÑÑоеннÑй обÑÐµÐºÑ Array: он не иÑполÑзÑÐµÑ Ð²Ð½ÑÑÑенние ÑлоÑÑ. Так ÑложилоÑÑ Ð¸ÑÑоÑиÑеÑки, Ð²ÐµÐ´Ñ Ð¼Ð°ÑÑÐ¸Ð²Ñ Ð±Ñли Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ñ Ð² ÑзÑк оÑÐµÐ½Ñ Ð´Ð°Ð²Ð½Ð¾.
То еÑÑÑ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð½Ð°Ñ Ð²ÑÑе пÑоблема не Ð²Ð¾Ð·Ð½Ð¸ÐºÐ°ÐµÑ Ð¿Ñи пÑокÑиÑовании маÑÑивов.
ÐÑиваÑнÑе полÑ
ÐеÑÑо Ð¿Ð¾Ñ Ð¾Ð¶ÐµÐµ пÑоиÑÑ Ð¾Ð´Ð¸Ñ Ð¸ Ñ Ð¿ÑиваÑнÑми полÑми клаÑÑов.
ÐапÑимеÑ, меÑод getName() оÑÑÑеÑÑвлÑÐµÑ Ð´Ð¾ÑÑÑп к пÑиваÑÐ½Ð¾Ð¼Ñ Ð¿Ð¾Ð»Ñ #name, поÑле пÑокÑиÑÐ¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð½ пеÑеÑÑаÑÑ ÑабоÑаÑÑ:
class User {
#name = "ÐоÑÑÑ";
getName() {
return this.#name;
}
}
let user = new User();
user = new Proxy(user, {});
alert(user.getName()); // ÐÑибка
ÐÑиÑина вÑÑ Ñа же: пÑиваÑнÑе Ð¿Ð¾Ð»Ñ ÑÐµÐ°Ð»Ð¸Ð·Ð¾Ð²Ð°Ð½Ñ Ñ Ð¸ÑполÑзованием внÑÑÑенниÑ
ÑлоÑов. JavaScript не иÑполÑзÑÐµÑ [[Get]]/[[Set]] пÑи доÑÑÑпе к ним.
РвÑзове getName() знаÑением this ÑвлÑеÑÑÑ Ð¿ÑокÑиÑованнÑй user, в коÑоÑом Ð½ÐµÑ Ð²Ð½ÑÑÑеннего ÑлоÑа Ñ Ð¿ÑиваÑнÑми полÑми.
РеÑением, как и в пÑедÑдÑÑем ÑлÑÑае, ÑвлÑеÑÑÑ Ð¿ÑивÑзка конÑекÑÑа к меÑодÑ:
class User {
#name = "ÐоÑÑÑ";
getName() {
return this.#name;
}
}
let user = new User();
user = new Proxy(user, {
get(target, prop, receiver) {
let value = Reflect.get(...arguments);
return typeof value == 'function' ? value.bind(target) : value;
}
});
alert(user.getName()); // ÐоÑÑÑ
Ðднако, Ñакое ÑеÑение Ð¸Ð¼ÐµÐµÑ ÑÑд недоÑÑаÑков, о коÑоÑÑÑ Ñже говоÑилоÑÑ: меÑÐ¾Ð´Ñ Ð¿ÐµÑедаÑÑÑÑ Ð¾ÑигиналÑнÑй обÑекÑ, коÑоÑÑй Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿ÐµÑедан кÑда-Ñо еÑÑ, и ÑÑо Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð»Ð¾Ð¼Ð°ÑÑ Ð²ÑÑ ÑÑнкÑионалÑноÑÑÑ Ð¿ÑокÑиÑованиÑ.
ÐÑокÑи != оÑигиналÑнÑй обÑекÑ
ÐÑокÑи и обÑекÑ, коÑоÑÑй пÑокÑиÑÑеÑÑÑ, ÑвлÑÑÑÑÑ Ð´Ð²ÑÐ¼Ñ ÑазнÑми обÑекÑами. ÐÑо еÑÑеÑÑвенно, не пÑавда ли?
ÐÑли Ð¼Ñ Ð¸ÑполÑзÑем оÑигиналÑнÑй обÑÐµÐºÑ ÐºÐ°Ðº клÑÑ, а заÑем пÑокÑиÑÑем его, Ñо пÑокÑи не бÑÐ´ÐµÑ Ð½Ð°Ð¹Ð´ÐµÐ½:
let allUsers = new Set();
class User {
constructor(name) {
this.name = name;
allUsers.add(this);
}
}
let user = new User("ÐаÑÑ");
alert(allUsers.has(user)); // true
user = new Proxy(user, {});
alert(allUsers.has(user)); // false
Ðак Ð¼Ñ Ð²Ð¸Ð´Ð¸Ð¼, поÑле пÑокÑиÑÐ¾Ð²Ð°Ð½Ð¸Ñ Ð½Ðµ полÑÑаеÑÑÑ Ð½Ð°Ð¹Ñи обÑÐµÐºÑ user внÑÑÑи множеÑÑва allUsers, поÑÐ¾Ð¼Ñ ÑÑо пÑокÑи â ÑÑо дÑÑгой обÑекÑ.
===ÐÑокÑи ÑпоÑÐ¾Ð±Ð½Ñ Ð¿ÐµÑеÑ
ваÑÑваÑÑ Ð¼Ð½Ð¾Ð³Ð¾ опеÑаÑоÑов, напÑÐ¸Ð¼ÐµÑ new (ловÑÑка construct), in (ловÑÑка has), delete (ловÑÑка deleteProperty) и Ñак далее.
Ðо Ð½ÐµÑ ÑпоÑоба пеÑÐµÑ Ð²Ð°ÑиÑÑ Ð¿ÑовеÑÐºÑ Ð½Ð° ÑÑÑогое ÑавенÑÑво. ÐбÑÐµÐºÑ ÑÑÑого Ñавен ÑолÑко ÑÐ°Ð¼Ð¾Ð¼Ñ Ñебе, и никаким дÑÑгим знаÑениÑм.
Так ÑÑо вÑе опеÑаÑии и вÑÑÑоеннÑе клаÑÑÑ, коÑоÑÑе иÑполÑзÑÑÑ ÑÑÑогÑÑ Ð¿ÑовеÑÐºÑ Ð¾Ð±ÑекÑов на ÑавенÑÑво, оÑлиÑÐ°Ñ Ð¿ÑокÑи Ð¾Ñ Ð¸Ð·Ð½Ð°ÑалÑного обÑекÑа. ÐÑозÑаÑной Ð·Ð°Ð¼ÐµÐ½Ñ Ð² данном ÑлÑÑае не пÑоизойдÑÑ.
ÐÑклÑÑаемÑе пÑокÑи
ÐÑклÑÑаемÑй (revocable) пÑокÑи â ÑÑо пÑокÑи, коÑоÑÑй Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¾ÑклÑÑÑн вÑзовом ÑпеÑиалÑной ÑÑнкÑии.
ÐопÑÑÑим, Ñ Ð½Ð°Ñ ÐµÑÑÑ ÐºÐ°ÐºÐ¾Ð¹-Ñо ÑеÑÑÑÑ, и Ð¼Ñ Ð±Ñ Ñ Ð¾Ñели имеÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð·Ð°ÐºÑÑÑÑ Ðº Ð½ÐµÐ¼Ñ Ð´Ð¾ÑÑÑп в лÑбой моменÑ.
ÐÐ»Ñ Ñого, ÑÑÐ¾Ð±Ñ ÑеÑиÑÑ Ð¿Ð¾ÑÑавленнÑÑ Ð·Ð°Ð´Ð°ÑÑ, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ Ð¾ÑклÑÑаемÑй пÑокÑи, без ловÑÑек. Такой пÑокÑи бÑÐ´ÐµÑ Ð¿ÐµÑедаваÑÑ Ð²Ñе опеÑаÑии на пÑокÑиÑÑемÑй обÑекÑ, и Ñ Ð½Ð°Ñ Ð±ÑÐ´ÐµÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð² лÑбой Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð¾ÑклÑÑиÑÑ ÑÑо.
СинÑакÑиÑ:
let {proxy, revoke} = Proxy.revocable(target, handler)
ÐÑзов возвÑаÑÐ°ÐµÑ Ð¾Ð±ÑÐµÐºÑ Ñ proxy и ÑÑнкÑией revoke, коÑоÑÐ°Ñ Ð¾ÑклÑÑÐ°ÐµÑ ÐµÐ³Ð¾.
ÐÐ¾Ñ Ð¿ÑимеÑ:
let object = {
data: "ÐажнÑе даннÑе"
};
let {proxy, revoke} = Proxy.revocable(object, {});
// пеÑедаÑм пÑокÑи кÑда-нибÑÐ´Ñ Ð²Ð¼ÐµÑÑо оÑигиналÑного обÑекÑа...
alert(proxy.data); // ÐажнÑе даннÑе
// позже в коде
revoke();
// пÑокÑи болÑÑе не ÑабоÑÐ°ÐµÑ (оÑклÑÑÑн)
alert(proxy.data); // ÐÑибка
ÐÑзов revoke() ÑдалÑÐµÑ Ð²Ñе внÑÑÑенние ÑÑÑлки на оÑигиналÑнÑй обÑÐµÐºÑ Ð¸Ð· пÑокÑи, Ñак ÑÑо Ð¼ÐµÐ¶Ð´Ñ Ð½Ð¸Ð¼Ð¸ болÑÑе Ð½ÐµÑ ÑвÑзи, и оÑигиналÑнÑй обÑÐµÐºÑ ÑепеÑÑ Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¾ÑиÑен ÑбоÑÑиком мÑÑоÑа.
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ Ñ
ÑаниÑÑ ÑÑнкÑÐ¸Ñ revoke в WeakMap, ÑÑÐ¾Ð±Ñ Ð»ÐµÐ³ÐºÐ¾ найÑи ÐµÑ Ð¿Ð¾ обÑекÑÑ Ð¿ÑокÑи:
let revokes = new WeakMap();
let object = {
data: "ÐажнÑе даннÑе"
};
let {proxy, revoke} = Proxy.revocable(object, {});
revokes.set(proxy, revoke);
// ..позже в коде..
revoke = revokes.get(proxy);
revoke();
alert(proxy.data); // ÐÑибка (пÑокÑи оÑклÑÑÑн)
ÐÑеимÑÑеÑÑво Ñакого подÑ
ода в Ñом, ÑÑо Ð¼Ñ Ð½Ðµ Ð´Ð¾Ð»Ð¶Ð½Ñ ÑаÑкаÑÑ ÑÑнкÑÐ¸Ñ revoke повÑÑдÑ. ÐÑ Ð¿Ð¾Ð»ÑÑаем ÐµÑ Ð¿Ñи необÑ
одимоÑÑи из revokes по обÑекÑÑ Ð¿ÑокÑи.
ÐÑ Ð¸ÑполÑзовали WeakMap вмеÑÑо Map, ÑÑÐ¾Ð±Ñ Ð½Ðµ блокиÑоваÑÑ ÑбоÑÐºÑ Ð¼ÑÑоÑа. ÐÑли пÑокÑи обÑÐµÐºÑ ÑÑановиÑÑÑ Ð½ÐµÐ´Ð¾ÑÑижимÑм (Ñо еÑÑÑ Ð½Ð° него болÑÑе Ð½ÐµÑ ÑÑÑлок), Ñо WeakMap позволÑÐµÑ ÑбоÑÑÐ¸ÐºÑ Ð¼ÑÑоÑа ÑдалиÑÑ ÐµÐ³Ð¾ из памÑÑи вмеÑÑе Ñ ÑооÑвеÑÑÑвÑÑÑей ÑÑнкÑией revoke, коÑоÑÐ°Ñ Ð² ÑÑом ÑлÑÑае болÑÑе не нÑжна.
СÑÑлки
ÐÑого
ÐÑокÑи â ÑÑо обÑÑÑка вокÑÑг обÑекÑа, коÑоÑÐ°Ñ Â«Ð¿Ð¾ ÑмолÑаниÑ» пеÑенапÑавлÑÐµÑ Ð¾Ð¿ÐµÑаÑии над ней на обÑекÑ, но Ð¸Ð¼ÐµÐµÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð¿ÐµÑÐµÑ Ð²Ð°ÑÑваÑÑ Ð¸Ñ .
ÐÑокÑиÑоваÑÑ Ð¼Ð¾Ð¶Ð½Ð¾ лÑбой обÑекÑ, вклÑÑÐ°Ñ ÐºÐ»Ð°ÑÑÑ Ð¸ ÑÑнкÑии.
СинÑакÑиÑ:
let proxy = new Proxy(target, {
/* ловÑÑки */
});
â¦ÐаÑем обÑÑно иÑполÑзÑÑÑ Ð¿ÑокÑи везде вмеÑÑо оÑигиналÑного обÑекÑа target. ÐÑокÑи не Ð¸Ð¼ÐµÐµÑ ÑобÑÑвеннÑÑ
ÑвойÑÑв или меÑодов. Ðн пÑоÑÑо пеÑеÑ
ваÑÑÐ²Ð°ÐµÑ Ð¾Ð¿ÐµÑаÑиÑ, еÑли имееÑÑÑ ÑооÑвеÑÑÑвÑÑÑÐ°Ñ Ð»Ð¾Ð²ÑÑка, а инаÑе пеÑенапÑавлÑÐµÑ ÐµÑ ÑÑÐ°Ð·Ñ Ð½Ð° обÑÐµÐºÑ target.
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ пеÑÐµÑ Ð²Ð°ÑÑваÑÑ:
- ЧÑение (
get), запиÑÑ (set), Ñдаление (deleteProperty) ÑвойÑÑва (даже неÑÑÑеÑÑвÑÑÑего). - ÐÑзов ÑÑнкÑии (
apply). - ÐпеÑаÑоÑ
new(ловÑÑкаconstruct). - Рмногие дÑÑгие опеÑаÑии (полнÑй ÑпиÑок пÑиведÑн в наÑале ÑÑаÑÑи, а Ñакже в докÑменÑаÑии).
ÐÑо позволÑÐµÑ Ð½Ð°Ð¼ ÑоздаваÑÑ Â«Ð²Ð¸ÑÑÑалÑнÑе» ÑвойÑÑва и меÑодÑ, ÑеализовÑваÑÑ Ð·Ð½Ð°ÑÐµÐ½Ð¸Ñ Ð¿Ð¾ ÑмолÑаниÑ, наблÑдаемÑе обÑекÑÑ, ÑÑнкÑии-декоÑаÑоÑÑ Ð¸ многое дÑÑгое.
ÐÑ Ñакже можем обоÑаÑиваÑÑ Ð¾Ð´Ð¸Ð½ и ÑÐ¾Ñ Ð¶Ðµ обÑÐµÐºÑ Ð¼Ð½Ð¾Ð³Ð¾ Ñаз в ÑазнÑе пÑокÑи, добавлÑÑ ÐµÐ¼Ñ ÑазлиÑнÑе аÑпекÑÑ ÑÑнкÑионалÑноÑÑи.
Reflect API Ñоздано как дополнение к Proxy. ÐÐ»Ñ Ð»Ñбой ловÑÑки из Proxy ÑÑÑеÑÑвÑÐµÑ Ð¼ÐµÑод в Reflect Ñ Ñеми же аÑгÑменÑами. Ðам ÑледÑÐµÑ Ð¸ÑполÑзоваÑÑ ÐµÐ³Ð¾, еÑли нÑжно пеÑенапÑавиÑÑ Ð²Ñзов на оÑигиналÑнÑй обÑекÑ.
ÐÑокÑи имеÑÑ Ð½ÐµÐºÐ¾ÑоÑÑе огÑаниÑениÑ:
- ÐÑÑÑоеннÑе обÑекÑÑ Ð¸ÑполÑзÑÑÑ Ñак назÑваемÑе «внÑÑÑенние ÑлоÑÑ», доÑÑÑп к коÑоÑÑм нелÑÐ·Ñ Ð¿ÑокÑиÑоваÑÑ. Ðднако, Ñанее в ÑÑой главе бÑл показан один ÑпоÑоб, как обойÑи ÑÑо огÑаниÑение.
- То же Ñамое можно ÑказаÑÑ Ð¸ о пÑиваÑнÑÑ
полÑÑ
клаÑÑов, Ñак как они ÑÐµÐ°Ð»Ð¸Ð·Ð¾Ð²Ð°Ð½Ñ Ð½Ð° оÑнове ÑлоÑов. То еÑÑÑ Ð²ÑÐ·Ð¾Ð²Ñ Ð¿ÑокÑиÑованнÑÑ
меÑодов Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¸Ð¼ÐµÑÑ Ð¾ÑигиналÑнÑй обÑÐµÐºÑ Ð² каÑеÑÑве
this, ÑÑÐ¾Ð±Ñ Ð¿Ð¾Ð»ÑÑиÑÑ Ðº ним доÑÑÑп. - ÐÑовеÑка обÑекÑов на ÑÑÑогое ÑавенÑÑво
===не Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿ÐµÑÐµÑ Ð²Ð°Ñена. - ÐÑоизводиÑелÑноÑÑÑ: конкÑеÑнÑе показаÑели завиÑÑÑ Ð¾Ñ Ð¸Ð½ÑеÑпÑеÑаÑоÑа, но в Ñелом полÑÑение ÑвойÑÑва Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ Ð¿ÑоÑÑейÑего пÑокÑи Ð·Ð°Ð½Ð¸Ð¼Ð°ÐµÑ Ð² неÑколÑко Ñаз болÑÑе вÑемени. Ð ÑеалÑноÑÑи ÑÑо Ð¸Ð¼ÐµÐµÑ Ð·Ð½Ð°Ñение ÑолÑко Ð´Ð»Ñ Ð½ÐµÐºÐ¾ÑоÑÑÑ Â«Ð¾Ñобо нагÑÑженнÑÑ Â» обÑекÑов.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)