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>