简体   繁体   English

有没有办法在 JavaScript 中遍历一个可能自包含的对象?

[英]Is there a way to traverse a possibly-self-containing object in JavaScript?

I want to descend an object in Javascript looking for a specific string.我想在 Javascript 中下降一个对象以寻找特定的字符串。 Unfortunately, this object is built in such a way that it'd be impossible to simply use the source and Ctrl-F for that string, and it's also built in such a way that recursive functions trying to descend it risk getting trapped inside of it forever.不幸的是,这个对象的构建方式是不可能简单地使用源代码和 Ctrl-F 来表示该字符串,而且它的构建方式也使得递归函数试图降低它有被困在其中的风险永远。

Basically, this object contains itself.基本上,这个对象包含它自己。 Not just once, but in very many areas.不只是一次,而是在很多领域。 I cannot simply say "exclude these keys", as the object is obfuscated and therefore we'd be here all day listing keys, and once we were done we wouldn't have looked at all the data.我不能简单地说“排除这些键”,因为对象被混淆了,因此我们整天都在这里列出键,一旦完成,我们就不会查看所有数据。
As well, I need to be able to descend __proto__ and prototype , as useful strings are hidden in there too.同样,我需要能够下降__proto__prototype ,因为有用的字符串也隐藏在那里。 (But only for functions and objects.) (但仅适用于函数和对象。)

While I'd prefer something along the lines of findStuff(object, /string/ig) , that may be hard, so any function that simply has areas clearly marked that the control flow falls to once it's found specific objects (function, string, etc.)虽然我更喜欢findStuff(object, /string/ig) ,但这可能很难,所以任何简单地具有明确标记的区域的函数,一旦找到特定对象(函数、字符串、等等。)

Thank you, and sorry for such a pain in the butt question.谢谢你,很抱歉在这个问题上这么痛苦。


Edit: In case it helps, I'm trying to traverse a compiled Construct2 runtime object.编辑:如果有帮助,我正在尝试遍历已编译的 Construct2 运行时对象。 I'm not going to post the full thing here as it's not going to fit in any pastebin no matter how forgiving, and also I don't want to accidentally post resources I don't have the permission to provide.我不会在这里发布完整的内容,因为无论多么宽容,它都不适合任何粘贴箱,而且我不想意外发布我无权提供的资源。 (Don't worry though, I'm not trying to pirate it myself, I'm simply trying to figure out some user-facing functionality) (不过别担心,我不是想自己盗版,我只是想找出一些面向用户的功能)

You could use a WeakSet to keep track of the objects that were already traversed:您可以使用 Wea​​kSet 来跟踪已经遍历过的对象:

 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);
 }

Through the WeakSet you got O(1) lookup time, and are also sure that this will never leak.通过 WeakSet,您获得了 O(1) 的查找时间,并且还确保这永远不会泄漏。

Usable as:可用作:

 const nested = { other: { a: 1 } };
 nested.self = nested;

 traverseOnce(nested, console.log);
 // nested: { other, self }
 // other: { a: 1 }

You could also use a Symbol to flag traversed objects, for that replace new WeakSet() with Symbol() , visited.has(value) with value[visited] and visuted.add(value) with value[visited] = true;您还可以使用 Symbol 来标记遍历的对象,为此将new WeakSet()替换为Symbol()visited.has(value)替换为value[visited]visuted.add(value)替换为value[visited] = true;

Any time you're traversing a potentially cyclical object, keeping a memo of already traversed objects and breaking if you've seen the current object before is a standard technique.每当您遍历一个潜在的循环对象时,保留已遍历对象的备忘录,如果您以前看过当前对象,则将其中断是一种标准技术。 You can use Set to do so.您可以使用Set来执行此操作。

Keep a list of objects you have recursed into, and then check each new object against that list.保留您递归访问的对象列表,然后根据该列表检查每个新对象。

 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.log("Seen this already"); } else { seen.push(value); search(value); } } else { if (value === 1) { console.log("Found 1"); } } }); } } search_for_1();

Don't reinvent the wheel There are libraries for this kind of stuff.不要重新发明轮子有这类东西的库。

We use object-scan for all our data processing.我们使用对象扫描进行所有数据处理。 It's very powerful once you wrap your head around it.一旦你把头环绕在它周围,它就非常强大。 Here is how it would work for your questions这是它如何解决您的问题

 // 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.log(traverse(circular)); /* => [ [ 'details', 'breed' ], [ 'details', 'color' ], [ 'details' ], [ 'sex' ], [ 'age' ], [ 'name' ] ] */
 .as-console-wrapper {max-height: 100% !important; top: 0}
 <script src="https://bundle.run/object-scan@13.8.0"></script>

Disclaimer : I'm the author of object-scan免责声明:我是对象扫描的作者

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM