Бул бөлүмдүн биринчи бөлүмүндө биз прототиптер менен иштөөнүн заманбап ыкмалары бар экенин айттык.
Менчик __proto__
эскирген жана стандарт боюнча браузерлер тарабынан гана колдоого алынышы керек.
Заманбап ыкмалары болуп төмөнкүлөр саналат:
- Object.create(прото, [дескрипторлор])
[[Prototype]]
- катары белгиленген касиетиproto
жана кошумча касиетинин дескрипторлору менен бош объектти түзөтdescriptors
. - Object.getPrototypeOf(obj)
[[Prototype]]
- Объекттин касиетин кайтаратobj
. - Object.setPrototypeOf(obj, proto) -
[[Prototype]]
объекттинobj
касиетинproto
.
Бул ыкмаларды ордуна колдонуу керек __proto__
.
Мисалы:
@A@let animal = {
eats: true
};
// создаём новый объект с прототипом animal
let rabbit = Object.create(animal);
alert(rabbit.eats); // true
alert(Object.getPrototypeOf(rabbit) === animal); // получаем прототип объекта rabbit
Object.setPrototypeOf(rabbit, {}); // заменяем прототип объекта rabbit на {}@A@
Анын Object.create
кошумча экинчи аргументи бар: мулк дескрипторлору. Жаңы объектке төмөнкүдөй кошумча касиетти кошо алабыз:
@A@let animal = {
eats: true
};
let rabbit = Object.create(animal, {
jumps: {
value: true
}
});
alert(rabbit.jumps); // true@A@
Дескрипторлорду көрсөтүү форматы Flags жана касиеттин дескрипторлору бөлүмүндө сүрөттөлөт .
Object.create
Биз ошондой эле циклдеги касиеттерди көчүрүүдөн күчтүүрөөк объектти клондоо үчүн колдоно алабыз for..in
:
// клон obj c тем же прототипом (с поверхностным копированием свойств)
let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
Мындай чалуу объекттин так көчүрмөсүн түзөт obj
, анын ичинде бардык касиеттери: саналуучу жана саналбаган, касиеттерге алуучулар/жөндөөчүлөр - бардыгы туура касиетке ээ [[Prototype]]
.
Кыска окуя
Эгерде сиз прототипти башкаруунун бардык жолдорун санасаңыз, анда алар көп болот! Жана алардын көбү бир эле нерсени жасашат!
Эмнеге андай?
Тарыхый себептерден улам.
"prototype"
Конструктор функциясынын касиети көптөн бери эле бар.- Кийинчерээк, 2012-жылы
Object.create
. Бул көрсөтүлгөн прототиби менен объекттерди түзүүгө мүмкүндүк берди, бирок аны орнотууга/алууга жол берген жок. Ал кезде браузерлер стандарттуу эмес аксессорду ишке ашырышкан__proto__
, ал прототипти каалаган убакта орнотууга/чыгарып алууга мүмкүндүк берген. - Кийинчерээк, 2015-жылы алмаштыруучу аксессуар стандартка кошулган
Object.setPrototypeOf
,Object.getPrototypeOf,
ал__proto__
стандарттын В тиркемесинде айтылган, ал браузерден тышкары чөйрөлөрдө колдоого алынбайт. Ошол эле учурда де-факто__proto__
дагы эле бардык жерде колдоого алынат.
Натыйжада, азыр бизде прототиби менен иштөөнүн бардык жолдору бар.
Эмне үчүн __proto__
ал функциялар менен алмаштырылган getPrototypeOf/setPrototypeOf
? Жоопту билүү үчүн окуңуз.
[[Prototype]]
Ылдамдык маанилүү болсо, учурдагы объекттерди өзгөртпөңүз[[Prototype]]
Техникалык жактан биз каалаган убакта орното/ала алабыз . Бирок, адатта, объектти түзүү учурунда прототипти бир гана жолу орнотобуз, андан кийин биз аны өзгөртпөйбүз: rabbit
ал дан мурастайт animal
жана бул өзгөрбөйт.
Жана JavaScript кыймылдаткычтары бул үчүн жакшы оптималдаштырылган. Прототипти тез арада өзгөртүү Object.setPrototypeOf
же obj.__proto__=
объекттин касиетине кирүү операциялары үчүн ички оптималдаштырууну буза турган өтө жай операция. Андыктан эмне кылып жатканыңызды билмейинче же JavaScript ылдамдыгы сизди кызыктырбаса, андан качканыңыз жакшы.
"Эң жөнөкөй" объект
Белгилүү болгондой, объекттер ачкыч/баа жуптарын сактоо үчүн ассоциативдик массивдер катары колдонулушу мүмкүн.
…Бирок, эгерде биз колдонуучу тарабынан түзүлгөн ачкычтарды сактоого аракет кылсак (мисалы, колдонуучу киргизген сөздүктөр), биз кызыктуу мүчүлүштүктөрдү байкашыбыз мүмкүн: бардык баскычтар күтүлгөндөй иштейт, "__proto__"
.
Мисалга карагыла:
@A@let obj = {};
let key = prompt("What's the key?", "__proto__");
obj[key] = "some value";
alert(obj[key]); // [object Object], не "some value"!@A@
Колдонуучу кирсе __proto__
, тапшырма этибарга алынбайт!
Жана бул бизди таң калтырбашы керек. Мүлк __proto__
өзгөчө: ал объект же а болушу керек null
, ал эми сап прототип боло албайт.
Бирок биз бул жүрүм-турумду ишке ашырууну көздөгөн эмеспиз , туурабы? Биз ачкыч/маани жуптарын сактагыбыз келет жана аталган ачкыч "__proto__"
туура сакталган эмес. Демек, бул ката!
Бул конкреттүү мисалда кесепеттер анчалык деле коркунучтуу эмес, бирок биз объекттин баалуулуктарын ыйгарсак, анда прототибин чындап эле өзгөртүүгө болот. Натыйжада, андан ары аткаруу таптакыр күтүүсүз жол менен кетет.
Эң жаманы, иштеп чыгуучулар бул мүмкүнчүлүк жөнүндө такыр ойлонушпайт. Бул мындай каталарды кармоону кыйындатат же ал тургай, айрыкча серверде JavaScript колдонулганда, алсыздыктарга айланат.
toString
Күтүлбөгөн нерселер демейки боюнча функция болгон касиетти жана чындыгында орнотулган ыкмалар болгон башка касиеттерди дайындоодо да болушу мүмкүн .
Кантип көйгөйдөн качуу керек?
Биринчиден, биз коллекцияны колдонууга өтсөк болот Map
, анан баары жакшы болот.
Бирок Object
ал жакшы иштей алат, анткени тилди жаратуучулар маселени чечүү жолун көптөн бери ойлонуп келишкен.
Мүлк __proto__
жөнөкөй эмес, төмөнкүдө аныкталган аксессуар болуп саналат Object.prototype
Ошентип, окуганда же орнотууда, obj.__proto__
прототиптен тиешелүү алуучу/жөндөөчү деп аталат obj
, жана бул касиетти орноткон/алат [[Prototype]]
.
Окутуучунун бул бөлүмүнүн башында айтылгандай, __proto__
бул менчикке жетүүнүн жолу [[Prototype]]
, ал менчиктин өзү эмес [[Prototype]]
.
Эми, эгерде объектти ассоциативдик массив катары колдонгубуз келсе, анда биз муну бир аз куулук менен жасай алабыз:
@A@let obj = Object.create(null);
let key = prompt("What's the key?", "__proto__");
obj[key] = "some value";
alert(obj[key]); // "some value"@A@
Object.create(null)
прототиби жок бош объектти түзөт ( [[Prototype]]
эрк null
):
Ошентип, . үчүн тукум кууган алуучу/жөндөөчү болбойт __proto__
. Бул мүлк азыр кадимки менчик катары каралат жана жогорудагы мисал туура иштейт.
Биз мындай объектти "эң жөнөкөй" же "таза сөздүк объектиси" деп атасак болот, анткени ал кадимки объекттерден да жөнөкөй {...}
.
Кемчилиги - мындай объекттерде орнотулган объекттик методдор болбойт, мисалы toString
:
@A@let obj = Object.create(null);
alert(obj); // Ошибка (no toString)@A@
…Бирок бул адатта ассоциативдик массивдер үчүн жакшы.
Объекттерге байланышкан ыкмалардын көбү формада экенине көңүл буруңуз Object.something(...)
. Мисалы, Object.keys(obj)
. Мындай ыкмалар прототипте жок, ошондуктан алар мындай объекттер үчүн иштей беришет:
@A@let chineseDictionary = Object.create(null);
chineseDictionary.hello = "你好";
chineseDictionary.bye = "再见";
alert(Object.keys(chineseDictionary)); // hello,bye@A@
Бардыгы
орнотуунун заманбап ыкмалары жана прототиби түз жетүү болуп саналат:
- Object.create(proto[, descriptors]) - (мүмкүн )
[[Prototype]]
катары көрсөтүлгөн касиети жана кошумча касиеттин дескрипторлору менен бош объектти түзөт .proto
null
- Object.getPrototypeOf(obj) -
[[Prototype]]
объекттин касиетин кайтаратobj
(алуучу сыяктуу__proto__
). - Object.setPrototypeOf(obj, proto)
[[Prototype]]
- объекттин касиетин (setter менен элеobj
) орнотот .proto
__proto__
Эгерде биз объектте колдонуучу тарабынан түзүлгөн__proto__
ачкычтарды колдонгубуз келсе, орнотулган алуу/жөндөөчү коопсуз эмес. Жок дегенде, себеби колдонуучу ачкыч катары киргизе алат, бул катага алып келиши мүмкүн. Эгер бактылуу болсоңуз, кесепеттери жумшак болот, бирок, жалпысынан алганда, алар күтүүсүз болот."__proto__"
Object.create(null)
Ошентип, биз "жөнөкөй" объектти түзүү үчүн колдоно алабыз , же Map
.
Мындан тышкары, Object.create
ал бизге бардык дескрипторлор менен объекттин тайыз көчүрмөсүн түзүүнүн оңой жолун берет:
let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
Биз ошондой эле __proto__
мүлк үчүн алуучу/жөндөөчү экенин айкын көрдүк жана ал башка ыкмалар сыяктуу эле [[Prototype]]
жайгашкан .Object.prototype
Биз прототиптери жок объекттерди түзө алабыз Object.create(null)
. Мындай объекттерди "таза сөздүктөр" катары колдонсо болот, аларда сапты "__proto__"
ачкыч катары колдонууда көйгөй жок.
Көбүрөөк ыкмалар:
- Object.keys(obj) / Object.values(obj) / Object.entries(obj) - Бардык санала турган түпкү сап ачкычтарынын/баалардын/ачкыч-маани түгөйлөрүнүн массивдерин кайтарат.
- Object.getOwnPropertySymbols(obj) - Бардык түпнуска символикалык ачкычтардын массивдерин кайтарат.
- Object.getOwnPropertyNames(obj) - Бардык түпкү сап баскычтарынын массивдерин кайтарат.
- Reflect.ownKeys(obj) - Бардык өз ачкычтарынын массивин кайтарат.
- obj.hasOwnProperty(ачкыч) :
true
эгерде анынobj
өзүнүн (мурас алынбаган) касиети бар болсо кайтаратkey
.
Объекттин касиеттерин кайтарган бардык ыкмалар (мисалы, Object.keys
башкалар) "өздүк" касиеттерин кайтарат. Эгерде биз да мураска алгыбыз келсе, анда биз колдоно алабыз for..in
.
Tasks
Ар кандай жуптарды сактоо үчүн dictionary
түзүлгөн объект бар .Object.create(null)
ключ/значение
dictionary.toString()
Ачкычтардын үтүр менен бөлүнгөн тизмесин кайтара турган ыкманы кошуңуз . Объектти цикл менен итерациялоодо сиздики toString
чыгарылбашы керек for..in
.
Бул кандай иштеши керек:
@A@let dictionary = Object.create(null);
// ваш код, который добавляет метод dictionary.toString
// добавляем немного данных
dictionary.apple = "Apple";
dictionary.__proto__ = "test"; // здесь __proto__ -- это обычный ключ
// только apple и __proto__ выведены в цикле
for(let key in dictionary) {
alert(key); // "apple", затем "__proto__"
}
// ваш метод toString в действии
alert(dictionary); // "apple,__proto__"@A@
Келгиле, жаңы объект түзөлү rabbit
:
@A@function Rabbit(name) {
this.name = name;
}
Rabbit.prototype.sayHi = function() {
alert(this.name);
};
let rabbit = new Rabbit("Rabbit");@A@
Бул чалуулардын бардыгы бирдей эле нерсени жасайбы же жокпу?
@A@rabbit.sayHi();
Rabbit.prototype.sayHi();
Object.getPrototypeOf(rabbit).sayHi();
rabbit.__proto__.sayHi();@A@