Цель: Итерация по свойствам объекта с возможностью требований к свойствам.
Настройки: Принимает на вход два аргумента, первый это любой объект, свойства которого будут перечислены, второй аргумент это объект опций, но вместо объекта можно указать функцию, которая будет задавать правила по которому будет отсеивать свойства.
Номенклатура объекта опций:
{
/// Данный режим влияет на то как будут интерпретироваться флаги,
/// match - это полное соответствие флагов к дескрипторам,
/// accessor - тогда значения флагов могут быть true или false, если true тогда тогда не важно какое значение имеет дескриптор свойства, если false тогда дескриптор с false или undefined не учитывается при перечислении
mode: match/permission,
/// Выбор по типу дескриптора
type: both/data/accessor,
/// К свойствам теперь добавляется [[Prototype]], true - прототип добавляется к перечислению и к раскрытию, false - выключен, iterate - только перечисляется, reveal - только раскрывается, only - перечисляются только прототипы
proto: true/false/iterate/reveal/only,
/// Раскрытие сложных типов
reveal: {
/// Режим раскрытия, exclude - исключающий, include - включающий (черный и белый списки)
mode: exclude/include,
/// Указываются типы в строковом типе
list: [...]
},
/// Сортировка по значениям флагов, если какой-то из флагов не указывается, значит он не учитывается
flags: {
/// Эти свойства по-умолчанию не указываются (и считается что по всем свой свойствам идет совпадение)
enumerable: any_value or true/false,
writable: any_value or true/false,
configurable: any_value or true/false,
value: any_value or true/false,
set: any_value or true/false,
get: any_value or true/false,
}
}
Если вам требуется другая логика и вас не устраивает тот набор опций то можете указать функцию, она имеет следующую номенклатуру:
/// this - объект с которого началось перечисление (объект-инициатор)
/// descriptor - дескриптор свойства итерируемого объекта (включает в себя тип дескриптора)
/// key - имя свойства итерируемого элемента
/// node - объект с узлами
/// node.parent - объект-родитель данному итерируемому элементу
/// node.current - текущий итерируемый объект, если не объект, тогда null
/// node.isVisited - если node.current объект и он ранее был посещен будет значение true, в противном случае false
/// proto - содержит вспомогательные флаги для определения прототипов
/// proto.is - является ли итерируемое на данный момент значение прототипом
/// proto.isIn - является ли итерируемое значение на данный момент внутри прототипа
/// trace - объект с трассировкой свойств объектов
/// trace.main - список свойств по которым нужно пройти чтобы попасть в текущий итерируемый элемент
/// trace.proto - список свойств по которым нужно пройти чтобы попасть в текущий итерируемый элемент, но начинается с прототипа
function ({descriptor, key, node, proto, trace}) {
/// true - дескриптор допускается к перечислению и раскрытию
/// false - дескриптор не допускается ни к перечислению ни к раскрытию
/// iterate - дескриптор допускается только к перечислению
/// reveal - дескриптор допускается только к раскрытию
/// exit - полный выход из перечисления свойств объекта
return true/false/iterate/reveal/exit;
}
Примечание: Модификация дескриптора и структур, которые обращаются к перечисляемой структуре заблокирована. Это сделано намерено чтобы уберечь от непредсказуемого поведения.
Для примеров я буду использовать следующую структуру объекта:
const obj = {
a: 1,
b: 2,
c: 3,
d: {
value1: "text1",
value2: "text2",
value3: "text3",
},
get e() {
return "getter value";
},
__proto__: {
aaa: 100,
bbb: 200,
ccc: 300,
}
}
Пример 1 (по-умолчанию, перечисляются свойства объекта, прототипы выключены, функции не раскрываются):
for (const item of createObjectIterator(obj)) console.log(item);
/// или
for (const item of createObjectIterator(obj, ({node, proto}) => {
if (proto.is) return false;
if (typeof node.current === "function") return "iterate";
return true;
})) console.log(item);
/// Вывод:
/// {descriptor: Proxy(Object), key: 'a', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'b', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'c', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'd', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'value1', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'value2', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'value3', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'e', node: {…}, proto: {…}, trace: {…}}
Пример 2 (перечисляются свойства объекта и его прототипы со всеми их свойствами, функции не раскрываются):
for (const item of createObjectIterator(obj, {proto: true})) console.log(item)
/// или
for (const item of createObjectIterator(obj, ({node}) => {
if(typeof node.current === "function") return "iterate";
return true;
})) console.log(item)
/// Вывод:
/// {descriptor: Proxy(Object), key: 'a', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'b', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'c', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'd', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'value1', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'value2', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'value3', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '[[Prototype]]', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'constructor', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '__defineGetter__', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '__defineSetter__', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'hasOwnProperty', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '__lookupGetter__', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '__lookupSetter__', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'isPrototypeOf', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'propertyIsEnumerable', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'toString', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'valueOf', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '__proto__', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'toLocaleString', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '[[Prototype]]', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'e', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '[[Prototype]]', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'aaa', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'bbb', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'ccc', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '[[Prototype]]', node: {…}, proto: {…}, trace: {…}}
Пример 3 (перечисление как в for...in):
for (const item of createObjectIterator(obj, {
mode: "match",
proto: "reveal",
reveal: {
mode: "exclude",
list: []
},
flags:{
enumerable: true
}
})) console.log(item)
/// или
for (const item of createObjectIterator(obj, ({descriptor, node, proto}) => {
if (proto.is) return "reveal";
if (descriptor.enumerable) {
if (typeof node.current === "function") return "iterate";
return true;
}
})) console.log(item)
/// Вывод
/// {descriptor: Proxy(Object), key: 'a', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'b', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'c', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'd', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'value1', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'value2', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'value3', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'e', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'aaa', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'bbb', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: 'ccc', node: {…}, proto: {…}, trace: {…}}
Пример 4: (перечислить только прототипы):
for (const item of createObjectIterator(obj, {proto: "only"})) console.log(item)
/// или
for (const item of createObjectIterator(obj, ({proto}) => {
if (proto.is) return true;
})) console.log(item)
/// Вывод:
/// {descriptor: Proxy(Object), key: '[[Prototype]]', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '[[Prototype]]', node: {…}, proto: {…}, trace: {…}}
/// {descriptor: Proxy(Object), key: '[[Prototype]]', node: {…}, proto: {…}, trace: {…}}