自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

五個(gè)常見(jiàn)的JavaScript內(nèi)存錯(cuò)誤

開(kāi)發(fā) 前端
JavaScript沒(méi)有提供任何內(nèi)存管理原語(yǔ)。相反,內(nèi)存由JavaScript VM通過(guò)內(nèi)存回收過(guò)程管理。該過(guò)程稱(chēng)為垃圾收集。

JavaScript沒(méi)有提供任何內(nèi)存管理原語(yǔ)。相反,內(nèi)存由JavaScript VM通過(guò)內(nèi)存回收過(guò)程管理。該過(guò)程稱(chēng)為垃圾收集。

[[405938]]

由于我們不能強(qiáng)迫它運(yùn)行,我們?nèi)绾沃浪鼤?huì)正常工作?我們對(duì)此了解了什么?

  • 腳本執(zhí)行在此過(guò)程中暫停。
  • 它釋放內(nèi)存以實(shí)現(xiàn)無(wú)法訪問(wèn)的資源。
  • 這是非確定性的。
  • 它不會(huì)一次性檢查整個(gè)內(nèi)存,但將在多個(gè)周期中運(yùn)行。
  • 這是不可預(yù)測(cè)的。它將在必要時(shí)執(zhí)行。

這是否意味著我們不必?fù)?dān)心資源和內(nèi)存分配?當(dāng)然不是。如果您不小心,您可能會(huì)創(chuàng)建一些內(nèi)存泄漏。

什么是內(nèi)存泄漏?

內(nèi)存泄漏是軟件無(wú)法回收的分配的存儲(chǔ)器。

javascript為您提供垃圾收集過(guò)程并不意味著您可以從內(nèi)存泄漏中安全。為了有資格獲得垃圾收集,必須在其他地方引用對(duì)象。如果您持有對(duì)未使用的資源的引用,則會(huì)阻止這些資源未分配。這被稱(chēng)為無(wú)意的記憶保留。

泄漏內(nèi)存可能導(dǎo)致更頻繁的垃圾收集器運(yùn)行。由于此過(guò)程將阻止腳本運(yùn)行,因此可能會(huì)減慢您的Web應(yīng)用程序。這將使您的表現(xiàn)較少,這將由用戶注意到。它甚至可以導(dǎo)致您的Web應(yīng)用程序崩潰。

我們?nèi)绾畏乐刮覀兊腤eb應(yīng)用程序泄漏內(nèi)存?這很簡(jiǎn)單:通過(guò)避免保留不必要的資源。讓我們看看可能發(fā)生的最常見(jiàn)的場(chǎng)景。

計(jì)時(shí)器監(jiān)聽(tīng)器

讓我們來(lái)看看SetInterval定時(shí)器。它是一個(gè)常用的Web API功能。

“窗口和工作接口提供的setInterval()方法,重復(fù)調(diào)用函數(shù)或執(zhí)行代碼片段,每個(gè)呼叫之間的固定時(shí)間延遲。它返回唯一標(biāo)識(shí)間隔的間隔ID,因此您可以通過(guò)調(diào)用ClearInterval()稍后刪除它。該方法由WindoworWorkerglobalscope Mixin定義。“

- MDN Web Docs

