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

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

我想下降 Javascript 中的对象以查找特定字符串。不幸的是,这个对象的构建方式不可能简单地为该字符串使用源和 Ctrl-F,而且它的构建方式使得试图下降它的递归函数有被困在其中的风险永远。

基本上,这个对象包含它自己。不只是一次,而是在很多领域。我不能简单地说 "exclude these keys",因为对象被混淆了,因此我们会整天在这里列出密钥,一旦完成,我们就不会查看所有数据。
同样,我需要能够下降 __proto__prototype,因为有用的字符串也隐藏在其中。 (但仅适用于函数和对象。)

虽然我更喜欢 findStuff(object, /string/ig) 中的一些东西,但这可能很难,所以任何函数只要有明确标记的区域,一旦找到特定对象(函数、字符串),控制流就会下降等)

谢谢你,很抱歉这个问题让你这么痛苦。


编辑:如果有帮助,我正在尝试遍历已编译的 Construct2 运行时对象。我不会 post 这里的全部内容,因为无论多么宽容,它都不适合任何 pastebin,而且我不想不小心 post 我没有的资源允许提供。 (不过别担心,我不是想自己盗版,我只是想找出一些面向用户的功能)

您可以使用 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);
 }

通过 WeakSet 你得到了 O(1) 的查找时间,并且也确信这永远不会泄漏。

可用作:

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

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

您还可以使用符号来标记遍历的对象,因为将 new WeakSet() 替换为 Symbol(),将 visited.has(value) 替换为 value[visited],将 visuted.add(value) 替换为 value[visited] = true;

任何时候你正在遍历一个潜在的循环对象,保留一个已经遍历的对象的备忘录,如果你以前见过当前对象就中断是一种标准技术。您可以使用 Set 来执行此操作。

保留您递归到的对象列表,然后根据该列表检查每个新对象。

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

不要重新发明轮子这种东西有图书馆。

我们使用 object-scan 进行所有数据处理。一旦你全神贯注,它就会非常强大。以下是它如何解决您的问题

// 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>

免责声明:我是object-scan

的作者