Кайталануучу (же кайталануучу ) объекттер массивдердин жалпылоосу болуп саналат. Ар кандай объектти циклде колдонууга мүмкүндүк берген түшүнүк for..of
.
Албетте, массивдердин өзү кайталануучу объекттер. Бирок саптар сыяктуу көптөгөн башка камтылган кайталануучулар бар.
for..of
Эгерде объект массив эмес, кээ бир элементтердин (тизме, топтом) жыйындысы болсо, анда аларды кайталоо үчүн циклди колдонуу ыңгайлуу, андыктан муну кантип жасоо керектигин карап көрөлү.
Symbol.iterator
Кайталануучу объекттердин жайгашуу принцибин алардын бирин түзүү менен оңой түшүнө алабыз.
Мисалы, бизде объект бар. Бул массив эмес, бирок ал үчүн ылайыктуу окшойт for..of
.
Мисалы, range
сандардын диапазонун билдирген объект:
@A@let range = {
from: 1,
to: 5
};
// Мы хотим, чтобы работал for..of:
// for(let num of range) ... num=1,2,3,4,5@A@
Аны кайталануучу кылуу үчүн range
(жана for..of
аны менен иштөөгө уруксат берүү) биз объектке аталган ыкманы кошуубуз керек ( бул үчүн гана түзүлгөн Symbol.iterator
атайын орнотулган ).Symbol
- Цикл
for..of
иштегенде, бул ыкманы бир жолу чакырат (же метод табылбаса ката кетирет). Бул ыкма итераторду - методу бар объектти кайтарышы керекnext
. - Андан ары бул кайтарылган объект менен гана
for..of
иштейт . for..of
Кийинки маанини алгысы келгенде, ал объектnext()
боюнча методду чакырат.- Чалуунун натыйжасы
next()
окшош болушу керек{done: Boolean, value: any}
, булdone=true
цикл аяктаганын билдирет, антпесеvalue
ал кийинки маанини камтыйт.
Бул жерде түшүндүрмөлөр менен толук ишке ашыруу болуп саналат range
:
@A@let range = {
from: 1,
to: 5
};
// 1. вызов for..of сначала вызывает эту функцию
range[Symbol.iterator] = function() {
// ...она возвращает объект итератора:
// 2. Далее, for..of работает только с этим итератором, запрашивая у него новые значения
return {
current: this.from,
last: this.to,
// 3. next() вызывается на каждой итерации цикла for..of
next() {
// 4. он должен вернуть значение в виде объекта {done:.., value :...}
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
// теперь работает!
for (let num of range) {
alert(num); // 1, затем 2, 3, 4, 5
}@A@
Итераторлордун негизги өзгөчөлүгүнө көңүл буруңуз: тынчсызданууларды бөлүү.
- Анын өзүнүн
range
ыкмасы жокnext()
. - Анын ордуна, "итератор" деп аталган башка объект чалуу тарабынан түзүлөт
range[Symbol.iterator]()
жана булnext()
баалуулуктарды жаратат.
Ошентип, итератор объекти кайталануучу объектинин өзүнөн өзүнчө.
range
Техникалык жактан биз аларды бириктирип, кодду жөнөкөйлөтүү үчүн өзүн итератор катары колдоно алабыз .
Мисалы, бул сыяктуу:
@A@let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
this.current = this.from;
return this;
},
next() {
if (this.current <= this.to) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
for (let num of range) {
alert(num); // 1, затем 2, 3, 4, 5
}@
Эми range[Symbol.iterator]()
объекттин өзүн кайтарат range
: анын талап кылынган ыкмасы бар next()
жана ал итерациянын учурдагы абалын эстейт this.current
. Кыскача айтканда? Ооба. Жана кээде бул да жакшы.
Бул ыкманын кемчилиги - азыр биз бул объектти эки параллелдүү циклде колдоно албайбыз for..of
: алар жалпы учурдагы итерация абалына ээ болот, анткени азыр бир гана итератор бар - объекттин өзү. Бирок бир эле учурда аткарылуучу эки циклдин зарылдыгы for..of
асинхрондук операциялар болгондо да сейрек пайда болот.
Сиз чексиз итератор жасай аласыз. Мисалы, range
чексиз болот range.to = Infinity
. Же псевдококустук сандардын чексиз ырааттуулугун жараткан кайталануучуну түзө алабыз. Пайдалуу болуп калат.
Метод next
эч кандай чектөөлөргө ээ эмес, ал барган сайын жаңы баалуулуктарды кайтара алат, бул нормалдуу көрүнүш.
Албетте, for..of
мындай кайталануучу объект менен цикл чексиз болот. Бирок биз аны колдонуу менен ар дайым үзгүлтүккө учурата алабыз break
.
Стринг - кайталануучу объект
Эң кеңири колдонулган камтылган кайталануучулар массивдер жана саптар.
Сап for..of
символдордун үстүнөн кайталанат:
@A@for (let char of "test") {
// срабатывает 4 раза: по одному для каждого символа
alert( char ); // t, затем e, затем s, затем t
}@A@
Жана ал суррогат жуптары менен да туура иштейт!
@A@let str = '𝒳😂';
for (let char of str) {
alert( char ); // 𝒳, а затем 😂
}@A@
Ачык итератор чалуу
Итераторлорду бир аз көбүрөөк түшүнүү үчүн, аларды кантип ачык колдонууну карап көрөлү.
Биз сапты циклдегидей эле кайталайбыз for..of
, бирок кол менен түз чалуулар аркылуу. Төмөнкү код сап итераторун алып, андан баалуулуктарды алат:
@A@let str = "Hello";
// делает то же самое, что и
// for (let char of str) alert(char);
let iterator = str[Symbol.iterator]();
while (true) {
let result = iterator.next();
if (result.done) break;
alert(result.value); // выводит символы один за другим
}@A@
Бул чанда гана керек, бирок ал бизге процессти көзөмөлдөөгө мүмкүнчүлүк берет for..of
. Мисалы, биз итерация процессин бөлүктөргө бөлсөк болот: бир нече элементтерди кайталап, андан кийин токтоп, башка бир нерсе жасап, анан улантыңыз.
Итеративдер жана псевдо-массивдер
Абдан окшош, бирок ошол эле учурда абдан айырмаланган эки расмий термин бар. Андыктан баш аламандыкка жол бербөө үчүн аларды жакшы түшүнгөнүңүздү текшериңиз.
- Итеративдер
Symbol.iterator
- жогоруда сүрөттөлгөндөй ыкманы ишке ашыруучу объекттер . - Псевдомассивдер - бул индекстери жана касиети бар объекттер
length
, башкача айтканда, массивдерге окшош.
JavaScriptти браузерде же башка чөйрөлөрдө колдонгондо, биз кайталануучу же псевдо-массив же экөө тең объекттерге туш болушубуз мүмкүн.
Мисалы, саптар кайталануучу (алар үчүн иштейт for..of
) жана псевдо-массивдер (алар индекстелген жана length
).
Бирок кайталануучу объект псевдо-массив болбошу мүмкүн. Тескерисинче, псевдо-массив кайталанбашы мүмкүн.
range
Мисалы, жогорудагы мисалдагы объект кайталануучу, бирок ал псевдо-массив эмес, анткени анын индекстелген касиеттери жана length
.
Жана бул жерде псевдо-массив болгон объект, бирок кайталанбайт:
let arrayLike = { // есть индексы и свойство length => псевдомассив
0: "Hello",
1: "World",
length: 2
};
// Ошибка (отсутствует Symbol.iterator)
for (let item of arrayLike) {}
Алардын кандай жалпылыгы бар? Итеративдер да, псевдо-массивдер да адатта массив эмесpush
, алардын методдору жок pop
ж.б.у.с. Эгерде бизде мындай объект бар болсо жана аны менен массив катары иштегибиз келсе, абдан ыңгайсыз. range
Мисалы, биз массив ыкмаларын колдонуу менен иштегибиз келет . Буга кантип жетишсе болот?
Array.from
Array.from жалпы ыкмасы бар , ал кайталануучу объектти же псевдо-массивди алып, аны "чыныгы" кылат Array
. Андан кийин, биз буга чейин массив ыкмаларын колдоно алабыз.
Мисалы:
@A@let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike); // (*)
alert(arr.pop()); // World (метод работает)@A@
Array.from
объектти inline алат (*)
, анын кайталануучу объект же псевдо-массив экендигин текшерет, андан кийин жаңы массив түзүп, ал жердеги бардык элементтерди көчүрөт.
Ошол эле нерсе кайталануучу объект менен болот:
@A@// range взят из примера выше
let arr = Array.from(range);
alert(arr); // 1,2,3,4,5 (преобразование массива через toString работает)@A@
Толук синтаксис Array.from
кошумча "трансформациялоо" функциясын көрсөтүүгө мүмкүндүк берет:
@A@Array.from(obj[, mapFn, thisArg])@A@
Кошумча экинчи аргумент массивге кошулганга чейин ар бир элементке колдонула турган функция болушу мүмкүн жана ал функцияны коюуга thisArg
мүмкүндүк берет .this
Мисалы:
@A@// range взят из примера выше
// возводим каждое число в квадрат
let arr = Array.from(range, num => num * num);
alert(arr); // 1,4,9,16,25@A@
Бул жерде биз Array.from
сапты анын элементтеринин массивине айландыруу үчүн колдонобуз:
@A@let str = '𝒳😂';
// разбивает строку на массив её элементов
let chars = Array.from(str);
alert(chars[0]); // 𝒳
alert(chars[1]); // 😂
alert(chars.length); // 2@A@
дан айырмаланып str.split
, бул ыкма сап итерациясына таянат, ошондуктан, сыяктуу for..of
, ал суррогат жуптары менен туура иштейт.
Техникалык жактан бул окшош:
@A@let str = '𝒳😂';
let chars = []; // Array.from внутри себя выполняет тот же цикл
for (let char of str) {
chars.push(char);
}
alert(chars);@A@
...Бирок алда канча кыскараак.
@A@slice
Ал тургай , биз суррогат жуптарды колдогон , түзө алабыз :
function slice(str, start, end) {
return Array.from(str).slice(start, end).join('');
}
let str = '𝒳😂𩷶';
alert( slice(str, 1, 3) ); // 😂𩷶
// а вот встроенный метод не поддерживает суррогатные пары
alert( str.slice(1, 3) ); // мусор (две части различных суррогатных пар)@A@
Бардыгы
Циклде колдонула турган объекттер кайталануучуfor..of
деп аталат .
- Техникалык жактан, кайталануучуларда
Symbol.iterator
.- Чалуунун натыйжасы итератор
obj[Symbol.iterator]
деп аталат . Ал итерация процессин башкарат. next()
Итераторда объектти кайтаруучу метод болушу керек,{done: Boolean, value: any}
ал жердеdone:true
ал кайталоо процессинин аяктаганын, антпесеvalue
кийинки маанини берет.
- Чалуунун натыйжасы итератор
- Метод
Symbol.iterator
цикл тарабынан автоматтык түрдө чакырылатfor..of
, бирок сиз аны түз чакырсаңыз да болот. - Саптар же массивдер сыяктуу орнотулган кайталануучулар да
Symbol.iterator
. - Саптын итератору суррогат жуптары жөнүндө билет.
Индекстелген касиеттерге ээ жана псевдо-массивдерlength
деп аталган объекттер . Алар ошондой эле башка касиеттерге жана ыкмаларга ээ болушу мүмкүн, бирок аларда камтылган массив ыкмалары жок.
Эгерде биз спецификацияны карай турган болсок, анда орнотулган ыкмалардын көбү "чыныгы" массивдердин ордуна кайталануучуларга же псевдо-массивдерге таянарын көрө алабыз, анткени ал объекттер абстракттуураак.
Array.from(obj[, mapFn, thisArg])
Array
кайталануучу же псевдо-массивден чыныгыны түзөт obj
, анан биз ага массив ыкмаларын колдоно алабыз. Кошумча аргументтер mapFn
жана thisArg
ар бир элементке берилген контекст менен функцияны колдонууга мүмкүндүк берет.