js考题-高级篇(1)
于2024-02-20 11:49:39发布
79
1. 说一下浏览器的缓存策略
可以从2个方面来说
一方面是人为定义的:强缓存(本地缓存)和弱缓存(协商缓存)
强缓存:不发起请求,直接用缓存里的数据。
弱缓存:发起请求,由服务端决定是否使用缓存,如果内容没变化,则服务端返回304,浏览器就用缓存里的内容
另一方面则是http协议里,规定的缓存策略
缓存字段:
1. cache-control
指定多少秒之后过期
2. expires
指定某个日期后过期,假设设置过期时间是2023年9月16日,则浏览器会获取系统时间,查看是否过期。因为获取的是系统时间,所以不推荐使用
3. etag、if-none-match
文件指纹。客户端请求a资源,服务端把a资源以及a资源对应的文件指纹返回给客户端,客户端把指纹保存起来。下次又请求a资源,则把文件指纹传递给服务端,服务端对比后,若指纹未发生改变,则
返回304状态码,浏览器则继续使用缓存里的内容。否则返回最新的内容以及最新的文件指纹给客户端
4. last-modified、if-modified-since
最后修改时间。和上面的文件指纹类似
下面通过一个案例,来讲解下last-modified、if-modified-since的使用。
有一个电商网站,有一个手机商品,数据如下 :
const phone = {
price: 500, // 价格
updateTime: '2024/02/21 21:54:22', // 最后修改时间
updateUser: '小李', // 修改者
......
}
此时,小明作为一名前端,他需要去获取这个手机的数据,所以他发起了ajax请求。不出问题,他拿到了这个手机的数据。然后小明把数据渲染到页面上。
这时候,小李跑来告诉他,因为获取手机数据的接口需要调用很多别的接口,最后通过组装才能把数据给到小明。所以为了提升用户的访问速度,此时小李给了一个方案给小明。
当手机的数据没有发生改变的时候,则用缓存。否则用接口返回的数据。这个过程是这样的 :
1、小明发起ajax请求,在请求头中携带一个属性If-Modified-Since,它的值是updateTime。(值是小李给小明的,若是首次发起ajax请求,这个值是null)
2、小李接受到请求后,他会先干一件事,就是去数据库获取这个资源的最后修改时间。然后对比2个时间
3、若这2个时间不相等,则小李在响应头中设置一个属性Last-Modified,它的值是最新的updateTime。并且返回最新的手机数据。小明则把响应头中的updateTime以及最新的手机数据保存在localStorage中,然后在把数据渲染到页面中。
4、这时候小明关掉页面,然后又打开那个手机的页面,又再次触发第1、2步骤
5、这时候如果小李没有修改过手机的数据信息,那么updateTime是不会变的,所以当小明再次访问这个手机的页面时,小李对比到这2个时间是一致的,则直接返回304状态码。
6、小明发现接口返回304状态码后,则直接从localStorage中取之前存的数据即可。
案例: https://gitee.com/tainiu123/cache
tips:调试时候,需要把该选项去掉
2. 为什么0.1 + 0.2 != 0.3?如何让它等于0.3?
十进制转二进制、二进制转十进制
图解
小数转二进制,则不是用上面的规则。如 0.1转成十进制是
0.1 * 2 = 0.2 ---- 0
0.2 * 2 = 0.4 ----- 0
0.4 * 2 = 0.8 ----- 0
0.8 * 2 = 1.6 ----- 1
0.6 * 2 = 1.2 ----- 1
0.2 * 2 = 0.4 ----- 0
所以0.1的二进制是一个无线循环小数,0.0001 1001 1001 1001
为什么0.1 + 0.2 != 0.3?
因为计算机在计算数值的时候,是会把数值转换成二进制来计算的。
0.1和0.2转换成二进制是一个无线循环小数,它们分别是:
0.1的二进制 :0.0001 1001 1001 ...
0.2的二进制 :0.0011 0011 0011 ...
又因为计算机的存储大小是有限制的,所以存储一个无限循环小数,小数部分的精度只能保存到52位。超过52位的会被舍去。
但是舍去之前,需要看53位的数字是0还是1,如果是0,则直接舍去,如果是1,则逢二进一。
所以上面的0.1和0.2的二进制,保留52位的值应该是 :
0.1的二进制:0.0001100110011001100110011001100110011001100110011001101 // 可以用0.1.toString(2)得到0.1的二进制
0.2的二进制:0.001100110011001100110011001100110011001100110011001101 // 可以用0.2.toString(2)得到0.2的二进制
相加的结果 :0.0100110011001100110011001100110011001100110011001100111 // 转成10进制的方式是从右边第一位开始,用第1位乘以2的0次方 + 第2位乘以2的1次方 + 第3位乘以2的2次方...(如上面的图片中的“二转十)”
十进制结果 :0.300000000000000044408920958...
十进制最终的结果会默认最终17位小数,所以0.1 + 0.2 = 0.30000000000000004
如何让它等于0.3?
知道0.1和0.2的二进制是循环小数才导致的这个问题后,就很好解决。只要不是小数就没事了。如 :
(0.1 * 100 + 0.2 * 100) / 100 = 0.3
接: https://www.bchrt.com/tools/binary-calculator/
3. 虚拟列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
.app {
height: 500px;
width: 500px;
border: 1px solid red;
box-sizing: border-box;
margin: 0 auto;
overflow: auto;
}
.item {
height: 50px;
box-sizing: border-box;
border-bottom: 1px solid blue;
}
</style>
</head>
<body>
<div id="app" class="app" @scroll="sfun">
<div class="ctx" :style="{height: ctxH}">
<ul ref="ctx">
<li class="item" v-for="(item, key) in showData" :key="key">{{ item }}</li>
</ul>
</div>
</div>
<script src="./vue.min.js"></script>
<script>
new Vue({
el: '#app',
data() {
return {
tableData: [], // 原始数据
showData: [], // 展示的数据
pageSize: 10, // 最多展示的条数
ctxH: 0, // ctx的高度
itemHeight: 50, // item的高度
}
},
methods: {
getList() {
// 模拟生成数据
for (let i = 1; i <= 1000; i++) {
this.tableData.push(i);
}
// 设置展示的数据
this.showData = this.tableData.slice(0, this.pageSize)
// 设置ctx的高度,不设置高度的话就没有滚动条了
this.ctxH = this.tableData.length * this.itemHeight + 'px'
},
sfun(e) {
const start = Math.ceil(e.target.scrollTop / this.itemHeight)
const end = start + this.pageSize
this.showData = this.tableData.slice(start, end)
this.$refs.ctx.style.transform = `translateY(${start * this.itemHeight}px)`
}
},
created() {
this.getList()
}
})
</script>
</body>
</html>