克隆对象,使用 JavaScript 跳过循环引用
Clone an object, skipping circular references with JavaScript
我有这段代码是基于 Crockford 的 cycle.js,但它从输入对象中剥离的内容超出了应有的范围。出于某种原因,它会识别缓存中已经看到的几乎所有内容并跳过它。
我不完全确定 WeakMap 与 Set 的区别是什么,所以这可能是问题的一部分。我试过将两者互换,但似乎没有什么不同。
这是代码。我已经让它足够独立,您应该可以将它放在浏览器控制台中。
const message = (function cloneWithoutCircularReferences(object) {
const cache = new WeakMap();
const clone = Array.isArray(object) ? [] : {};
function getKeyOfObjectByPath(object, path) {
return path.reduce(function(object, key) {
return object[key];
}, clone) || clone;
}
(function traverse(object, path = []) {
try {
for (const [key, value] of Object.entries(object)) {
if (typeof object === "object" && object !== null) {
if (cache.has(object)) {
continue;
}
cache.set(object, path);
getKeyOfObjectByPath(object, path)[key] = Array.isArray(object[key]) ? [] : {};
traverse(value, [...path, key]);
} else {
getKeyOfObjectByPath[key] = value;
}
}
} catch (error) { }
})(object);
return clone;
})(globalThis); // I'm using `globalThis` since it's an easily accessible object with circular references. The actual object I want to decircularize is an array of doubly linked objects.
console.log(message);
这是我尝试去环化的特定对象的屏幕截图。注意 next
键是对双向链表中下一个对象的引用。
此外,subject
指向 window
对象,因此它也将包含一些循环引用。
期望输出:
我最终进行了 breadth-first 搜索,使用 Set 来缓存所见值,并添加了将密钥列入黑名单和限制遍历深度的功能。
我对必须添加这些选项并不感到兴奋,但 children 非常独特,它们可以通过缓存。
(function cloneWithoutCircularReferences(object, options) {
const cache = new Set([window]);
function getKeyOfObjectByPath(object, path) {
return path.reduce(function(object, key) {
return object[key];
}, clone) || clone;
}
const clone = Array.isArray(object) ? [] : {};
for (const queue = [{ "path": [], "node": object }]; queue.length !== 0;) {
const { path, node } = queue.shift();
for (const [key, value] of Object.entries(node)) {
if (typeof value === "object" && value !== null) {
if (cache.has(value) || options.blacklistedKeys.includes(key) || path.length >= options.maxLength) {
continue;
}
cache.add(value);
queue.push({
"path": [...path, key],
"node": value
});
getKeyOfObjectByPath(clone, path)[key] = Array.isArray(object) ? [] : {};
} else {
try {
getKeyOfObjectByPath(clone, path)[key] = value;
} catch (error) { }
}
}
}
return clone;
})(message, options);
我有这段代码是基于 Crockford 的 cycle.js,但它从输入对象中剥离的内容超出了应有的范围。出于某种原因,它会识别缓存中已经看到的几乎所有内容并跳过它。
我不完全确定 WeakMap 与 Set 的区别是什么,所以这可能是问题的一部分。我试过将两者互换,但似乎没有什么不同。
这是代码。我已经让它足够独立,您应该可以将它放在浏览器控制台中。
const message = (function cloneWithoutCircularReferences(object) {
const cache = new WeakMap();
const clone = Array.isArray(object) ? [] : {};
function getKeyOfObjectByPath(object, path) {
return path.reduce(function(object, key) {
return object[key];
}, clone) || clone;
}
(function traverse(object, path = []) {
try {
for (const [key, value] of Object.entries(object)) {
if (typeof object === "object" && object !== null) {
if (cache.has(object)) {
continue;
}
cache.set(object, path);
getKeyOfObjectByPath(object, path)[key] = Array.isArray(object[key]) ? [] : {};
traverse(value, [...path, key]);
} else {
getKeyOfObjectByPath[key] = value;
}
}
} catch (error) { }
})(object);
return clone;
})(globalThis); // I'm using `globalThis` since it's an easily accessible object with circular references. The actual object I want to decircularize is an array of doubly linked objects.
console.log(message);
这是我尝试去环化的特定对象的屏幕截图。注意 next
键是对双向链表中下一个对象的引用。
此外,subject
指向 window
对象,因此它也将包含一些循环引用。
期望输出:
我最终进行了 breadth-first 搜索,使用 Set 来缓存所见值,并添加了将密钥列入黑名单和限制遍历深度的功能。
我对必须添加这些选项并不感到兴奋,但 children 非常独特,它们可以通过缓存。
(function cloneWithoutCircularReferences(object, options) {
const cache = new Set([window]);
function getKeyOfObjectByPath(object, path) {
return path.reduce(function(object, key) {
return object[key];
}, clone) || clone;
}
const clone = Array.isArray(object) ? [] : {};
for (const queue = [{ "path": [], "node": object }]; queue.length !== 0;) {
const { path, node } = queue.shift();
for (const [key, value] of Object.entries(node)) {
if (typeof value === "object" && value !== null) {
if (cache.has(value) || options.blacklistedKeys.includes(key) || path.length >= options.maxLength) {
continue;
}
cache.add(value);
queue.push({
"path": [...path, key],
"node": value
});
getKeyOfObjectByPath(clone, path)[key] = Array.isArray(object) ? [] : {};
} else {
try {
getKeyOfObjectByPath(clone, path)[key] = value;
} catch (error) { }
}
}
}
return clone;
})(message, options);