讓我們創(chuàng)建一個(gè)調(diào)用回調(diào)函數(shù)的組件,以發(fā)出x循環(huán)后的完成。我正在為這個(gè)特定的例子做出反應(yīng),但這適用于任何FE框架。

  1. import React, { useRef } from 'react'; 
  2.  
  3. const Timer = ({ cicles, onFinish }) => { 
  4.     const currentCicles = useRef(0); 
  5.  
  6.     setInterval(() => { 
  7.         if (currentCicles.current >= cicles) { 
  8.             onFinish(); 
  9.             return; 
  10.         } 
  11.         currentCicles.current++; 
  12.     }, 500); 
  13.  
  14.     return ( 
  15.         <div>Loading ...</div> 
  16.     ); 
  17.  
  18. export default Timer; 

起初,看起來(lái)沒(méi)有什么是錯(cuò)的。讓我們創(chuàng)建一個(gè)觸發(fā)此計(jì)時(shí)器的組件,并分析其內(nèi)存性能:

  1. import React, { useState } from 'react'; 
  2. import styles from '../styles/Home.module.css' 
  3. import Timer from '../components/Timer'; 
  4.  
  5. export default function Home() { 
  6.     const [showTimer, setShowTimer] = useState(); 
  7.     const onFinish = () => setShowTimer(false); 
  8.  
  9.     return ( 
  10.       <div className={styles.container}> 
  11.           {showTimer ? ( 
  12.               <Timer cicles={10} onFinish={onFinish} /> 
  13.           ): ( 
  14.               <button onClick={() => setShowTimer(true)}> 
  15.                 Retry 
  16.               </button> 
  17.           )} 
  18.       </div> 
  19.     ) 

在重試按鈕上單擊幾次后,這是我們使用Chrome Dev Tools獲得內(nèi)存使用的結(jié)果:

您可以看到在擊中重試按鈕時(shí)分配了越來(lái)越多的內(nèi)存。這意味著分配的先前內(nèi)存并沒(méi)有釋放。間隔計(jì)時(shí)器仍在運(yùn)行而不是被替換。

我們?nèi)绾谓鉀Q這個(gè)問(wèn)題?setInterval的返回是我們可以使用的間隔ID來(lái)取消間隔。在這個(gè)特定的方案中,我們可以在組件上卸載一旦組件才能調(diào)用ClearInterval。

  1. useEffect(() => { 
  2.     const intervalId = setInterval(() => { 
  3.         if (currentCicles.current >= cicles) { 
  4.             onFinish(); 
  5.             return; 
  6.         } 
  7.         currentCicles.current++; 
  8.     }, 500); 
  9.  
  10.     return () => clearInterval(intervalId); 
  11. }, []) 

有時(shí),在代碼審查中發(fā)現(xiàn)這些問(wèn)題很難。最好的做法是創(chuàng)建抽象,您可以管理所有復(fù)雜性。

正如我們?cè)诖耸褂玫姆磻?yīng),我們可以在自定義掛鉤中包裝所有這些邏輯:

  1. import { useEffect } from 'react'; 
  2.  
  3. export const useTimeout = (refreshCycle = 100, callback) => { 
  4.     useEffect(() => { 
  5.         if (refreshCycle <= 0) { 
  6.             setTimeout(callback, 0); 
  7.             return; 
  8.         } 
  9.  
  10.         const intervalId = setInterval(() => { 
  11.             callback(); 
  12.         }, refreshCycle); 
  13.  
  14.         return () => clearInterval(intervalId); 
  15.     }, [refreshCycle, setInterval, clearInterval]); 
  16. }; 
  17.  
  18. export default useTimeout; 

現(xiàn)在,無(wú)論何時(shí)需要使用SetInterval,您都可以執(zhí)行以下操作:

  1. const handleTimeout = () => ...; 
  2. useTimeout(100, handleTimeout); 

現(xiàn)在,您可以使用此USETIMEOUT掛鉤而無(wú)需擔(dān)心內(nèi)存泄露,它都是由抽象管理的。

2. 事件監(jiān)聽(tīng)器

Web API提供了大量的事件偵聽(tīng)器,您可以自己掛鉤。以前,我們覆蓋了settimout?,F(xiàn)在我們將看addeventlistener。

讓我們?yōu)槲覀兊腤eb應(yīng)用程序創(chuàng)建一個(gè)鍵盤(pán)快捷功能。由于我們?cè)诓煌?yè)面上有不同的功能,因此我們將創(chuàng)建不同的快捷函數(shù):

  1. function homeShortcuts({ key}) { 
  2.     if (key === 'E') { 
  3.         console.log('edit widget') 
  4.     } 
  5.  
  6. // user lands on home and we execute 
  7. document.addEventListener('keyup', homeShortcuts);  
  8.  
  9.  
  10. // user does some stuff and navigates to settings 
  11.  
  12. function settingsShortcuts({ key}) { 
  13.     if (key === 'E') { 
  14.         console.log('edit setting') 
  15.     } 
  16.  
  17. // user lands on home and we execute 
  18. document.addEventListener('keyup', settingsShortcuts);  

一切似乎很好,除了我們?cè)趫?zhí)行第二個(gè)AddeventListener時(shí)沒(méi)有清潔先前的鍵。此代碼而不是更換我們的keyup偵聽(tīng)器,而不是更換keyup偵聽(tīng)器。這意味著當(dāng)按下鍵時(shí),它將觸發(fā)兩個(gè)功能。

要清除以前的回調(diào),我們需要使用remove eventListener。讓我們看看代碼示例:

  1. document.removeEventListener(‘keyup’, homeShortcuts); 

