美团-前端开发面经(六)

点击上方蓝字关注我们





防抖和节流
防抖:
在某个事件触发n秒后执行,如果在事件触发的n秒内又再次触发,则以新的时间为基准
我们可以使用闭包来实现防抖,使用闭包可以实现对函数的复用,也可以避免对全局作用域的污染,这边要注意我们这边在定时器里使用了箭头函数是为了保留返回的闭包的this指向,因为最后事件是指向调用这个闭包的对象的,而不是指向调用用产生闭包的foo函数的对象。(可能有点绕口)
function foo(event,time){
let timer=null
return function(...args){
clearTimeout(timer)
timer=setTimeout(()=>{
event.apply(this,args)
},time)
}
}
function show(){
console.log('111')
}
window.addEventListener('scroll',foo(show,1000))
这个意思就是我们触发了事件都会清空之前的定时器,重新开始计时,这样保证了不管触发在上一次计时范围内或者外都清空计时器并重新计时。
节流:
上面防抖如果我们一直都在触发这个事件,持续触发了很久,但是他也只会执行最后一次,这很显然是不好的,所以我们就有了节流,节流就是不管事件触发的频率有多大,都会在固定的时间间隔内执行一次。
有两种方法可以实现节流
//使用时间戳
function throttle(e,time){
let pre_time=0
return function(...args){
if(Date.now()-pre_time>time){ //超过间隔则执行
pre_time=Date.now()
e.apply(this,args)
}
}
}
//使用定时器
function throttle(e,time){
let timer=null;
return funciton(...args){
if(!timer){
timer=setTimeout(()=>{
timer=null
e.apply(this,args)
},time)
}
}
}


js中let,var和const的区别
在javascript中有三种声明变量的方式:var、let、const。
JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域。块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。
var
var定义的变量可以修改,如果不初始化会输出undefined,不会报错。
var a = 1;
// var a;//不会报错
console.log('函数外var定义a:' + a);//可以输出a=1
function change(){
a = 4;
console.log('函数内var定义a:' + a);//可以输出a=4
}
change();
console.log('函数调用后var定义a为函数内部修改值:' + a);//可以输出a=4
var 声明全局变量,换句话理解就是,声明在for循环中的变量,跳出for循环同样可以使用。
声明在for循环内部的sum,跳出for循环一样可以使用,不会报错正常弹出结果
for(var i=0;i<=1000;i++){
var sum=0;
sum+=i;
}
alert(sum);
let
同一个变量,不可在声明之前调用,必须先定义再使用,否则会报错,循环体中可以用let
let是块级作用域,函数内部使用let定义后,对函数外部无影响。并且let不能定义同名变量,否则会报错。
let c = 3;
console.log('函数外let定义c:' + c);//输出c=3
function change(){
let c = 6;
console.log('函数内let定义c:' + c);//输出c=6
}
change();
console.log('函数调用后let定义c不受函数内部定义影响:' + c);//输出c=3
注意:必须声明'use strict';后才能使用let声明变量否则浏览并不能显示结果,
const
const:用于声明常量,也具有块级作用域 ,也可声明块级。const定义的变量不可以修改,而且必须初始化。
const b = 2;//正确
// const b;//错误,必须初始化
console.log('函数外const定义b:' + b);//有输出值
// b = 5;
// console.log('函数外修改const定义b:' + b);//无法输出
const PI=3.14;
它和let一样,也不能重复定义同一个变量,const一旦定义,无法修改.
let和const属于局部变量,不会出现变量提升的情况,全局定义的let和const变量,不属于顶层变量,不属于window的属性


浏览器的缓存机制
缓存位置
从缓存位置上来说分为四种,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络:
Service Worker
Memory Cache
Disk Cache
Push Cache
网络请求
网络请求
Service Worker
Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的. 如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容
Memory Cache(内存中的缓存)
读取内存中的数据肯定比磁盘快。但是内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。
对于大文件来说,大概率是不存储在内存中的,反之优先
当前系统内存使用率高的话,文件优先存储进硬盘
Disk Cache(硬盘中的缓存)
读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。
Push Cache
Push Cache 是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。并且缓存时间也很短暂,只在会话(Session)中存在,一旦会话结束就被释放。
所有的资源都能被推送,但是 Edge 和 Safari 浏览器兼容性不怎么好
可以推送 no-cache 和 no-store 的资源
一旦连接被关闭,Push Cache 就被释放
多个页面可以使用相同的 HTTP/2 连接,也就是说能使用同样的缓存
Push Cache 中的缓存只能被使用一次
浏览器可以拒绝接受已经存在的资源推送
你可以给其他域名推送资源
缓存策略
查看之前写的Node的Http相关文章
实际场景应用缓存策略
频繁变动的资源
对于频繁变动的资源,首先需要使用 Cache-Control: no-cache 使浏览器每次都请求服务器,然后配合 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。
代码文件
这里特指除了 HTML 外的代码文件,因为 HTML 文件一般不缓存或者缓存时间很短。一般来说,现在都会使用工具来打包代码,那么我们就可以对文件名进行哈希处理,只有当代码修改后才会生成新的文件名。基于此,我们就可以给代码文件设置缓存有效期一年 Cache-Control: max-age=31536000,这样只有当 HTML 文件中引入的文件名发生了改变才会去下载最新的代码文件,否则就一直使用缓存。


扫码二维码
获取更多面经
扶遥就业