JavaScript代码优化 | 青训营

134 阅读4分钟

回流

首先介绍回流。回流也叫重排。

触发条件

简单来说,就是当我们对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生回流的过程。比如以下情况:

1、一个 DOM 元素的几何属性变化,常见的几何属性有width、height、padding、margin、left、top、border 等等, 这个很好理解。

2、使 DOM 节点发生增减或者移动。

3、读写 offset族、scroll族和client族属性的时候,浏览器为了获取这些值,需要进行回流操作。

4、调用 window.getComputedStyle 方法。

回流过程

依照上面的渲染流水线,触发回流的时候,如果 DOM 结构发生改变,则重新渲染 DOM 树,然后将后面的流程(包括主线程之外的任务)全部走一遍。

减少DOM操作: 频繁的DOM操作会导致重绘和重新布局,影响性能。建议将多个DOM操作合并为一个操作,或者使用DocumentFragment来批量插入DOM元素。

// 不推荐写法(频繁操作DOM) const container = document.getElementById('container'); for (let i = 0; i < 1000; i++) { const element = document.createElement('div'); element.textContent = 'Item ' + i; container.appendChild(element); } // 推荐写法(合并DOM操作) const container = document.getElementById('container'); const fragment = document.createDocumentFragment(); for (let i = 0; i < 1000; i++) { const element = document.createElement('div'); element.textContent = 'Item ' + i; fragment.appendChild(element); } container.appendChild(fragment);

避免不必要的重绘和回流:

重绘和回流会消耗大量的计算资源。尽量避免在循环中修改样式属性或获取布局信息。如果需要对多个样式进行修改,可以使用CSS的class切换。

// 不推荐写法(频繁触发重绘和回流) const element = document.getElementById('myElement'); for (let i = 0; i < 1000; i++) { element.style.left = i + 'px'; element.style.top = i + 'px'; } // 推荐写法(使用CSS class) const element = document.getElementById('myElement'); element.classList.add('move'); // CSS样式: // .move { // left: 1000px; // top: 1000px; // }

内存管理

内存:由可读写单元组成,是一片可操作空间

管理:人为地区操作一片空间的申请、使用和释放

内存管理:开发者主动申请空间、使用空间、释放空间

管理过程:申请-使用-释放

在Js中,内存空间的申请是自动的,声明一个变量或方法等就会自动申请。内存的使用就是访问变量。可以通过把变量赋值为null来释放这片空间。

JavaScript中的垃圾回收(GC)算法

GC算法:GC是一种机制,就是垃圾回收器如何完成具体的工作,工作内容就是查找垃圾释放空间、回收空间,算法就是工作时查找和回收所遵循的原则。

常见的GC算法:

1.引用计算

原理:判断当前对象身上的引用计数的数值是否为0,来判断为是垃圾,则进行回收

优点:发现垃圾时立即回收,由于回收及时可最大限度减少程序暂停

缺点:无法回收循环引用的对象,由于时刻维护对象的引用数值导致资源开销大

2.标记清除

原理:遍历所有对象标记其中活动对象,再次遍历你所有对象清除未标记对象以及上次的标记

优点:可回收循环引用的对象,

缺点:会导致内存空间碎片化、容易浪费空间、不会立即回收垃圾对象

3.标记整理

原理:遍历所有对象标记其中活动对象,然后对移动对象位置对内存空间进行整理,再清除未标记对象和上次标记

可看作是标记清除算法的增强版,它可以解决内存空间碎片化的问题

优点:减少空间碎片化

缺点:不会立即回收垃圾对象

4.分代回收

详见后面V8引擎的垃圾回收策略

尽量使用源生方法

   由于javaScript是解释性语言,相比编译性语言执行速度要慢,所以尽量使用源生方法,对于浏览器已经实现的方法,就不要去再实现一遍了,加快页面的加载速度。

避免全局查找

为避免全局查找,现在我们一般的做法,都是在函数中将全局对象存储为局部变量来处理,加快访问速度。

尽量减少循环次数

   在代码编写中,循环的使用是不可避免的,但是为了提高代码质量,我们需要做的就是在保证不影响相关功能的前提下,尽量减少循环的使用。因为,少一层循环, 就 能提高数倍的性能。如果要对一个数组的每个元素进行多次操作,尽可能使用一次循环,多次操作,而不是多次循环,每次循环执行一次操作。尤其是在进行多 个正 则匹配的时候,尽可能合并正则表达式,在一次遍历中尽可能找到相应的匹配。