真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

常見的JavaScript內存錯誤有哪些

本文小編為大家詳細介紹“常見的JavaScript內存錯誤有哪些”,內容詳細,步驟清晰,細節(jié)處理妥當,希望這篇“常見的JavaScript內存錯誤有哪些”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

創(chuàng)新互聯(lián)公司從2013年開始,先為東城等服務建站,東城等地企業(yè),進行企業(yè)商務咨詢服務。為東城企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務解決您的所有建站問題。

常見的JavaScript內存錯誤有哪些

JavaScript 不提供任何內存管理操作。相反,內存由 JavaScript VM 通過內存回收過程管理,該過程稱為垃圾收集。

既然我們不能強制的垃圾回收,那我們怎么知道它能正常工作?我們對它又了解多少呢?

  • 腳本執(zhí)行在此過程中暫停

  • 它為不可訪問的資源釋放內存

  • 它是不確定的

  • 它不會一次檢查整個內存,而是在多個周期中運行

  • 它是不可預測的,但它會在必要時執(zhí)行

這是否意味著無需擔心資源和內存分配問題?當然不是。如果我們一不小心,可能會產(chǎn)生一些內存泄漏。

什么是內存泄漏?

內存泄漏是軟件無法回收的已分配的內存塊。

Javascript 提供了一個垃圾收集程序,但這并不意味著我們就能避免內存泄漏。為了符合垃圾收集的條件,該對象必須不被其他地方引用。如果持有對未使用的資源的引用,這將會阻止這些資源被回收。這就是所謂的無意識的內存保持。

泄露內存可能會導致垃圾收集器更頻繁地運行。由于這個過程會阻止腳本的運行,它可能會讓我們程序卡起來,這么一卡,挑剔的用戶肯定會注意到,一用不爽了,那這個產(chǎn)品離下線的日子就不完了。更嚴重可能會讓整個應用奔潰,那就gg了。

如何防止內存泄漏? 主要還是我們應該避免保留不必要的資源。來看看一些常見的場景。

1.計時器的監(jiān)聽

setInterval() 方法重復調用函數(shù)或執(zhí)行代碼片段,每次調用之間有固定的時間延遲。它返回一個時間間隔ID,該ID唯一地標識時間間隔,因此您可以稍后通過調用 clearInterval() 來刪除它。

我們創(chuàng)建一個組件,它調用一個回調函數(shù)來表示它在x個循環(huán)之后完成了。我在這個例子中使用React,但這適用于任何FE框架。

import React, { useRef } from 'react';

const Timer = ({ cicles, onFinish }) => {
    const currentCicles = useRef(0);

    setInterval(() => {
        if (currentCicles.current >= cicles) {
            onFinish();
            return;
        }
        currentCicles.current++;
    }, 500);

    return (
        

Loading ...

    ); } export default Timer;

一看,好像沒啥問題。不急,我們再創(chuàng)建一個觸發(fā)這個定時器的組件,并分析其內存性能。

import React, { useState } from 'react';
import styles from '../styles/Home.module.css'
import Timer from '../components/Timer';

export default function Home() {
    const [showTimer, setShowTimer] = useState();
    const onFinish = () => setShowTimer(false);

    return (
      
          {showTimer ? (
              
          ): (
               setShowTimer(true)}>
                Retry
              
          )}
      

    ) }

Retry 按鈕上單擊幾次后,這是使用Chrome Dev Tools獲取內存使用的結果:

常見的JavaScript內存錯誤有哪些

當我們點擊重試按鈕時,可以看到分配的內存越來越多。這說明之前分配的內存沒有被釋放。計時器仍然在運行而不是被替換。

怎么解決這個問題?setInterval 的返回值是一個間隔 ID,我們可以用它來取消這個間隔。在這種特殊情況下,我們可以在組件卸載后調用 clearInterval。

useEffect(() => {
    const intervalId = setInterval(() => {
        if (currentCicles.current >= cicles) {
            onFinish();
            return;
        }
        currentCicles.current++;
    }, 500);

    return () => clearInterval(intervalId);
}, [])

有時,在編寫代碼時,很難發(fā)現(xiàn)這個問題,最好的方式,還是要把組件抽象化。

這里使用的是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;

