JS面试真题 part5
JS面试真题 part5
- 21、说说对事件循环的理解
- 22、JavaScript本地存储方式有哪些?区别及应用场景?
- 23、大文件上传如何断点续传?
- 24、ajax原理是什么?如何实现?
- 25、什么是防抖和节流?有什么区别?如何实现
21、说说对事件循环的理解
自己回答:
js是单线程运行机制,事件分为同步事件和异步事件,就涉及到了运行时机的问题。同步任务执行完之后,会轮询异步事件队列,检查异步事件队列里是否有待执行任务。异步事件任务队列又分为宏任务队列和微任务队列,当微任务队列没有事件时,才去执行宏任务队列。源码里是没有宏任务这个概念,宏任务是一个口头概念
标准回答:
首先,JavaScript
是一门单线程的语言,意味着同一时间内只能做一件事,但这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环
在JavaScript
中,所有的任务都可以分为 同步任务和异步任务
同步任务和异步任务的运行流程图如下:
同步任务进入主线程,异步任务进入任务队列,主线程内的任务执行完毕,就会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就是事件循环。
异步任务分为宏任务和微任务
常见的宏任务有:
- script
- setTimeout/setInterval
- UI rendering/UI事件
- postMessage、MessageChannel
- setImmediate、I/O(Node.js)
常见的微任务有:
- Promise.then
- MutaionObserver
- process.nextTick(Node.js)
事件循环、宏任务、微任务的关系如图所示:
流程分析:
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
分析:
1、遇见console.log('script start')
,打印,输出script start
2、遇见定时器,加入宏任务列表
3、执行函数async1,输出async1 start
,async2
,后面的 console.log('async1 end')
加入微任务
4、执行promise1
,.then加入微任务列表
5、执行script end
6、执行微任务列表,输出async1 end
,promise2
7、执行宏任务,打印settimeout
22、JavaScript本地存储方式有哪些?区别及应用场景?
自己回答:
JavaScript本地存储方式:localS、indexDB、cookie
localS:内存少(5M?),存一些关键信息
indexDB:离线缓存方案,内存大,类似前端数据库
cookie:浏览器缓存,4kb,存一些token
标准回答:
主要讲四种
- cookie
- sessionStorage
- localStorage
- indexedDB
cookie:网站为了辨别用户身份而储存在用户本地终端上的数据,为了解决HTTP
无状态导致的问题。一般不超过4kb,它由一个名称(Name)、一个值(Value)和其他几个用于控制cookie
有效期、安全性、使用范围的可选属性组成。cookie在每次请求中都会被发送,如果不使用https对其加密,其保存的信息很容易被窃取,导致安全风险。
localStorage:h5新方法,IE8及以上浏览器都兼容
特点:
- 生命周期:
持久化
的本地存储,除非主动删除数据,否则数据是永远不会过期的 - 存储的信息在同一域中是共享的
- 大小:5M(与浏览器有关系)
- localStorage 本质上是对字符串的读取,如果存储内容多的话,会消耗内存空间,导致页面变卡。
- 受同源策略的限制
sessionStorage:sessionStorage
和localStorage
使用方法基本一致,唯一不同的是生命周期,一旦页面(会话)关闭,sessionStorage
将会删除数据
indexedDB:一个本地数据库,存储更大量的结构化数据。
优点:储存量理论上没有上限;所有操作是异步的,比localStorage同步操作性能更高,尤其是数据量较大时;原生支持储存JS的对象;是正经的数据库,数据库能干都能干
缺点:操作非常繁琐;有一定门槛
区别:
关于 cookie、sessionStorage、localStorage三者的区别主要如下:
- 存储大小:
cookie
数据大小不能超过4k
,sessionStorage
和localStorage
虽然也有存储大小的限制,但是比cookie
大得多,可以达到5M或更大 - 有效时间:
localStorage
存储持久数据,浏览器关闭后数据不丢失除非主动删除数据。sessionStorage
数据在当前浏览器窗口关闭后自动删除。cookie
设置的cookie
过期时间之前一直有效,即使窗口或浏览器关闭 - 数据与服务器之间的交互方式,
cookie
的数据会自动的传递到服务器,服务器也可以写cookie
到客户端;sessionStorage
和localStorage
不会自动把数据发给服务器,仅在本地保存
应用场景
- 标记用户与跟踪用户行为的情况,推荐使用cookie
- 适合长期保存在本地的数据(令牌),推荐使用localStorage
- 敏感账号一次性登录,推荐使用sessionStorage
- 存储大量数据的情况,在线文档(富文本编辑器)保存编辑历史的情况,推荐使用indexedDB
23、大文件上传如何断点续传?
自己回答:
文件分批次上传,每个批次添加标记是否上传
标准回答:
分片上传:就是将所要上传的文件,按照一定的大小,将整个文件分割成多个数据块来进行分片上传,上传之后再由服务端对所有上传的文件进行汇总整合成原始文件
流程:
1、将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
2、初始化一个分片上传任务,返回本次分片上传唯一标识;
3、按照一定的策略(串行或并行)发送各个分片数据块;
4、发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件;
断点续传:指在下载或上传时,将下载或上传任务人为的划分为几个部分
每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没必要从头开始上传下载。用户可以节省时间,提高速度。
一般实现方式有两种
- 服务器端返回,告知从哪开始
- 浏览器自行处理
上传过程中将文件在服务器写为临时文件,等全部写完了(文件上传完),将此临时文件重命名为正式文件即可。
如果中途上传中断过,下次上传的时候根据当前临时文件大小,作为在客户端读取文件的偏移量,从此位置继续读取文件数据块,上传到服务器从此偏移量继续写入文件
实现思路:
整体思路比较简单,拿到文件,保存文件唯一标识,切割文件,分段上传,每次上传一段,根据唯一标识判断文件上传进度,直到文件的全部片段上传完毕
使用场景:
- 大文件加速上传:当文件大小超过预期大小时,使用分片上传可实现并行上传多个Part,以加快上传速度
- 网络环境较差:建议使用分片上传,当出现上传失败的时候,仅需重传失败的Part
- 流式上传:可以在需要上传的文件大小还不确定的情况下开始上传。这种场景在视频监控等行业应用这比较常见
24、ajax原理是什么?如何实现?
自己回答:
对js的XmlHttpRequest的封装
标准回答:
AJAX
全称(Async Javascript and XML)
即异步的Javascript
和XML
,是一种创建交互式网页应用的网页开发技术,可以在不重新加载整个网页的情况下,与服务器交换数据,并且更新部分网页
AJAX
的原理简单来说通过XmlHttpRequest
对象来向服务器发异步请求,从服务器获得数据,然后用Javascript
来操作DOM
而更新页面
流程图如下:
实现过程:看这里浏览器请求详解(ajax、fetch、axios使用,手写ajax)
25、什么是防抖和节流?有什么区别?如何实现
自己回答:
防抖:是一段时间内防止多次点击,第一次点击后,会设置一个间隔时间,在间隔时间内点击的会重新开始间隔时间,典型应用:搜索框输入
节流:是多次重复事件,只对一次事件进行处理,典型应用:监听滚动条
标准回答:
防抖: n秒后再执行该事件,若在n秒内被重复触发,则重新计时
重置计时器:每次事件触发时,都会重置计时器。
执行时机:只有在用户停止触发事件指定时间间隔后,才会执行最后一次事件。
节流: n秒内只运行一次(第一次),若在n秒内重复触发,只有一次生效
单次执行:在时间间隔内只执行一次事件处理函数。
忽略后续触发:在时间间隔内,后续的事件触发将被忽略。
实现:
防抖:
// 创建一个防抖函数,它返回一个新的函数,该新函数在指定的 wait 时间后执行 func
function debounce(func, wait) {
// 保存定时器的引用
let timeout;
// 返回的函数是用户实际调用的函数,它包含了防抖逻辑
return function(...args) {
// 保存当前的 this 上下文
const context = this;
console.log(context);
// 清除之前的定时器,如果存在
if (timeout) clearTimeout(timeout);
// 设置一个新的定时器
// 当指定的 wait 时间过后,将执行 func 函数
// 并将当前的 this 上下文和参数传入
timeout = setTimeout(function() {
// 执行原始函数,绑定正确的 this 上下文和参数
func.apply(context, args);
}, wait);
};
}
- 当防抖函数被触发时,首先会检查是否已经存在一个timeout(即是否有一个定时器在运行)。
- 如果存在,表示之前有触发过防抖函数但还未执行func,此时使用clearTimeout清除之前的定时器。
- 然后,设置一个新的timeout,如果在wait指定的时间内再次触发防抖函数,之前的定时器会被清除并重新设置,这意味着func的执行会被不断推迟。
- 只有当指定的时间间隔wait内没有再次触发防抖函数时,timeout才会到达,此时会执行原始函数func,并且使用apply方法将存储的context和args传递给它。
节流:
function throttle(func, limit) {
let inThrottle = false;
return function(...args) {
const context = this; // 保存当前的 this 上下文
if (!inThrottle) {
// 执行传入的函数
func.apply(context, args);
inThrottle = true; // 标记为正在节流
// 使用闭包和 setTimeout 来在指定的延迟后重置 inThrottle
setTimeout(() => {
inThrottle = false; // 重置节流状态
}, limit);
}
};
}
- func:需要被节流的函数。
- limit:表示在指定的时间间隔后,func才能再次被执行的时间(以毫秒为单位)。
- inThrottle:一个布尔值,用来标记func是否处于可执行状态。
- context:保存当前的this上下文,确保在执行func时this指向正确。
- args:使用扩展运算符…来收集所有参数,以便将它们传递给func。
- setTimeout:在指定的limit时间后执行,将inThrottle重置为false,这样func就可以在下一次调用时被执行了。
总结:
相同点:
- 实现机制:两者都可以通过JavaScript的setTimeout函数实现,利用时间延迟来控制回调函数的执行。
- 性能优化:它们的共同目的都是减少函数的执行频率,以此来提高程序的性能,避免不必要的计算资源消耗。
不同点:
执行时机:
- 防抖(Debounce):确保在指定的时间间隔结束后执行一次函数。如果在这段时间内多次触发事件,则只有最后一次事件会在延迟后执行函数。
- 节流(Throttle):确保在指定的时间间隔内最多执行一次函数。无论在这段时间内触发了多少次事件,只有第一次事件会立即执行函数。
应用场景:
- 防抖:适用于搜索框输入、表单验证等场景,用户完成输入后,才执行相关操作。
- 节流:适用于滚动事件、按钮点击等,需要在连续事件中合理控制执行频率的场景。
触发逻辑:
- 防抖:关注一段时间内的连续触发,但只对最后一次操作做出响应。
- 节流:在一段时间内,无论触发多少次事件,只响应一次。
分辨技巧:
- 如果您希望在一系列快速操作结束后只执行一次函数,那么使用防抖。
- 如果您希望在一系列快速操作中合理控制函数的执行频率,那么使用节流。