讓我們重構(gòu)代碼以防止這種不需要的行為:

  1. function homeShortcuts({ key}) { 
  2.     if (key === 'E') { 
  3.         console.log('edit widget') 
  4.     } 
  5.  
  6. // user lands on home and we execute 
  7. document.addEventListener('keyup', homeShortcuts);  
  8.  
  9.  
  10. // user does some stuff and navigates to settings 
  11.  
  12. function settingsShortcuts({ key}) { 
  13.     if (key === 'E') { 
  14.         console.log('edit setting') 
  15.     } 
  16.  
  17. // user lands on home and we execute 
  18. document.removeEventListener('keyup', homeShortcuts);  
  19. document.addEventListener('keyup', settingsShortcuts); 

作為拇指的規(guī)則,當(dāng)使用來(lái)自全局對(duì)象的工具時(shí),您需要謹(jǐn)慎且負(fù)責(zé)任。

3. 觀察者

觀察者是大量開(kāi)發(fā)人員未知的瀏覽器Web API功能。如果您想檢查HTML元素的可見(jiàn)性或大小的更改,它們是強(qiáng)大的。

讓我們檢查交叉點(diǎn)觀察者API:

“Intersection Observer API提供了一種異步地觀察目標(biāo)元素與祖先元素或頂級(jí)文檔的視口的交叉點(diǎn)的變化。”

- MDN Web Docs

盡可能強(qiáng)大,您需要負(fù)責(zé)任地使用它。完成觀察對(duì)象后,您需要取消監(jiān)視過(guò)程。

讓我們看一些代碼:

  1. const ref = ... 
  2. const visible = (visible) => { 
  3.   console.log(`It is ${visible}`); 
  4.  
  5. useEffect(() => { 
  6.     if (!ref) { 
  7.         return; 
  8.     } 
  9.  
  10.     observer.current = new IntersectionObserver( 
  11.         (entries) => { 
  12.             if (!entries[0].isIntersecting) { 
  13.                 visible(true); 
  14.             } else { 
  15.                 visbile(false); 
  16.             } 
  17.         }, 
  18.         { rootMargin: `-${header.height}px` }, 
  19.     ); 
  20.  
  21.     observer.current.observe(ref); 
  22. }, [ref]); 

上面的代碼看起來(lái)很好。但是,一旦組件未安裝,觀察者會(huì)發(fā)生什么?它不會(huì)被清除,所以你會(huì)泄漏內(nèi)存。我們?cè)鯓硬拍芙鉀Q這個(gè)問(wèn)題?只需使用斷開(kāi)連接方法:

現(xiàn)在我們可以確定,當(dāng)組件卸載時(shí),我們的觀察者將被斷開(kāi)連接。

4. 窗口對(duì)象

將對(duì)象添加到窗口是一個(gè)常見(jiàn)的錯(cuò)誤。在某些情況下,可能很難找到 - 特別是如果您使用窗口執(zhí)行上下文中的此關(guān)鍵字。

讓我們來(lái)看看以下例子:

  1. function addElement(element) { 
  2.     if (!this.stack) { 
  3.         this.stack = { 
  4.             elements: [] 
  5.         } 
  6.     } 
  7.  
  8.     this.stack.elements.push(element); 

它看起來(lái)無(wú)害,但這取決于你調(diào)用一個(gè)addelement的上下文。如果從窗口上下文中調(diào)用AddElement,則會(huì)開(kāi)始查看堆積的項(xiàng)目。

另一個(gè)問(wèn)題可能是錯(cuò)誤地定義全局變量:

  1. var a = 'example 1'; // scoped to the place where var was createdb = 'example 2'; // added to the Window object 

為防止這種問(wèn)題,始終以嚴(yán)格模式執(zhí)行JavaScript:

  1. "use strict" 

通過(guò)使用嚴(yán)格模式,您將暗示您想要保護(hù)自己免受這些類(lèi)型的行為保護(hù)的JavaScript編譯器。當(dāng)您需要時(shí),您仍然可以使用窗口。但是,您必須以明確的方式使用它。

如何影響我們之前的示例的嚴(yán)格模式:

  • 在Addelement函數(shù)上,從全局范圍內(nèi)調(diào)用時(shí),這將是未定義的。
  • 如果您未指定const |左撇子var在變量上,您將收到以下錯(cuò)誤:
    1. Uncaught ReferenceError: b is not defined 

5. 持有DOM參考

DOM節(jié)點(diǎn)也沒(méi)有內(nèi)存泄漏。你需要小心不要抓住他們的參考。否則,垃圾收集器將無(wú)法清除它們,因?yàn)樗鼈內(nèi)匀豢梢缘竭_(dá)。

讓我們看一個(gè)小的代碼示例來(lái)說(shuō)明這個(gè):

  1. const elements = []; 
  2. const list = document.getElementById('list'); 
  3.  
  4. function addElement() { 
  5.     // clean nodes 
  6.     list.innerHTML = ''
  7.  
  8.     const divElementdocument.createElement('div'); 
  9.     const element = document.createTextNode(`adding element ${elements.length}`); 
  10.     divElement.appendChild(element); 
  11.  
  12.  
  13.     list.appendChild(divElement); 
  14.     elements.push(divElement); 
  15.  
  16. document.getElementById('addElement').onclick = addElement

請(qǐng)注意,AddElement函數(shù)清除列表DIV并將新元素添加為子項(xiàng)。此新創(chuàng)建的元素將添加到元素?cái)?shù)組中。

下次執(zhí)行AddElement,將從列表Div中刪除該元素。但是,它不會(huì)有資格獲得垃圾收集,因?yàn)樗鎯?chǔ)在元素?cái)?shù)組中。這使得它可以到達(dá)。這將使您在每個(gè)addelement執(zhí)行上的節(jié)點(diǎn)。