現(xiàn)在需要使用setInterval時,都可以這樣做:

const handleTimeout = () => ...;

useTimeout(100, handleTimeout);

現(xiàn)在你可以使用這個useTimeout Hook,而不必擔心內存被泄露,這也是抽象化的好處。

2.事件監(jiān)聽

Web API提供了大量的事件監(jiān)聽器。在前面,我們討論了setTimeout?,F(xiàn)在來看看 addEventListener。

在這個事例中,我們創(chuàng)建一個鍵盤快捷鍵功能。由于我們在不同的頁面上有不同的功能,所以將創(chuàng)建不同的快捷鍵功能

function homeShortcuts({ key}) {
    if (key === 'E') {
        console.log('edit widget')
    }
}

// 用戶在主頁上登陸,我們執(zhí)行
document.addEventListener('keyup', homeShortcuts); 


// 用戶做一些事情,然后導航到設置

function settingsShortcuts({ key}) {
    if (key === 'E') {
        console.log('edit setting')
    }
}

// 用戶在主頁上登陸,我們執(zhí)行
document.addEventListener('keyup', settingsShortcuts);

看起來還是很好,除了在執(zhí)行第二個 addEventListener 時沒有清理之前的 keyup。這段代碼不是替換我們的 keyup 監(jiān)聽器,而是將添加另一個 callback。這意味著,當一個鍵被按下時,它將觸發(fā)兩個函數(shù)。

要清除之前的回調,我們需要使用 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);

根據(jù)經(jīng)驗,當使用來自全局對象的工具時,需要灰常小心。

3.Observers

Observers是一個瀏覽器的 Web API功能,很多開發(fā)者都不知道。如果你想檢查HTML元素的可見性或大小的變化,這個就很強大了。

IntersectionObserver接口 (從屬于Intersection Observer API) 提供了一種異步觀察目標元素與其祖先元素或頂級文檔視窗(viewport)交叉狀態(tài)的方法。祖先元素與視窗(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]);

上面的代碼看起來不錯。然而,一旦組件被卸載,觀察者會發(fā)生什么?它不會被清除,那內存可就泄漏了。我們怎么解決這個問題呢?只需要使用 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'; // 作用域限定在創(chuàng)建var的地方
b = 'example 2'; // 添加到Window對象中

要防止這種問題可以使用嚴格模式:

"use strict"

通過使用嚴格模式,向JavaScript編譯器暗示,你想保護自己免受這些行為的影響。當你需要時,你仍然可以使用Window。不過,你必須以明確的方式使用它。

嚴格模式是如何影響我們前面的例子:

  • 對于 addElement 函數(shù),當從全局作用域調用時,this 是未定義的

  • 如果沒有在一個變量上指定const | let | var,你會得到以下錯誤:

Uncaught ReferenceError: b is not defined

5. 持有DOM引用

DOM節(jié)點也不能避免內存泄漏。我們需要注意不要保存它們的引用。否則,垃圾回收器將無法清理它們,因為它們仍然是可訪問的。

用一小段代碼演示一下:

const elements = [];
const list = document.getElementById('list');

function addElement() {
    // clean nodes
    list.innerHTML = '';

    const pElement= document.createElement('p');
    const element = document.createTextNode(`adding element ${elements.length}`);
    pElement.appendChild(element);


    list.appendChild(pElement);
    elements.push(pElement);
}

document.getElementById('addElement').onclick = addElement;

注意,addElement 函數(shù)清除列表 p,并將一個新元素作為子元素添加到它中。這個新創(chuàng)建的元素被添加到 elements 數(shù)組中。

下一次執(zhí)行 addElement 時,該元素將從列表 p 中刪除,但是它不適合進行垃圾收集,因為它存儲在 elements 數(shù)組中。

我們在執(zhí)行幾次之后監(jiān)視函數(shù):

常見的JavaScript內存錯誤有哪些

在上面的截圖中看到節(jié)點是如何被泄露的。那怎么解決這個問題?清除 elements  數(shù)組將使它們有資格進行垃圾收集。

讀到這里,這篇“常見的JavaScript內存錯誤有哪些”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


新聞標題:常見的JavaScript內存錯誤有哪些
網(wǎng)頁路徑:http://weahome.cn/article/psjsgh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部