我在理解内存泄漏的来源时遇到了很大的困难,尤其是在 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 }
);
```
但是请注意,这不会找到被闭包保留的对象,即使你能找到闭包本身,你仍然需要手动检查它们的代码。
返回首页
最新
嗨,HN,
我是Raj Guru Yadav,一名来自印度的16岁自学开发者。在过去的三年里,我完成了700多个编程项目,涵盖了从简单的JS游戏和用户界面仪表板到人工智能工具、语音助手,甚至是一个基于《巴哈伐德·吉塔》所有700首颂歌训练的问答系统。
我没有明确的学习路线图,只是根据自己的好奇心开始构建项目——有时一天就能完成一个项目,有时则会对一个想法进行十次迭代。
以下是一些亮点:
- 使用HTML/CSS/JS构建了一个可玩的飞行棋游戏
- 制作了一个智能机器人,通过语音解决数学问题并回答问题
- 创建了一个带有霓虹用户界面的电子书仪表板,并支持本地身份验证
- 用印地语和英语训练了一个关于《巴哈伐德·吉塔》的人工智能
- 构建了按班级管理学生的系统,支持本地存储和图表
所有项目都托管在GitHub、CodePen以及一些个人实验中。
我学到的东西:
- 开始时不需要一个“完美”的想法,直接开始就好。
- 重复是强大的。你构建的第12个计算器将比前5个的总和更好。
- 记录和分享。这样可以建立信心和社区。
- 项目是学习真实开发的最佳方式——比单纯的教程更有效。
我分享这些并不是为了炫耀,而是想说:
如果你年轻(或刚起步),不要过于思考。直接去构建。这个过程教给你的东西比结果更重要。
如果你有任何问题或想要分享代码片段,欢迎随时问我!
Raj
[https://github.com/Rajyadav81](https://github.com/Rajyadav81)
你好,HN,
Stripe 的发票费用悄然增加——每笔交易 0.4%(每张发票最高 $2)——而真正的成本来自于每次客户要求更改时需要手动定制发票的工作。
这就是我创建 <a href="https://envilo.app" rel="nofollow">https://envilo.app</a> 的原因:自动化的 Stripe 发票处理(适用于一次性付款和订阅),并提供自助编辑功能。您的客户可以预览、编辑和下载他们的发票——消除了来回沟通,节省了您的时间和 Stripe 费用。
为什么还要继续为账单支付过高的费用?
- 无需每张发票的费用
- 不需要手动编辑或代码
- 客户自行处理更改
免费试用 7 天——无需信用卡 → <a href="https://envilo.app" rel="nofollow">https://envilo.app</a>
对于其他 Stripe 用户:您希望发票处理能做得更好的是什么?让我们讨论一下!