前言:
javascript
不提供任何内存管理操作。相反,内存由 javascript vm
通过内存回收过程管理,该过程称为垃圾收集。
既然我们不能强制的垃圾回收,那我们怎么知道它能正常工作?我们对它又了解多少呢?
脚本执行在此过程中暂停
它为不可访问的资源释放内存
它是不确定的
它不会一次检查整个内存,而是在多个周期中运行
它是不可预测的,但它会在必要时执行
这是否意味着无需担心资源和内存分配问题?当然不是。如果我们一不小心,可能会产生一些内存泄漏。
什么是内存泄漏?
内存泄漏是软件长沙学院分数线无法回收的已分配的内存块。
javascript
提供了一个垃圾收集程序,但这并不意味着我们就能避免内存泄漏。为了符合垃圾收集的条件,该对象必须不被其他地方引用。如果持有对未使用的资源的引用,这将会阻止这些资源被回收。这就是所谓的无意识的内存保持。
泄露内存可能会导致垃圾收集器更频繁地一句格言的启示作文运行。由于这个过程会阻止脚本的运行,它可能会让我们程序卡起来,这么一卡,挑剔的用户肯定会注意到,一用不爽了,那这个产品离下线的日子就不完了。更严重可能会让整个应用奔溃,那就gg了。
如何防止内存泄漏? 主要还是我们应该避免保留不必要的资源。来看看一些常见的场景。
tinterval()
方法重复调用函数或执行代码片段,每次调用之间有固定的时间延迟。它返回一个时间间隔id,该id唯一地标识时间间隔,因此您可以稍后通过调用 clearinterval()
来删除它。
我们创建一个组件,它调用一个回调函数来表示它在x个循环之后完成了。我在这个例子中使用react
,但这适用于任何fe框架。
import react, { uref } from 'react'; const timer = ({ cicles, onfinish }) => { const currentcicles = uref(0); tinterval(() => { if (currentcicles.current >= cicles) { onfinish(); return; } currentcicles.current++; }, 500); return ( <div>loading ...</div> ); } export default timer;
一看,好像没啥问题。不急,我们再创建一个触发这个定时器的组件,并分析其内存性能。
import react, { ustate } from 'react'; import styles from '../styles/home.module.css' import timer from '../components/timer'; export default function home() { const [showtimer, tshowtimer] = ustate(); const onfinish = () => tshowtimer(fal); return ( <div classname={styles.container}> {showtimer ? ( <timer cicles={10} onfinish={onfinish} /> ): ( <button onclick={() => tshowtimer(true)}> retry </button> )} </div> ) }
在 retry 按钮上单击几次后,这是使用chrome dev tools
获取内存使用的结果:
当我们点击重试按钮时,可以看到分配的内存越来越多。这说明之前分配的内存没有被释放。计时器仍然在运行而不是被替换。
怎么解决这个问题?tinterval 的返回值是一个间隔 id,我们可以用它来取消这个间隔。在这种特殊情况下,我们可以在组件卸载后调用 clearinterval
。
ueffect(() => { const intervalid = tinterval(() => { if (currentcicles.current >= cicles) { onfinish(); return; } currentcicles.current++; }, 500); return () => clearinterval(intervalid); }, [])
氧化铁与硫酸反应有时,在编写代码时,很难发现这个问题,最好的方式,还是要把组件抽象化。
这里使用的是react
,我们可以把所有这些逻辑都包装在一个自定义的 hook
中。
import { ueffect } from 'react'; export const utimeout = (refreshcycle = 100, callback) => { ueffect(() => { if (refreshcycle <= 0) { ttimeout(callback, 0); return; } const intervalid = tinterval(() => { callback(); }, refreshcycle); return () => clearinterval(intervalid); }, [refreshcycle, tinterval, clearinterval]); }; export default utimeout;
现在需要使用tinterval时,都可以这样做:
const handletimeout = () => ...; utimeout(100, handletimeout);
现在你可以使用这个utimeout hook
,而不必担心内存被泄露,这也是抽象化的好处。
web api
提供了大量的事件监听器。在前面,我们讨论了ttimeout
。现在来看看 addeventlistener
。
在这个事例中,我们创建一个键盘快捷键功能。由于我们在不同的页面上有不同的功能,所以将创建不同的快捷键功能
function homeshortcuts({ key}) { if (key === 'e') { console.log('edit widget') } } // 用户在主页上登陆,我们执行 document.addeventlistener('keyup', homeshortcuts); // 用户做一些事情,然后导航到设置 function ttingsshortcuts({ key}) { if (key === 'e') { console.log('edit tting') } } // 用户在主页上登陆,我们执行 docu烟草属ment.addeventlistener('keyup', ttingsshortcuts);
看起来还是很好,除了在执行第二个 addeventlistener
时没有清理之前的 keyup
。这段代码不是替换我们的 keyup
监听器,而是将添加另一个 callback
。这意味着,当一个键被按下时,它将触发两个函数。
要清除之前的回调,我们需要使用 removeeventlistener :
document.removeeventlistener(‘keyup', homeshortcuts);
重构一下上面的代码:
function homeshortcuts({ key}) { if (key === 'e') { console.log('edit widget') } } // ur lands on home and we execute document.addeventlistener('keyup', homeshortcuts); // ur does some stuff and navigates to ttings function ttingsshortcuts({ key}) { if (key === 'e') { console.log('edit tting') } } // ur lands on home and we execute document.removeeventlistener('keyup', homeshortcuts); document.addeventlistener('keyup', ttingsshortcuts);
根据经验,当使用来自全局对象的工具时,需要灰常小心。
obrvers
是一个浏览器的 web api
功能,很多开发者都不知道。如果你想检查html元素的可见性或大小的变化,这个就很强大了。
interctionobrver
接口 (从属于interction obrver api) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。
尽管它很强大,但我们也要谨慎的使用它。一旦完成了对对象的观察,就要记得在不用的时候取消它。
看看代码:
const ref = ... const visible = (visible) => { console.log(`it is ${visible}`); } ueffect(() => { if (!ref) { return; } obrver.current = new interctionobrver( (entries) => { if (!entries[0].isintercting) { visible(true); } el { visbile(fal); } }, { rootmargin: `-${header.height}px` }, ); obrver.current.obrve(ref); }, [ref]);
上面的代码看起来不错。然而,一旦组件被卸载,观察者会发生什么?它不会被清除,那内存可就泄漏了。
我们怎么解决这个问题呢?只需要使用 disconnect 方法:
const ref = ... const visible = (visible) => { console.log(`it is ${visible}`); } ueffect(() => { if (!ref) { return; } obrver.current = new interctionobrver( (entries) => { if (!entries[0].isintercting) { visible(true); } el { visbile(fal); } }, { rootmargin: `-${header.height}px` }, ); obrver.current.obrve(ref); return () => obrver.current?.disconnect(); }, [ref]);
向 window
添加对象是一个常见的错误。在某些场景中,可能很难找到它,特别是在使用 window execution
上下文中的this关键字。
看看下面的例子:
function addelement(element) { if (!this.stack) { this.stack = { elements: [] } } this.stack.elements.push(element); }
它看起来无害,但这取决于你从哪个上下文调用addelement
。如果你从window context
调用addelement
,那就会越堆越多。
另一个问题可能是错误地定义了一个全局变量:
var a = 'example 1'; // 作用域限定在创建var的地方 b = 'example 2'; // 添加到window对象中
要防止这种问题可以使用严格模式:
"u strict"
通过使用严格模式,向javascript
编译器暗示,你想保护自己免受这些行为的影响。当你需要时,你仍然可以使用window
。不过,你必须以明确的方式使用它。
严格模式是如何影响我们前面的例子:
对于 addelement 函数,当从全局作用域调用时,this 是未定义的如果没有在一个变量上指定const | let | var,你会得到以下错误:uncaught ref问候英文erenceerror: b is not defined
dom节点也不能避免内存泄漏。我们需要注意不要保存它们的引用。否则,垃圾回收器将无法清理它们,因为它们仍然是可访问的。
用一小段代码演示一下:
const elements = []; const list = document.getelementbyid('list'); function addelement() { // clean nodes list.innerhtml = ''; const divelement= document.createelement('div'); const element = document.createtextnode(`adding element ${elements.length}`); divelement.appendchild(element); list.appendchild(divelement); elements.push(divelement); } document.getelementbyid('addelement').onclick = addelement;
注意: addelement 函数清除列表 div,并将一个新元素作为子元素添加到它中。这个新创建的元素被添加到 elements 数组中。
下一次执行 addelement
时,该元素将从列表 div 中删除,但是它不适合进行垃圾收集,因为它存储在 elements
数组中。
我们在执行几次之后监视函数:
在上面的截图中看到节点是如何被泄露的。那怎么解决这个问题?清除 elements
数组将使它们有资格进行垃圾收集。
总结:
在这篇文章中,我们已经看到了最常见的内存泄露方式。很明显,javascript
本身并没有泄漏内存。相反,它是由开发者方面无意的内存保持造成的。只要代码是整洁的,而且我们不忘自己清理,就不会发生泄漏。
了解内存和垃圾回收在javascript
中是如何工作的是必须的。一些开发者得到了错误的意识,认为由于它是自动的,所以他们不需要担心这个问题。
到此这篇关于常见的javascript
内存错误的文章就介绍到这了,更多相关javascript内存错误内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!
本文发布于:2023-04-04 08:55:25,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/e5cdf9a8271a01064fa37865dddc80c2.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:常见的JavaScript内存错误及解决方法.doc
本文 PDF 下载地址:常见的JavaScript内存错误及解决方法.pdf
留言与评论(共有 0 条评论) |