ÐбâÑÐºÑ Proxy обгоÑÑÐ°Ñ ÑнÑий обâÑÐºÑ Ñ Ð¿ÐµÑеÑ
оплÑÑ Ð¾Ð¿ÐµÑаÑÑÑ, ÑÐ°ÐºÑ Ñк ÑиÑаннÑ/Ð·Ð°Ð¿Ð¸Ñ Ð²Ð»Ð°ÑÑивоÑÑей Ñа ÑнÑÑ, за бажаннÑм обÑоблÑÑÑи ÑÑ
ÑамоÑÑÑйно, або пÑозоÑо дозволÑÑÑи обâÑкÑÑ Ð¾Ð±ÑоблÑÑи ÑÑ
.
ÐÑокÑÑ Ð²Ð¸ÐºÐ¾ÑиÑÑовÑÑÑÑÑÑ Ð² багаÑÑÐ¾Ñ Ð±ÑблÑоÑÐµÐºÐ°Ñ Ñ Ð´ÐµÑÐºÐ¸Ñ ÑÑеймвоÑÐºÐ°Ñ Ð±ÑаÑзеÑа. У ÑÑÐ¾Ð¼Ñ ÑоздÑÐ»Ñ Ð¼Ð¸ побаÑимо багаÑо випадкÑв виÑÑÑÐµÐ½Ð½Ñ ÑеалÑÐ½Ð¸Ñ Ð·Ð°Ð´Ð°Ñ.
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 нового пÑокÑÑ, Ñоб пеÑеÑ
опиÑи опеÑаÑÑÑ:
| ÐнÑÑÑÑÑнÑй ÐеÑод | ÐеÑод ÐаÑÑки | ÐикликаÑÑÑÑÑ, коли⦠|
|---|---|---|
[[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Ñ Ð¹Ð¾Ð³Ð¾ викликÑ. ÐазвиÑай Ñе Ñам обâÑкÑproxy(або обâÑкÑ, Ñкий ÑÑпадковÑÑÑÑÑÑ Ð²Ñд нÑого, ÑкÑо ми ÑÑпадковÑÑмо вÑд пÑокÑÑ). ÐаÑÐ°Ð·Ñ Ñей аÑгÑÐ¼ÐµÐ½Ñ Ð½Ð°Ð¼ не поÑÑÑбен, ÑÐ¾Ð¼Ñ Ð´ÐµÑалÑнÑÑе ÑозâÑÑнимо його пÑзнÑÑе.
ÐикоÑиÑÑаймо 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 (Ð½ÐµÐ¼Ð°Ñ Ð¿ÐµÑекладÑ)
ÐвеÑнÑÑÑ ÑвагÑ, Ñк пÑокÑÑ Ð¿ÐµÑезапиÑÑÑ Ð·Ð¼ÑннÑ:
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("test"); // 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 над user, а Ñакож 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) { // викликаÑÑÑÑÑ Ð¾Ð´Ð¸Ð½ Ñаз, Ñоб оÑÑимаÑи ÑпиÑок влаÑÑивоÑÑей
return ['a', 'b', 'c'];
},
getOwnPropertyDescriptor(target, prop) { // викликаÑÑÑÑÑ Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ знаÑеннÑ
return {
enumerable: true,
configurable: true
/* ...ÑнÑÑ Ð¿ÑапоÑи, ймовÑÑне "знаÑеннÑ:..." */
};
}
});
alert( Object.keys(user) ); // a, b, c
ÐаÑважимо Ñе Ñаз: нам поÑÑÑбно пеÑеÑ
оплÑваÑи [[GetOwnProperty]] лиÑе ÑодÑ, коли влаÑÑивÑÑÑÑ Ð²ÑдÑÑÑÐ½Ñ Ð² обâÑкÑÑ.
ÐÐ°Ñ Ð¸ÑÐµÐ½Ñ Ð²Ð»Ð°ÑÑивоÑÑÑ Ð· âdeletePropertyâ Ñа ÑнÑими паÑÑками
ÐÑнÑÑ Ð¿Ð¾ÑиÑена домовленÑÑÑÑ, Ñо влаÑÑивоÑÑÑ Ñа меÑоди з пÑеÑÑкÑом пÑдкÑеÑÐ»ÐµÐ½Ð½Ñ _ Ñ Ð²Ð½ÑÑÑÑÑнÑми. Ðо ниÑ
не ÑлÑд звеÑÑаÑиÑÑ Ð·Ð·Ð¾Ð²Ð½Ñ Ð¾Ð±âÑкÑа.
Ðле ÑÐµÑ Ð½ÑÑно Ñе можливо:
let user = {
name: "Ðван",
_password: "secret"
};
alert(user._password); // secret
ÐикоÑиÑÑаймо пÑокÑÑ, Ñоб запобÑгÑи бÑдÑ-ÑÐºÐ¾Ð¼Ñ Ð´Ð¾ÑÑÑÐ¿Ñ Ð´Ð¾ влаÑÑивоÑÑей, ÑÐºÑ Ð¿Ð¾ÑинаÑÑÑÑÑ Ð· _.
Ðам знадоблÑÑÑÑÑ Ð¿Ð°ÑÑки:
get, Ñоб пÑокидаÑи Ð¿Ð¾Ð¼Ð¸Ð»ÐºÑ Ð¿Ñд ÑÐ°Ñ ÑиÑÐ°Ð½Ð½Ñ ÑÐ°ÐºÐ¾Ñ Ð²Ð»Ð°ÑÑивоÑÑÑ,set, Ñоб пÑокидаÑи Ð¿Ð¾Ð¼Ð¸Ð»ÐºÑ Ð¿Ñд ÑÐ°Ñ Ð·Ð°Ð¿Ð¸ÑÑ,deleteProperty, Ñоб пÑокидаÑи Ð¿Ð¾Ð¼Ð¸Ð»ÐºÑ Ð¿Ñд ÑÐ°Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ,ownKeysÐ´Ð»Ñ Ð²Ð¸ÐºÐ»ÑÑÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑÑивоÑÑей, Ñо поÑинаÑÑÑÑÑ Ð·_, Ñзfor..inÑа меÑодÑв, ÑÐ°ÐºÐ¸Ñ ÑкObject.keys.
ÐÑÑ ÐºÐ¾Ð´:
let user = {
name: "John",
_password: "***"
};
user = new Proxy(user, {
get(target, prop) {
if (prop.startsWith('_')) {
throw new Error("ÐоÑÑÑп забоÑонено");
}
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â
ÐодивÑмоÑÑ Ð±ÑлÑÑе пÑикладÑв.
У Ð½Ð°Ñ Ñ Ð¾Ð±âÑÐºÑ Ð´ÑапазонÑ:
let range = {
start: 1,
end: 10
};
Ðи Ñ
оÑÑли б викоÑиÑÑовÑваÑи опеÑаÑÐ¾Ñ in, Ñоб пеÑевÑÑиÑи, Ñи знаÑ
одиÑÑÑÑ ÑиÑло в range.
ÐаÑÑка 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 (в оголоÑÐµÐ½Ð½Ñ Ð¾Ð±Ð³Ð¾ÑÑки нÑÐ»Ñ Ð°ÑгÑменÑÑв)
Proxy набагаÑо поÑÑжнÑÑÑ, оÑкÑлÑки вони пеÑенапÑавлÑÑÑÑ Ð²Ñе до ÑÑлÑового обâÑкÑа.
ÐикоÑиÑÑаймо Proxy замÑÑÑÑ ÑÑнкÑÑÑ-обгоÑÑки:
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 (*) пÑокÑÑ Ð¿ÐµÑенапÑавлÑÑ Ð¾Ð¿ÐµÑаÑÑÑ "get length" до ÑÑлÑ
sayHi("John"); // ÐÑивÑÑ, Ðван! (ÑеÑез 3 ÑекÑнди)
РезÑлÑÑÐ°Ñ Ñой Ñамий, але ÑÐµÐ¿ÐµÑ Ð½Ðµ ÑÑлÑки виклики, а й ÑÑÑ Ð¾Ð¿ÐµÑаÑÑÑ Ð½Ð° пÑокÑÑ Ð¿ÐµÑеÑилаÑÑÑÑÑ Ð´Ð¾ оÑигÑналÑÐ½Ð¾Ñ ÑÑнкÑÑÑ. ÐÑже, sayHi.length повеÑÑÐ°Ñ Ð¿ÑавилÑне знаÑÐµÐ½Ð½Ñ Ð¿ÑÑÐ»Ñ Ð¾Ð±Ð³Ð¾ÑÑÐ°Ð½Ð½Ñ Ð² ÑÑÐ´ÐºÑ (*).
У Ð½Ð°Ñ Ñ âбагаÑÑаâ обгоÑÑка.
ÐÑнÑÑÑÑ Ð¹ ÑнÑÑ Ð¿Ð°ÑÑки: повний ÑпиÑок на поÑаÑÐºÑ ÑÑÑÑ ÑÑаÑÑÑ. Ð¡Ñ ÐµÐ¼Ð° ÑÑ Ð²Ð¸ÐºÐ¾ÑиÑÑÐ°Ð½Ð½Ñ ÑÑ Ð¾Ð¶Ð° на опиÑÐ°Ð½Ñ Ð²Ð¸Ñе.
Reflect
Reflect â Ñе вбÑдований обâÑкÑ, Ñкий ÑпÑоÑÑÑ ÑÑвоÑÐµÐ½Ð½Ñ Proxy.
РанÑÑе бÑло Ñказано, Ñо внÑÑÑÑÑÐ½Ñ Ð¼ÐµÑоди, ÑÐ°ÐºÑ Ñк [[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.<method> з Ñими ж аÑгÑменÑами.
У бÑлÑÑоÑÑÑ Ð²Ð¸Ð¿Ð°Ð´ÐºÑв ми можемо зÑобиÑи Ñе ж Ñаме без Reflect, напÑиклад, зÑиÑÑÐ²Ð°Ð½Ð½Ñ Ð²Ð»Ð°ÑÑивоÑÑÑ Reflect.get(target, prop, receiver) можна замÑниÑи на target[prop]. Ðле Ñ Ð²Ð°Ð¶Ð»Ð¸Ð²Ñ Ð½ÑанÑи.
ÐÑокÑÑ Ð´Ð»Ñ Ð³ÐµÑÑеÑа
ÐодивÑмоÑÑ Ð½Ð° пÑиклад, Ñкий демонÑÑÑÑÑ, ÑÐ¾Ð¼Ñ Reflect.get кÑаÑе. Рми Ñакож побаÑимо, ÑÐ¾Ð¼Ñ get/set Ð¼Ð°Ñ ÑÑеÑÑй аÑгÑÐ¼ÐµÐ½Ñ receiver, Ñкий ми ÑанÑÑе не викоÑиÑÑовÑвали.
У Ð½Ð°Ñ Ñ Ð¾Ð±âÑÐºÑ user з влаÑÑивÑÑÑÑ _name Ñ Ð³ÐµÑÑÐµÑ Ð´Ð»Ñ Ð½Ñого.
ÐÑÑ Ð¿ÑокÑÑ Ð½Ð°Ð²ÐºÐ¾Ð»Ð¾ нÑого:
let user = {
_name: "ÐÑÑÑÑ",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) {
return target[prop];
}
});
alert(userProxy.name); // ÐÑÑÑÑ
ÐаÑÑка get ÑÑÑ Ñ âпÑозоÑоÑâ, вона повеÑÑÐ°Ñ Ð¾ÑигÑналÑÐ½Ñ Ð²Ð»Ð°ÑÑивÑÑÑÑ Ñ Ð±ÑлÑÑе нÑÑого не ÑобиÑÑ. ЦÑого доÑÑаÑнÑо Ð´Ð»Ñ Ð½Ð°Ñого пÑикладÑ.
ÐаÑебÑо вÑе гаÑазд. Ðле зÑобÑмо пÑиклад ÑÑÐ¾Ñ Ð¸ ÑкладнÑÑим.
ÐÑÑÐ»Ñ ÑÑпадкÑÐ²Ð°Ð½Ð½Ñ ÑнÑого обâÑкÑа admin вÑд user ми можемо ÑпоÑÑеÑÑгаÑи непÑавилÑÐ½Ñ Ð¿Ð¾Ð²ÐµÐ´ÑнкÑ:
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не Ð¼Ð°Ñ ÑÐ°ÐºÐ¾Ñ ÑвоÑÑ Ð²Ð»Ð°ÑÑивоÑÑÑ, поÑÑк пеÑÐµÑ Ð¾Ð´Ð¸ÑÑ Ð´Ð¾ його пÑоÑоÑипÑ. -
ÐÑоÑоÑипом Ñ
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 називаÑÑÑÑÑ ÑоÑно Ñак Ñамо, Ñк Ñ Ð¿Ð°ÑÑки, Ñ Ð¿ÑиймаÑÑÑ ÑÑ Ð¶ ÑÐ°Ð¼Ñ Ð°ÑгÑменÑи. Ðони бÑли ÑпеÑÑалÑно ÑозÑÐ¾Ð±Ð»ÐµÐ½Ñ Ñаким Ñином.
Тож, return Reflect... забезпеÑÑÑ Ð±ÐµÐ·Ð¿ÐµÑÐ½Ñ Ñа пÑоÑÑÑ Ð¿ÐµÑеадÑеÑаÑÑÑ Ð¾Ð¿ÐµÑаÑÑÑ Ñа обеÑÑÐ³Ð°Ñ Ð²Ñд Ñого, Ñо ми забÑдемо ÑоÑÑ, Ñо повâÑзане з Ñим.
ÐÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿ÑокÑÑ
ÐÑокÑÑ Ð½Ð°Ð´Ð°ÑÑÑ ÑнÑкалÑний ÑпоÑÑб змÑниÑи або налаÑÑÑваÑи поведÑÐ½ÐºÑ ÑÑнÑÑÑÐ¸Ñ Ð¾Ð±âÑкÑÑв на найнижÑÐ¾Ð¼Ñ ÑÑвнÑ. ÐÑе-Ñаки Ñе не ÑдеалÑно. ÐÑнÑÑÑÑ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ.
ÐбÑÐ´Ð¾Ð²Ð°Ð½Ñ Ð¾Ð±âÑкÑи: внÑÑÑÑÑÐ½Ñ ÑлоÑи
ÐагаÑо вбÑдованиÑ
обâÑкÑÑв, напÑиклад Map, Set, Date, Promise Ñа ÑнÑÑ, викоÑиÑÑовÑÑÑÑ Ñак Ð·Ð²Ð°Ð½Ñ âвнÑÑÑÑÑÐ½Ñ ÑлоÑиâ.
Це подÑÐ±Ð½Ñ Ð²Ð»Ð°ÑÑивоÑÑÑ, але заÑезеÑÐ²Ð¾Ð²Ð°Ð½Ñ Ð´Ð»Ñ Ð²Ð½ÑÑÑÑÑнÑÑ
ÑÑлей, пÑизнаÑениÑ
лиÑе Ð´Ð»Ñ ÑпеÑиÑÑкаÑÑÑ. ÐапÑиклад, Map збеÑÑÐ³Ð°Ñ ÐµÐ»ÐµÐ¼ÐµÐ½Ñи Ñ Ð²Ð½ÑÑÑÑÑнÑÐ¾Ð¼Ñ ÑлоÑÑ [[MapData]]. ÐбÑÐ´Ð¾Ð²Ð°Ð½Ñ Ð¼ÐµÑоди оÑÑимÑÑÑÑ Ð´Ð¾ÑÑÑп до ниÑ
безпоÑеÑеднÑо, а не ÑеÑез внÑÑÑÑÑÐ½Ñ Ð¼ÐµÑоди [[Get]]/[[Set]]. Ð¢Ð¾Ð¼Ñ Proxy не може пеÑеÑ
опиÑи Ñе.
Ð§Ð¾Ð¼Ñ Ñе Ð¼Ð°Ñ Ð·Ð½Ð°ÑеннÑ? Ðони ж вÑе одно внÑÑÑÑÑнÑ!
ÐÑ, Ñ Ð¾Ð´Ð½Ð° пÑоблема. ÐÑÑÐ»Ñ Ñого, Ñк Ñакий вбÑдований обâÑÐºÑ Ð¿ÑокÑÑÑÑÑÑÑÑ, пÑокÑÑ Ð½Ðµ Ð¼Ð°Ñ ÑÐ¸Ñ Ð²Ð½ÑÑÑÑÑнÑÑ ÑлоÑÑв, ÑÐ¾Ð¼Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÐ¸ вбÑÐ´Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¼ÐµÑодÑв пÑизведÑÑÑ Ð´Ð¾ помилок.
ÐапÑиклад:
let map = new Map();
let proxy = new Proxy(map, {});
proxy.set('test', 1); // Ðомилка
ÐÑеÑÐµÐ´Ð¸Ð½Ñ Map збеÑÑÐ³Ð°Ñ Ð²ÑÑ Ð´Ð°Ð½Ñ Ñ ÑвоÑÐ¼Ñ Ð²Ð½ÑÑÑÑÑнÑÐ¾Ð¼Ñ ÑлоÑÑ [[MapData]]. ÐÑокÑÑ Ð½Ðµ Ð¼Ð°Ñ Ñакого ÑлоÑа. ÐбÑдований меÑод Map.prototype.set намагаÑÑÑÑÑ Ð¾ÑÑимаÑи доÑÑÑп до внÑÑÑÑÑнÑÐ¾Ñ Ð²Ð»Ð°ÑÑивоÑÑÑ this.[[MapData]], але, оÑкÑлÑки this=proxy, не може знайÑи його в 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).
Ðа вÑдмÑÐ½Ñ Ð²Ñд попеÑеднÑого пÑикладÑ, знаÑÐµÐ½Ð½Ñ this вÑеÑÐµÐ´Ð¸Ð½Ñ proxy.set(...) бÑде не proxy, а оÑигÑналÑним 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 ÑÑнÑÑ Ð¾ÐºÑемо вÑд proxy, ÑÐ¾Ð¼Ñ Ð¼Ð¸ можемо пеÑедаваÑи proxy ÑкÑÑзÑ, залиÑаÑÑи revoke Ñ Ð¿Ð¾ÑоÑнÑй облаÑÑÑ.
Ðи Ñакож можемо пÑивâÑзаÑи меÑод revoke до пÑокÑÑ, вÑÑановивÑи proxy.revoke = revoke.
ÐнÑий ваÑÑÐ°Ð½Ñ â ÑÑвоÑиÑи WeakMap, Ñкий Ð¼Ð°Ñ proxy Ñк клÑÑ Ñ Ð²ÑдповÑдне знаÑÐµÐ½Ð½Ñ revoke Ñк знаÑеннÑ, Ñо дозволÑÑ Ð»ÐµÐ³ÐºÐ¾ знайÑи revoke Ð´Ð»Ñ Ð¿ÑокÑÑ:
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); // Ðомилка (вÑдкликано)
ТÑÑ Ð¼Ð¸ викоÑиÑÑовÑÑмо WeakMap замÑÑÑÑ Map, оÑкÑлÑки вÑн не блокÑÑ Ð·Ð±ÑÑ ÑмÑÑÑÑ. ЯкÑо обâÑÐºÑ Ð¿ÑокÑÑ ÑÑÐ°Ñ âнедоÑÑÑпнимâ (напÑиклад, жодна змÑнна бÑлÑÑе не поÑилаÑÑÑÑÑ Ð½Ð° нÑого), WeakMap дозволÑÑ ÑÑеÑÑи його з памâÑÑÑ Ñазом Ñз його revoke, Ñо нам бÑлÑÑе не знадобиÑÑÑÑ.
ÐоÑиланнÑ
â СпеÑиÑÑкаÑÑÑ: Proxy.
- MDN: Proxy.
ÐÑдÑÑмки
ÐÑокÑÑ â Ñе обгоÑÑка навколо обâÑкÑа, Ñка пеÑенапÑавлÑÑ Ð¾Ð¿ÐµÑаÑÑÑ Ð½Ð°Ð´ Ð½ÐµÑ Ð´Ð¾ обâÑкÑа, маÑÑи можливÑÑÑÑ Ð¿ÐµÑеÑ
оплÑваÑи деÑÐºÑ Ð· ниÑ
.
ÐÑокÑÑÑваÑи можна бÑдÑ-Ñкий Ñип обâÑкÑа, вклÑÑаÑÑи клаÑи Ñа ÑÑнкÑÑÑ.
СинÑакÑÐ¸Ñ Ñакий:
let proxy = new Proxy(target, {
/* паÑÑки */
});
â¦Ð¢Ð¾Ð´Ñ ми Ð¿Ð¾Ð²Ð¸Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾ÑиÑÑовÑваÑи proxy ÑкÑÑÐ·Ñ Ð·Ð°Ð¼ÑÑÑÑ target. ÐÑокÑÑ Ð½Ðµ Ð¼Ð°Ñ ÑвоÑÑ
влаÑÑивоÑÑей Ñи меÑодÑв. ÐÑн пеÑеÑ
оплÑÑ Ð¾Ð¿ÐµÑаÑÑÑ, ÑкÑо паÑÑка пеÑедбаÑена, в ÑнÑÐ¾Ð¼Ñ Ð²Ð¸Ð¿Ð°Ð´ÐºÑ Ð¿ÐµÑеÑÐ¸Ð»Ð°Ñ ÑÑ Ð´Ð¾ ÑÑлÑового обâÑкÑа.
Ðи можемо Ð·Ð°Ñ Ð¾Ð¿Ð¸Ñи:
- ÐÑиÑÑÐ²Ð°Ð½Ð½Ñ (
get), Ð·Ð°Ð¿Ð¸Ñ (set), Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ (deleteProperty) влаÑÑивоÑÑÑ (навÑÑÑ Ð½ÐµÑÑнÑÑÑоÑ). - Ðиклик ÑÑнкÑÑÑ (паÑÑка
apply). - ÐпеÑаÑоÑ
new(паÑÑкаconstruct). - ÐагаÑо ÑнÑÐ¸Ñ Ð¾Ð¿ÐµÑаÑÑй (повний ÑпиÑок на поÑаÑÐºÑ ÑÑаÑÑÑ Ñа в докÑменÑаÑÑÑ).
Це дозволÑÑ Ð½Ð°Ð¼ ÑÑвоÑÑваÑи âвÑÑÑÑалÑнÑâ влаÑÑивоÑÑÑ Ñа меÑоди, ÑеалÑзовÑваÑи знаÑÐµÐ½Ð½Ñ Ð·Ð° замовÑÑваннÑм, ÑпоÑÑеÑежÑÐ²Ð°Ð½Ñ Ð¾Ð±âÑкÑи, декоÑаÑоÑи ÑÑнкÑÑй Ñа багаÑо ÑнÑого.
Ðи Ñакож можемо обгоÑнÑÑи обâÑÐºÑ ÐºÑлÑка ÑазÑв Ñ ÑÑÐ·Ð½Ñ Ð¿ÑокÑÑ, пÑикÑаÑаÑÑи його ÑÑзними аÑпекÑами ÑÑнкÑÑоналÑноÑÑÑ.
Reflect API ÑозÑоблено Ð´Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Proxy. ÐÐ»Ñ Ð±ÑдÑ-ÑÐºÐ¾Ñ Ð¿Ð°ÑÑки Proxy Ñ Ð²Ð¸ÐºÐ»Ð¸Ðº Reflect з Ñими Ñамими аÑгÑменÑами. Ðи Ð¿Ð¾Ð²Ð¸Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾ÑиÑÑовÑваÑи ÑÑ
Ð´Ð»Ñ Ð¿ÐµÑеадÑеÑаÑÑÑ Ð²Ð¸ÐºÐ»Ð¸ÐºÑв ÑÑлÑовим обâÑкÑам.
ÐÑокÑÑ Ð¼Ð°ÑÑÑ Ð´ÐµÑÐºÑ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ:
- ÐбÑÐ´Ð¾Ð²Ð°Ð½Ñ Ð¾Ð±âÑкÑи маÑÑÑ âвнÑÑÑÑÑÐ½Ñ ÑлоÑиâ, доÑÑÑп до Ð½Ð¸Ñ Ð½Ðµ може бÑÑи пÑокÑÑйованим. ÐеÑеглÑнÑÑе Ð¾Ð±Ñ Ñдний ÑлÑÑ Ð²Ð¸Ñе.
- Те ж Ñаме ÑÑоÑÑÑÑÑÑÑ Ð¿Ð¾Ð»Ñв пÑиваÑного клаÑÑ, оÑкÑлÑки вони внÑÑÑÑÑнÑо ÑеалÑÐ·Ð¾Ð²Ð°Ð½Ñ Ð·Ð° Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾Ñ ÑлоÑÑв. Ð¢Ð¾Ð¼Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÐ¸ меÑодÑв ÑеÑез пÑокÑÑ Ð¿Ð¾Ð²Ð¸Ð½Ð½Ñ Ð¼Ð°Ñи ÑÑлÑовий обâÑÐºÑ Ñк
thisÐ´Ð»Ñ Ð´Ð¾ÑÑÑÐ¿Ñ Ð´Ð¾ Ð½Ð¸Ñ . - ÐеÑевÑÑки обâÑкÑÑв на ÑÑвоÑÑ ÑÑвнÑÑÑÑ
===не можÑÑÑ Ð±ÑÑи пеÑÐµÑ Ð¾Ð¿Ð»ÐµÐ½Ñ. - ÐÑодÑкÑивнÑÑÑÑ: конÑÑолÑÐ½Ñ Ð¿Ð¾ÐºÐ°Ð·Ð½Ð¸ÐºÐ¸ залежаÑÑ Ð²Ñд ÑнÑеÑпÑеÑаÑоÑа, але зазвиÑай доÑÑÑп до влаÑÑивоÑÑÑ Ð·Ð° Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾Ñ Ð½Ð°Ð¹Ð¿ÑоÑÑÑÑого пÑокÑÑ Ð·Ð°Ð¹Ð¼Ð°Ñ Ð² кÑлÑка ÑазÑв бÑлÑÑе ÑаÑÑ. Ðа пÑакÑиÑÑ Ñе Ð¼Ð°Ñ Ð·Ð½Ð°ÑÐµÐ½Ð½Ñ Ð»Ð¸Ñе Ð´Ð»Ñ Ð´ÐµÑÐºÐ¸Ñ âоÑобливо наванÑÐ°Ð¶ÐµÐ½Ð¸Ñ â обâÑкÑÑв.
ÐоменÑаÑÑ
<code>, Ð´Ð»Ñ ÐºÑлÑÐºÐ¾Ñ ÑÑдкÑв â обгоÑнÑÑÑ ÑÑ Ñегом<pre>, Ð´Ð»Ñ Ð¿Ð¾Ð½Ð°Ð´ 10 ÑÑдкÑв â викоÑиÑÑовÑйÑе пÑÑоÑниÑÑ (plnkr, jsbin, codepenâ¦)