五个常见的JavaScript内存错误(1)

useEffect(() => {

const intervalId = setInterval(() => {

if (currentCicles.current >= cicles) {

onFinish();

return;

}

currentCicles.current++;

}, 500);

return () => clearInterval(intervalId);

}, [])

有时,在编写代码时,很难发现这个问题,最好的方式,还是要把组件抽象化。

这里使用的是React,我们可以把所有这些逻辑都包装在一个自定义的 Hook 中。

import { useEffect } from ‘react’;

export const useTimeout = (refreshCycle = 100, callback) => {

useEffect(() => {

if (refreshCycle <= 0) {

setTimeout(callback, 0);

return;

}

const intervalId = setInterval(() => {

callback();

}, refreshCycle);

return () => clearInterval(intervalId);

}, [refreshCycle, setInterval, clearInterval]);

};

export default useTimeout;

现在需要使用setInterval时,都可以这样做:

const handleTimeout = () => …;

useTimeout(100, handleTimeout);

现在你可以使用这个useTimeout Hook,而不必担心内存被泄露,这也是抽象化的好处。

2.事件监听

Web API提供了大量的事件监听器。在前面,我们讨论了setTimeout。现在来看看 addEventListener。

在这个事例中,我们创建一个键盘快捷键功能。由于我们在不同的页面上有不同的功能,所以将创建不同的快捷键功能

function homeShortcuts({ key}) {

if (key === ‘E’) {

console.log(‘edit widget’)

}

}

// 用户在主页上登陆,我们执行

document.addEventListener(‘keyup’, homeShortcuts);

// 用户做一些事情,然后导航到设置

function settingsShortcuts({ key}) {

if (key === ‘E’) {

console.log(‘edit setting’)

}

}

// 用户在主页上登陆,我们执行

document.addEventListener(‘keyup’, settingsShortcuts);

看起来还是很好,除了在执行第二个 addEventListener 时没有清理之前的 keyup。这段代码不是替换我们的 keyup 监听器,而是将添加另一个 callback。这意味着,当一个键被按下时,它将触发两个函数。

要清除之前的回调,我们需要使用 removeEventListener :

document.removeEventListener(‘keyup’, homeShortcuts);

重构一下上面的代码:

function homeShortcuts({ key}) {

if (key === ‘E’) {

console.log(‘edit widget’)

}

}

// user lands on home and we execute

document.addEventListener(‘keyup’, homeShortcuts);

// user does some stuff and navigates to settings

function settingsShortcuts({ key}) {

if (key === ‘E’) {

console.log(‘edit setting’)

}

}

// user lands on home and we execute

document.removeEventListener(‘keyup’, homeShortcuts);

document.addEventListener(‘keyup’, settingsShortcuts);

根据经验,当使用来自全局对象的工具时,需要灰常小心。

3.Observers

Observers 是一个浏览器的 Web API功能,很多开发者都不知道。如果你想检查HTML元素的可见性或大小的变化,这个就很强大了。

IntersectionObserver接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。

尽管它很强大,但我们也要谨慎的使用它。一旦完成了对对象的观察,就要记得在不用的时候取消它。

看看代码:

const ref = …

const visible = (visible) => {

console.log(`It is ${visible}`);

}

useEffect(() => {

if (!ref) {

return;

}

observer.current = new IntersectionObserver(

(entries) => {

if (!entries[0].isIntersecting) {

visible(true);

} else {

visbile(false);

}

},

{ rootMargin: `-${header.height}px` },

);

observer.current.observe(ref);

}, [ref]);

上面的代码看起来不错。然而,一旦组件被卸载,观察者会发生什么?它不会被清除,那内存可就泄漏了。我们怎么解决这个问题呢?只需要使用 disconnect 方法:

const ref = …

const visible = (visible) => {

console.log(`It is ${visible}`);

}

useEffect(() => {

if (!ref) {

return;

}

observer.current = new IntersectionObserver(

(entries) => {

if (!entries[0].isIntersecting) {

visible(true);

} else {

visbile(false);

}

},

{ rootMargin: `-${header.height}px` },

);

observer.current.observe(ref);

return () => observer.current?.disconnect();

}, [ref]);

4. Window Object

向 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对象中

要防止这种问题可以使用严格模式:

“use strict”

通过使用严格模式,向JavaScript编译器暗示,你想保护自己免受这些行为的影响。当你需要时,你仍然可以使用Window。不过,你必须以明确的方式使用它。

严格模式是如何影响我们前面的例子:

  • 对于 addElement 函数,当从全局作用域调用时,this 是未定义的

  • 如果没有在一个变量上指定const | let | var,你会得到以下错误:

Uncaught ReferenceError: b is not defined

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)


后记


总结一下这三次面试下来我的经验是:

  1. 一定不要死记硬背,要理解原理,否则面试官一深入就会露馅!

  2. 代码能力一定要注重,尤其是很多原理性的代码(之前两次让我写过Node中间件,Promise.all,双向绑定原理,被虐的怀疑人生)!

  3. 尽量从面试官的问题中表现自己知识的深度与广度,让面试官发现你的闪光点!

  4. 多刷面经!

我把所有遇到的面试题都做了一个整理,并且阅读了很多大牛的博客之后写了解析,免费分享给大家,算是一个感恩回馈吧,有需要的朋友【点击我】免费获取。祝大家早日拿到自己心怡的工作!

篇幅有限,仅展示部分内容



我写过Node中间件,Promise.all,双向绑定原理,被虐的怀疑人生)!

  1. 尽量从面试官的问题中表现自己知识的深度与广度,让面试官发现你的闪光点!

  2. 多刷面经!

我把所有遇到的面试题都做了一个整理,并且阅读了很多大牛的博客之后写了解析,免费分享给大家,算是一个感恩回馈吧,有需要的朋友【点击我】免费获取。祝大家早日拿到自己心怡的工作!

篇幅有限,仅展示部分内容



  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值