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