给大家推荐一个实用面试题库
1、前端面试题库 ( 面试必备)推荐:★★★★★
地址:web前端面试题库
前言
为什么要做性能优化?性能优化到底有多重要? 性能优化是为了提供更好的用户体验 、加快网站加载速度 、提高搜索引擎排名 、节省服务器资源 、适应多种设备和网络环境等方面的需求。通过不断优化性能,可以提高用户满意度、增加网站流量提高业务效果。
同时性能优化是把双刃剑,有好的一面也有坏的一面。好的一面就是可以能提升网站性能,坏的一面就是配置多,代码复杂,或者要遵守的规则太多。并且某些性能优化规则并不适用所有场景,所以也并不是一味的追求性能优化,而是需要谨慎使用。
防抖和节流 是JavaScript
中常用的两种性能优化方式。面试中我们也会经常碰到。它们的作用是减少函数的执行次数,以提高代码的性能。
在进行窗口的resize、scroll、输出框内容校验等操纵的时候,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常之差。那么为了前端性能的优化也为了用户更好的体验,就可以采用防抖(debounce)和节流(throttle)的方式来到达这种效果,减少调用的频率。
防抖(Debounce)
1. 情景
-
有些场景事件触发的频率过高(
mousemove
、onkeydown
、onkeyup
、onscroll
),例如文本编辑器实时保存,当无任何更改操作一秒后进行保存;调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖。 -
回调函数执行的频率过高也会有卡顿现象。 可以一段时间过后进行触发去除无用操作
2. 防抖原理
一定在事件触发 n 秒后才执行,如果在一个事件触发的 n 秒内又触发了这个事件,以新的事件的时间为准,n 秒后才执行,等触发事件 n 秒内不再触发事件才执行。
官方解释:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。(ps:个人理解这就相当于游戏中的回城键,只有读秒到规定时间点才会触发回城,期间任意时间打断都会重新计时。)
3. 防抖函数简单实现
javascript
//简单的防抖函数
function debounce(func, wait, immediate) {
//定时器变量
var timeout;
return function () {
//每次触发scrolle,先清除定时器
clearTimeout(timeout);
//指定多少秒后触发事件操作handler
timeout = setTimeout(func, wait);
};
};
//绑定在scrolle事件上的handler
function handlerFunc() {
console.log('Success');
}
//没采用防抖动
window.addEventListener('scroll', handlerFunc);
//采用防抖动
window.addEventListener('scrolle', debounce(handlerFunc, 1000));
4. 防抖函数演化过程
(1)this event绑定问题
javascript
//以闭包的形式返回一个函数,内部解决了this指向的问题,event对象传递的问题
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
var args = arguments;
clearTimeout(timeout)
timeout = setTimeout(function () {
func.apply(context, args)
}, wait);
};
};
(2) 立即触发问题
javascript
//首次触发执行,再次触发以后开始执行防抖函数
//使用的时候不用重复执行这个函数,本身返回的函数才具有防抖功能
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
if(timeout) clearTimeout(timeout);
// 是否在某一批事件中首次执行
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function() {
timeout = null;
func.apply(context, args)
immediate = true;
}, wait);
if (callNow) {
func.apply(context, args)
}
immediate = false;
} else {
timeout = setTimeout(function() {
func.apply(context, args);
immediate = true;
}, wait);
}
}
}
(3)返回值问题
javascript
function debounce(func, wait, immediate) {
var timeout, result;
return function () {
var context = this, args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function() {
result = func.apply(context, args)
}, wait);
if (callNow) result = func.apply(context, args);
} else {
timeout = setTimeout(function() {
result = func.apply(context, args)
}, wait);
}
return result;
}
}
(4)取消防抖,添加cancel
方法
javascript
function debounce(func, wait, immediate) {
var timeout, result;
function debounced () {
var context = this, args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function() {
result = func.apply(context, args)
}, wait);
if (callNow) result = func.apply(context, args);
} else {
timeout = setTimeout(function() {
result = func.apply(context, args)
}, wait);
}
return result;
}
debounced.cancel = function(){
cleatTimeout(timeout);
timeout = null;
}
return debounced;
}
节流(Throttle)
1.情景
- 图片懒加载
- ajax数据请求加载
2.节流原理
如果持续触发事件,每隔一段时间只执行一次函数,控制了事件发生的频率。
官方解释:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。(s:个人理解这就相当于游戏中的射击,就算你高频点击,子弹只会在固定时间段内射出。)
3.节流实现-时间戳和定时器
- 时间戳
javascript
var throttle = function (func, delay) {
var prev = Date.now()
return function () {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
- 定时器
javascript
var throttle = function (func, delay) {
var timer = null;
return function () {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function () {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000))
4.节流函数的演化过程
- 时间戳触发
javascript
//在开始触发时,会立即执行一次; 如果最后一次没有超过 wait 值,则不触发
function throttle(func, wait) {
var context, args;
var previous = 0; // 初始的时间点,也是关键的时间点
return function() {
var now = +new Date();
context = this;
args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
- 定时器触发
javascript
//首次不会立即执行,最后一次会执行,和用时间戳的写法互补。
function throttle(func, wait){
var context, args, timeout;
return function() {
context = this;
args = arguments;
if(!timeout) {
timeout = setTimeout(function(){
func.apply(context, args);
timeout = null;
}, wait);
}
}
}
- 结合时间戳和定时器
javascript
function throttle(func, wait) {
var timer = null;
var startTime = Date.now();
return function(){
var curTime = Date.now();
var remaining = wait-(curTime-startTime);
var context = this;
var args = arguments;
clearTimeout(timer);
if(remaining<=0){
func.apply(context, args);
startTime = Date.now();
}else{
timer = setTimeout(fun, remaining); // 如果小于wait 保证在差值时间后执行
}
}
}
总结
防抖 :原理是维护一个定时器,将很多个相同的操作合并成一个。规定在delay后触发函数,如果在此之前触发函数,则取消之前的计时重新计时,只有最后一次操作能被触发。
节流 :原理是判断是否达到一定的时间来触发事件。某个时间段内只能触发一次函数。
区别:防抖只会在最后一次事件后执行触发函数,节流不管事件多么的频繁,都会保证在规定时间段内触发事件函数。
给大家推荐一个实用面试题库
1、前端面试题库 ( 面试必备)推荐:★★★★★
地址:web前端面试题库