1 分•作者: amelius•6 个月前
返回首页
最新
2 分•作者: Bluestein•6 个月前
1 分•作者: doppp•6 个月前
1 分•作者: ingve•6 个月前
1 分•作者: amusingimpala75•6 个月前
1 分•作者: amansiddhartha•6 个月前
2 分•作者: Letstalk123•6 个月前
1 分•作者: robertnishihara•6 个月前
1 分•作者: arjvik•6 个月前
1 分•作者: Bogdanp•6 个月前
1 分•作者: johntfella•6 个月前
1 分•作者: austinallegro•6 个月前
1 分•作者: helsinkiandrew•6 个月前
2 分•作者: EGreg•6 个月前
我曾经很难理解内存泄漏是从哪里来的,尤其是在 iOS Safari 上。我进入开发者工具 > 时间线标签,看到内存一直在增加,但不知道怎么增加的,也不知道在哪里。所以我写了这个函数来遍历各种软件添加的所有全局对象,避免重复访问同一个对象。这个函数是异步的,这样可以避免占用太多的用户体验。你可以运行它来开始查看引用在哪里泄漏了。
```javascript
Q = {};
Q.globalNames = Object.keys(window); // 快照基线
Q.globalNamesAdded = function () {
const current = Object.keys(window);
const baseline = Q.globalNames;
const added = [];
for (let i = 0; i < current.length; i++) {
if (!baseline.includes(current[i])) {
added.push(current[i]);
}
}
return added;
};
Q.walkGlobalsAsync = function (filterFn, options = {}) {
const seen = new WeakSet();
const found = new Set();
const pathMap = new WeakMap();
const maxDepth = options.maxDepth || 5;
const includeStack = options.includeStack || false;
const logEvery = options.logEvery || 100;
const startingKeys = Q.globalNamesAdded
? Q.globalNamesAdded()
: Object.keys(window);
let totalChecked = 0;
let matchesFound = 0;
function walk(obj, path = 'window', depth = 0) {
if (!obj || typeof obj !== 'object') return;
if (seen.has(obj)) return;
seen.add(obj);
totalChecked++;
if (totalChecked % logEvery === 0) {
console.log(`已检查 ${totalChecked} 个对象,找到 ${matchesFound}`);
}
if (filterFn(obj)) {
found.add(obj);
matchesFound++;
if (includeStack) {
pathMap.set(obj, path);
console.log(`[找到] ${path}`, obj);
} else {
console.log(`[找到]`, obj);
}
}
if (depth >= maxDepth) return;
const skipKeys = obj instanceof HTMLElement
? new Set([
'parentNode', 'parentElement', 'nextSibling', 'previousSibling',
'firstChild', 'lastChild', 'children', 'childNodes',
'ownerDocument', 'style', 'classList', 'dataset',
'attributes', 'innerHTML', 'outerHTML',
'nextElementSibling', 'previousElementSibling'
])
: null;
for (const key in obj) {
if (skipKeys && skipKeys.has(key)) continue;
try {
walk(obj[key], path + '.' + key, depth + 1);
} catch (e) {}
}
}
let i = 0;
function nextBatch() {
const batchSize = 10;
const end = Math.min(i + batchSize, startingKeys.length);
for (; i < end; i++) {
try {
walk(window[startingKeys[i]], 'window.' + startingKeys[i], 0);
} catch (e) {}
}
if (i < startingKeys.length) {
setTimeout(nextBatch, 0); // 安排下一个批次
} else {
console.log(`完成。找到 ${matchesFound} 个被保留的对象。`);
if (includeStack) {
console.log([...found].map(obj => ({
object: obj,
path: pathMap.get(obj)
})));
} else {
console.log([...found]);
}
}
}
nextBatch();
};
```
下面是如何使用它:
```javascript
Q.walkGlobalsAsync(
obj => obj instanceof HTMLElement && !document.contains(obj),
{ includeStack: true, maxDepth: 4, logEvery: 50 }
);
```
但是——请注意,即使你能找到闭包本身,这个方法也**不会**找到被闭包保留的对象,你仍然需要手动检查它们的代码。
41 分•作者: bundie•6 个月前
1 分•作者: zugi•6 个月前
1 分•作者: georgehill•6 个月前
1 分•作者: BhattMayurJ•6 个月前
1 分•作者: petethomas•6 个月前
1 分•作者: JnBrymn•6 个月前