作为一种脚本语言,JavaScript 的效率有时并不能满足开发者和用户的要求。但随着电子设备的不断更新换代,处理器的性能确实是在不断增长的。有时,程序卡顿的原因可能并非设备运行速度太慢,而是某些功能的实现不太合理。通过对代码进行分析,并应用一些技术进行改进,这些问题通常都能得到解决。
重绘和重排
如果对一个元素的样式进行修改,那很可能会涉及到重绘与重排。
重绘即对元素的外观进行更新。对于重绘而言,元素的排布应该没有发生变化,修改的可能是元素的边框、背景、文字颜色等等。通常,当元素需要传递的信息发生变化时,通常就需要对元素进行重绘。
重排即对元素的排布进行修改。如果修改了元素的大小、布局方式或是添加、移除了元素,都可以看作发生了重排。当浏览器窗口大小被调整、功能菜单展开/收起,或是表格内容需要更新时,通常就需要对元素进行重排。
通过 JavaScript 操作 DOM 是低效的。如果可能,还是尽量减少通过 JavaScript 对页面进行操作。合适的 CSS 属性也能做到响应式布局,且开销通常比 JavaScript 的实现更小。如果使用主流的前端框架,也能受益于框架提供的虚拟 DOM 等特性,先对元素需要的操作进行计算,再用最小的代价将更改反映到页面上。
节流和防抖
节流和防抖都是节省资源的有效技术。他们的原理和实现方式都是类似的,即在一定时间内触发多次事件时忽略大部分事件,只对少数事件进行响应。通常,频繁对事件进行响应都是没有必要的。因此,节流和防抖引入的时间限制能节省大量的资源,同时并不会对程序造成太大影响。
节流是指一段时间内只允许开始一次操作。如果在这段时间内重复要求开始操作,程序都不会作出响应。节流最典型的应用场景是短信验证码发送功能。我们并不希望用户过于频繁地要求发送短信。虽然这并不会对程序的性能造成太大的影响,但可能会给用户带来不必要的干扰,给平台带来不必要的开销。
对于 JavaScript 程序,节流可以应用于列表刷新等触发重绘和重排的功能。短时间反复刷新的开销就不小,且刷新前后的列表不一定有新变化。当数据量足够大时,每次刷新程序都会冻结,重复刷新造成的卡顿是肉眼可见的。此时,节流也就显得十分重要了。
下方代码实现了一个简单的节流操作。
function throttle(fn, delay) {
let timer = null;
return function () {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
}
}
防抖是指一段时间内只响应最后一次操作。如果在这段时间内多次请求开始操作,则只有最后一次操作生效。
对于浏览器缩放窗口,在用户按住鼠标拖动的过程中会不断触发窗口大小缩放事件。但如果用节流的方式对此类事件的响应进行限制,可能最后浏览器窗口的状态和程序内记录的并不一致,这会导致实际呈现的效果与预期有一定差距。但防抖则会在最后对事件进行响应,因此更接近窗口最后的状态,呈现效果也理应更好,同时也避免了反复触发重绘与重排。
下方代码实现了一个简单的防抖操作。
function debounce(fn, delay) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
}
}
受益于节流和防抖技术,实际触发的事件次数大幅减少,可以预见程序的性能也将得到不小的提升,尤其是在处理相关事件的函数较为复杂的情况下。