1、常见的内存泄漏案例?
(1)全局变量。如果不小心创建了全局变量,它们会一直存在于内存中,直到程序结束。
(2)闭包。闭包是JavaScript中一个强大的特性,但是如果不正确地使用,它们也会导致内存泄漏。如果一个闭包引用了一个外部变量,那么这个变量就不能被垃圾回收器回收,即使这个闭包不再被使用。
(3)定时器。定时器是一种在指定时间或间隔执行某些操作的机制。如果定时器的回调函数引用了一些对象,那么这些对象就不能被垃圾回收器回收,直到定时器被清除。
(4)脱离DOM的引用。如果一个对象被添加到DOM树中,然后又被移除,但是仍然有一个变量或属性引用它,那么这个对象就不能被垃圾回收器回收,造成内存泄漏。
2、如何避免内存泄漏?
(1)避免使用全局变量。使用var
或let
关键字声明变量,或者将变量封装在对象或函数中。
(2)注意闭包的使用。只在必要的时候使用闭包,避免引用不再需要的外部变量,或者将这些变量设为null。
(3)及时清除定时器。使用clearTimeout
或clearInterval
函数清除不再需要的定时器。
(4)移除对脱离DOM的对象的引用。使用removeChild
或innerHTML
等方法移除DOM元素时,同时将引用它们的变量或属性设为null。
3、什么是回流和重绘?
- 回流是指当页面中的元素的布局、大小、位置等发生变化时,浏览器需要重新计算元素的几何属性,并重新构建渲染树,这个过程叫做回流
- 重绘是指当页面中的元素的外观、颜色、背景等发生变化时,浏览器需要重新绘制元素的样式,这个过程叫做重绘
注:回流和重绘都会影响页面的性能,尤其是回流,因为它涉及到更多的计算和渲染
为了减少或避免页面的回流和重绘,可以采取以下一些方案:
(1)避免频繁操作DOM。可以使用文档碎片(document fragment)或虚拟DOM等技术,将多次DOM操作合并为一次,然后再更新到页面上
(2)避免使用table布局。因为table中的一个元素发生变化,可能会导致整个table的回流
(3)避免使用内联样式。因为内联样式会增加HTML的大小,导致页面加载和解析的时间增加
(4)使用css动画代替JavaScript动画。因为CSS动画可以利用GPU加速,而JavaScript动画则需要浏览器进行计算和渲染
(5)使用transform代替top和left。因为transform不会影响元素的布局,而top和left则会触发回流
4、apply、call、bind三个方法的区别?
- 参数传递方式不同:
apply
、call
的参数传递方式不同,apply
方法的参数需要放在数组中传递,而call
方法的参数需要依次列出。 - 返回值不同:
apply
、call
方法在执行函数时可以直接传递参数,并立即执行函数,而bind
方法只是返回一个绑定了执行上下文的新函数,需要手动调用。 - 直接调用和间接调用方式不同:
apply
、call
方法可以直接调用函数,并将执行上下文改为传递的第一个参数,而bind
方法只能返回一个新函数,需要手动调用。 - 改变执行上下文的方式不同:
apply
、call
方法是临时改变函数执行上下文的方式,而bind
方法是永久绑定函数执行上下文的方式。
举例来说,假设有一个对象 obj
和一个函数 fn
:
const obj = { name: 'John' };
function fn(age) {
console.log(`${this.name} is ${age} years old`);
}
使用 apply
方法调用 fn
函数:
fn.apply(obj, [20]); // "John is 20 years old"
使用 call
方法调用 fn
函数:
fn.apply(obj, [20]); // "John is 20 years old"
使用 bind
方法绑定 fn
函数:
const boundFn = fn.bind(obj);
boundFn(20); // "John is 20 years old"