什么是闭包?
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域。
简言之,闭包 = 内层函数 + 被函数引用的外部变量
通常会使用一个函数包裹闭包结构,起一个变量保护作用
闭包的作用是使变量私有化,防止外部调用、修改
return:当需要对闭包内的变量进行调用,可以使用return,但仍不可修改
闭包可能会造成内存泄漏的问题
function fn(){
let count = 1
function fun(){
count++
console.log("函数被调用了${count}次")
}
return fun
}
const result = fn()
result()
result()
在这一段代码中,我们定义了一个全局变量result,全局变量使用后不会被销毁,result调用fn函数,fn函数使用fun函数,fun函数使用count,所以count变量就会一直存在不会被销毁,就造成了内存泄漏的问题。
什么是内存泄漏?
在JavaScript中,内存泄漏是指程序分配的内存由于某种原因没有被正确释放,导致这些不再需要的内存无法被回收,从而占用系统资源,最终可能导致程序性能下降甚至崩溃。内存泄漏通常发生在不再使用的对象或数据仍然被引用时,导致垃圾回收器无法将其清除。
1.意外的全局变量:如果变量声明时漏掉了 var
、let
或 const
,它会被意外地创建为全局变量,并且一直存在于全局作用域中。
function foo() {
bar = 'This is a global variable'; // 应该使用 let 或 var 声明
}
2.闭包:闭包使内部函数可以访问外部函数的变量,但如果没有正确管理,闭包会导致不必要的变量保持引用,无法被垃圾回收。
function outer() {
let obj = { name: 'memory leak' };
return function inner() {
console.log(obj);
};
}
let leak = outer();
3.被遗忘的定时器或回调:当 setInterval
或 setTimeout
没有被清除时,函数仍然在内存中保留引用,导致内存泄漏。
let timer = setInterval(() => {
console.log('This will leak memory if not cleared');
}, 1000);
// 没有使用 clearInterval(timer) 来清除定时器
4.DOM 引用未被清理:当一个 DOM 元素(比如一个按钮或图片)被从页面中删除时,通常它会被浏览器的垃圾回收机制处理,释放其占用的内存资源。然而,如果 JavaScript 代码中仍然有对这个元素的引用,比如保存在变量里,那么即便该元素已经从页面中删除了,内存也无法被释放。这会导致内存泄漏,因为垃圾回收机制认为该对象仍然被使用。
let element = document.getElementById('myElement'); // 获取 DOM 元素
document.body.removeChild(element); // 从页面中移除该元素
// 尽管从页面中移除了,但 element 变量仍然引用着该 DOM 对象
在这种情况下,即使该元素从页面上消失了,但因为 element
变量仍然持有这个 DOM 节点的引用,它的内存就不会被回收。如果有很多类似的引用未被清理,内存消耗会逐渐增加。
为了避免这种问题,我们可以在删除 DOM 元素后,手动清理对它的引用:
let element = document.getElementById('myElement');
document.body.removeChild(element);
element = null; // 手动解除引用,允许垃圾回收器回收内存
5.事件监听器未被移除:当给 DOM 元素绑定了事件监听器(如点击事件),如果该元素被删除但事件监听器没有被移除,浏览器仍然会在内存中保留这个事件监听器的引用,导致该元素无法被完全释放。这也是一种常见的内存泄漏场景。
let button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
});
// 后来这个按钮被移除了,但监听器没有移除
document.body.removeChild(button);
即使按钮被删除了,JavaScript 依然记得它的事件监听器,这意味着与按钮相关的内存仍然被占用。
在删除 DOM 元素之前,应该手动移除所有绑定的事件监听器:
let button = document.getElementById('myButton');
let handleClick = () => {
console.log('Button clicked!');
};
button.addEventListener('click', handleClick);
// 在删除按钮之前,先移除事件监听器
button.removeEventListener('click', handleClick);
document.body.removeChild(button);
预防内存泄漏的措施
- 避免使用未声明的全局变量。
- 使用
clearInterval
和clearTimeout
清除不再需要的定时器。 - 确保在不再需要时手动移除事件监听器。
- 小心处理闭包,确保没有不必要的引用。
- 定期检查程序中是否有无用的引用,可以使用浏览器的开发者工具监控内存使用情况
什么是DOM?
概念:DOM(文档对象模型,Document Object Model)是一个跨平台、语言中立的接口,它将 HTML、XML 文档结构化表示为一个树形结构。通过 DOM,程序可以动态地访问和操作文档的内容、结构以及样式。它允许 JavaScript 等编程语言与网页进行交互,改变页面元素、属性、事件等。
DOM 就像是网页的“地图”或“骨架”,它将网页的所有元素(如按钮、文本、图片)组织成一棵树。这棵树上每一个元素都是一个“节点”,开发者可以通过 JavaScript 去找到这些节点,然后对它们做各种操作,比如修改文本内容、添加新的元素或删除不需要的部分。DOM 让网页变得“活起来”,使得我们可以通过代码动态修改网页。