讓我們?cè)趲讉€(gè)執(zhí)行之后監(jiān)視函數(shù):

我們可以在上面的屏幕截圖中看到節(jié)點(diǎn)如何泄露。我們?cè)鯓硬拍芙鉀Q這個(gè)問(wèn)題?清除元素?cái)?shù)組將使它們有資格獲得垃圾收集。

結(jié)論

在本文中,我們已經(jīng)看到了最常見(jiàn)的方法可以泄露。很明顯,JavaScript不會(huì)泄漏內(nèi)存本身。相反,它是由從開(kāi)發(fā)人員側(cè)的無(wú)意的記憶保留引起的。只要代碼整潔,我們就不會(huì)忘記在自己之后清理,不會(huì)發(fā)生泄漏。

了解JavaScript中的內(nèi)存和垃圾收集工作是必須的。一些開(kāi)發(fā)人員獲得虛假印象,因?yàn)樗亲詣?dòng)的,他們不需要擔(dān)心它。

建議在Web應(yīng)用程序上定期運(yùn)行瀏覽器分析器工具。這是唯一能夠肯定沒(méi)有泄漏并留下的方法。Chrome開(kāi)發(fā)人員性能選項(xiàng)卡是開(kāi)始檢測(cè)某些異常的地點(diǎn)。瀏覽問(wèn)題后,您可以通過(guò)拍攝快照并進(jìn)行比較,使用Profiler選項(xiàng)卡深入挖掘它。

有時(shí),我們花費(fèi)時(shí)間優(yōu)化方法,忘記內(nèi)存在我們的Web應(yīng)用程序的性能中播放了一個(gè)很大的部分。

干杯!

原文鏈接:https://betterprogramming.pub/5-common-javascript-memory-mistakes-c8553972e4c2

 

責(zé)任編輯:趙寧寧 來(lái)源: 今日頭條
相關(guān)推薦

2021-12-30 21:51:10

JavaScript開(kāi)發(fā)內(nèi)存

2019-10-14 16:39:50

云計(jì)算配置錯(cuò)誤企業(yè)

2015-07-29 10:46:20

Java錯(cuò)誤

2022-05-31 15:43:15

自動(dòng)化測(cè)試

2020-05-21 18:38:49

JavaScript前端技術(shù)

2022-06-07 12:38:44

云應(yīng)用安全勒索軟件網(wǎng)絡(luò)攻擊

2021-06-28 10:12:34

云計(jì)算云平臺(tái)云計(jì)算架構(gòu)

2021-11-26 05:50:50

Promise JS項(xiàng)目

2019-11-04 05:37:52

SD-WAN軟件定義的廣域網(wǎng)網(wǎng)絡(luò)

2019-06-21 10:13:26

JavaScript錯(cuò)誤開(kāi)發(fā)

2022-12-01 08:00:42

CICD部署

2020-10-19 09:34:04

C語(yǔ)言內(nèi)存錯(cuò)誤編程語(yǔ)言

2020-09-01 07:58:34

API漏洞黑客

2010-07-21 08:51:26

Perl錯(cuò)誤

2022-08-24 14:14:58

JavaScript函數(shù)

2014-03-11 11:33:21

2020-03-20 15:10:09

Python錯(cuò)誤分析代碼

2021-10-19 05:54:58

C語(yǔ)言編程

2013-07-09 15:54:10

VDI虛擬化

2023-09-12 09:47:38

云計(jì)算云管理
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)