Я хочу спустить объект в Javascript в поисках определенной строки. К сожалению, этот объект построен таким образом, что было бы невозможно просто использовать источник и Ctrl-F для этой строки, а также он построен таким образом, что рекурсивные функции, пытающиеся спуститься по нему, рискуют застрять внутри него. навсегда.
По сути, этот объект содержит сам себя. Не один раз, а во многих областях. Я не могу просто сказать «исключить эти ключи», так как объект запутан, и поэтому мы будем здесь весь день перечислять ключи, и как только мы закончим, мы не будем смотреть все данные.
Кроме того, мне нужно иметь возможность спускаться по __proto__
и prototype
, так как там тоже спрятаны полезные строки. (Но только для функций и объектов.)
Хотя я бы предпочел что-то вроде findStuff(object, /string/ig)
, это может быть сложно, поэтому любая функция, которая просто имеет четко обозначенные области, на которые падает поток управления, как только она находит определенные объекты (функцию, строку и т.
Спасибо, и извините за такой боль в прикладе вопрос.
Обновлено: если это поможет, я пытаюсь пройти скомпилированный объект времени выполнения Construct2. Я не собираюсь публиковать здесь все целиком, так как оно не поместится ни в какой pastebin, каким бы прощающим оно ни было, а также я не хочу случайно публиковать ресурсы, на предоставление которых у меня нет разрешения. (Не волнуйтесь, я не пытаюсь пиратить его сам, я просто пытаюсь выяснить некоторые функциональные возможности, ориентированные на пользователя)
Вы можете использовать WeakSet для отслеживания уже пройденных объектов:
function traverseOnce(obj, cb) {
const visited = new WeakSet();
(function traverse(obj) {
for(const [key, value] of Object.entries(obj)) {
if (typeof value === "object" && value !== null) {
if (visited.has(value)) continue;
visited.add(value);
cb(value);
traverse(value);
}
}
})(obj);
}
С помощью WeakSet вы получаете время поиска O(1), а также уверены, что это никогда не утечет.
Используется как:
const nested = { other: { a: 1 } };
nested.self = nested;
traverseOnce(nested, console.info);
// nested: { other, self }
// other: { a: 1 }
Вы также можете использовать символ для обозначения пройденных объектов, для этого замените new WeakSet()
на Symbol()
, visited.has(value)
на value[visited]
и visuted.add(value)
на value[visited] = true;
«Что «лучше» должно быть определено в вашем сценарии использования». - Я бы сказал, что подход (Weak)Set лучше во всех случаях, потому что вся необходимая вам очистка - это просто выход из функции, без каких-либо недостатков, которые я вижу.
@amadan Это поддерживает мое мышление. Если функция завершается, WeakSet
должен быть проверен, в то время как символ довольно легкий. С другой стороны, я предполагаю, что флаг будет сохранен на объектах, что означает, что их размер немного увеличивается, и скрытый класс должен быть изменен при добавлении флага. Это, вероятно, преждевременная оптимизация, хотя
@JonasWilms Разве delete visited;
в конце traverseOnce не исправит все это для WeakMap?
@no_boot_device, тем не менее, сборщик мусора срабатывает. Думаю, я слишком много думаю. «он изменяет состояние каждого пройденного объекта»… вы правы. Думаю, я должен перевернуть свой ответ
Сохраните список объектов, к которым вы выполняли рекурсию, а затем проверяйте каждый новый объект по этому списку.
const data = {
foo: {
bar: 1
},
one: 1,
jaz: {
hello: {
x: 1
}
}
};
data.bar = data.foo;
data.foo.foo = data.foo;
data.jaz.hello.foo = data;
function search_for_1() {
const seen = [];
search(data);
function search(object) {
Object.values(object).forEach(value => {
if (typeof value === "object") {
if (seen.includes(value)) {
console.info("Seen this already");
} else {
seen.push(value);
search(value);
}
} else {
if (value === 1) {
console.info("Found 1");
}
}
});
}
}
search_for_1();
Я бы сказал, что поиск WeakSet
гораздо более эффективен, чем поиск по массиву, однако, может ли он использоваться, зависит от среды.
Не изобретайте велосипед. Для таких вещей существуют библиотеки.
Мы используем объектное сканирование для всей обработки данных. Это очень мощно, как только вы обернете вокруг себя голову. Вот как это будет работать для ваших вопросов
// const objectScan = require('object-scan');
const traverse = (data) => objectScan(['**'], {
filterFn: ({ key, value, parent }) => {
// do something here
},
breakFn: ({ isCircular }) => isCircular === true
})(data);
const circular = { name: 'Max', age: 5, sex: undefined, details: { color: 'black', breed: undefined } };
circular.sex = circular;
circular.details.breed = circular;
console.info(traverse(circular));
/* =>
[ [ 'details', 'breed' ],
[ 'details', 'color' ],
[ 'details' ],
[ 'sex' ],
[ 'age' ],
[ 'name' ] ]
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src = "https://bundle.run/[email protected]"></script>
Отказ от ответственности: я автор объектное сканирование
Использовал
WeakSet
просто потому, что, хотя идея Symbol чрезвычайно умна, она изменяет состояние каждого пройденного объекта, что не очень хорошо. Спасибо!