記憶體洩漏 發生在程式碼中對記憶體的分配後,該記憶體不再被使用,但未被回收,導致記憶體資源浪費。
以下是常見的記憶體洩漏問題以及查資料後的對應解決方法整理。
在 DOM 元素上添加事件監聽器,但在元素不再需要時沒有移除這些監聽器,可能會導致記憶體洩漏,因為監聽器持有對 DOM 元素的引用。
解決方法
在不需要時移除事件監聽器,或使用自動化工具檢測未解除的監聽器。
function addEvent() {
const button = document.getElementById('myButton');
function handleClick() {
console.log('Button clicked!');
}
button.addEventListener('click', handleClick);
}
// 當你不再需要該事件監聽器時應該移除它
function removeEvent() {
const button = document.getElementById('myButton');
function handleClick() {
console.log('Button clicked!');
}
button.removeEventListener('click', handleClick);
}
閉包可能意外地保持對其外部作用域的引用,這樣會導致外部作用域中的變數無法被垃圾回收。
解決方法
小心設計閉包,避免不必要的引用。
function createClosure() {
let largeArray = new Array(1000000).fill('memory leak');
return function() {
console.log('Closure is still holding on to the large array');
};
}
const myClosure = createClosure(); // `largeArray` 不會被回收
全局變數在變數在整個應用程序的生命周期中都存在所以可以訪問,也因為可能長時間存在於內存中,導致記憶體洩漏。
// 不建議使用全局變數
const globalVariable = 'I am global';
function createLocal() {
const localVariable = 'I am local';
console.log(globalVariable); // `globalVariable` 是全局變數
}
解決方法
盡量避免使用全局變數,將變數封裝在局部作用域中,像函式作用域內,或是 ES6 module。
(function() {
const localConst = 'I am local';
// 這樣的變數只在函式作用域內可見
})();
// myModule.js
const moduleVar = 'I am module scoped';
export function getModuleVar() {
return moduleVar;
}
因為我目前使用 Vue 3 Composition API,框架提供了更好的模塊化和組織方式避免全局變數,並更好地管理變數和狀態。
// Vue 3 Composition API example
<script setup>
import { ref } from 'vue';
const localVar = ref('I am local');
function doSomething() {
console.log(localVar.value);
}
</script>
如果創建了定時器 setInterval 或異步回調 setTimeout,但是不再需要時沒有清理它們,這些回調函數可能會持續存在,佔用記憶體。
let timer = setInterval(() => {
console.log('Running...');
}, 1000);
// 沒有停止定時器,可能導致記憶體洩漏
// clearInterval(timer); // 應該在適當的時候停止定時器
解決方法
在不再需要定時器或回調函數時,使用 clearInterval
或其他清理方法來停止它們。
規劃第 25 天會分享 clean_code 在職場上的運用,其中有一個部分是函式的單一作用性,並且提取重複代碼,將重複的代碼提取到共用 function 或 ES module 中!
職場上很常遇到一進頁面就要呼叫 api 然後把 response 開始做邏輯轉換渲染在頁面上,然而有些回傳內容其實是不會改變的,此時可以使用 memoization 等技術減少呼叫 api 可以節省效能以及邏輯轉換時間!
// 不建議
function getData() {
// 每次調用都進行計算
return expensiveCalculation();
}
// 改進版
const cache = new Map();
function getData() {
if (!cache.has('data')) {
cache.set('data', expensiveCalculation());
}
return cache.get('data');
}