哔哩哔哩前端面试题

文章目录

1. vue的diff算法和数据劫持,进一讲你的理解
vue的diff算法
  1. diff算法基本原理:

    • 比较新旧节点差异:diff算法首先对比新旧虚拟Dom树的节点,找出他们的差异
    • 生成差异操作(patch): 然后,diff算法会生成一组操作(也成为patch),这些操作可以将旧的虚拟DOM树转变成新的虚拟DOM树。
    • 应用差异操作更新DOM树:最后,这些操作被应用到实际的DOM树上,从而更新视图。
  2. diff算法的实现方法:

    • 使用递归遍历的方式:发现节点不存在,则标记为删除;发现新节点,标记为添加;发现节点存在但内容改变,标记为更新。

    • vue采用了多种策略来优化diff算法,采用了原地复用、按key值比较、采用双端队列等方式,提高了性能,减少不必要的DOM操作。

  3. diff算法局限性和注意事项:

    • 复制的嵌套结构

    • 对于组件内部的更新,使用vue 的watch 监听可以更高效处理

    • 注意应当正确使用key值

数据劫持
  1. vue采用了数据劫持的方式来实现响应式数据绑定。当你将普通的Javascript对象传递给Vue示例作为data选项时,Vue就会遍历这个对象的所有属性,并使用Object.defineProperty方法将他们转成getter和setter。
  2. 这样一来,当你修改了这些属性的值,vue就能监测到变化,并自动更新相关视图,这就是vue的响应式数据绑定的基本原理。
2. 项目中的分片上传,如何实现的?(简历中的项目)
  1. 分片上传:将一个大文件分成多个小块,然后将这些小块逐个上传至服务器的一种上传方式。

  2. 一般文件大于5M为大文件。

    img

3. 如果用户上传两个一样的文件,一个文件上传上半段,一个文件上传下半段,如何拼起来优化,而不是直接返回上传失败?
  • 创建文件标识
  • 服务器端临时存储文件,而不是立刻进行拼接
  • 当所有文件上传后再进行拼接
  • 校验文件完整性
  • 返回上传成功信息
4. 瀑布流如何实现的长列表?
5. 瀑布流布局中,你是如何判断谁是底部元素的?

在瀑布流布局中,判断哪些元素是底部元素的方法通常涉及到计算每列的高度,然后比较它们的高度来确定。一般来说,当新元素要添加到最短列时,它就成为底部元素。

以下是一种简单的方法来判断底部元素:

  1. 计算列的高度:首先,需要计算每列的高度,这可以通过遍历每个列中的元素,并累加它们的高度来实现。在实际编码中,可以创建一个数组来存储每列的高度。

  2. 找到最短列:通过比较列的高度,找到最短的列。这意味着该列是当前最适合添加新元素的列,因为它的高度最小。

  3. 将新元素添加到最短列:将新元素添加到最短列中,并更新该列的高度。

  4. 判断是否为底部元素:如果新元素被添加到了最短列,那么它就是底部元素。

    // 获取所有列及其高度 column是所有子元素的类名
    var columns = document.querySelectorAll('.column');
    var columnHeights = [];
    
    columns.forEach(function(column) {
        columnHeights.push(column.offsetHeight);
    });
    
    // 找到最短列的索引
    var shortestColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));
    
    // 新元素将要添加到的列
    var bottomColumn = columns[shortestColumnIndex];
    
    // 判断是否为底部元素
    if (bottomColumn === event.target.parentElement) {
        console.log("This is a bottom element.");
    }
    
6. 如果一次只请求10条数据,前端只展示了5条数据,如何判断底部元素的交叉状态?
响应式怎么做的?
7. 如何通过bootstrape实现的?
8. 怎么监听移动端设备?
9. bootsreape底层怎么实现的?
10. 讲一讲浏览器缓存?

浏览器缓存:是指在访问网页时,将一些数据保存在本地的过程,提高网页加载速度和用户体验。

  1. HTTP缓存:HTTP缓存是通过HTTP协议来控制的,主要分为强缓存和协商缓存。
    浅谈 强制缓存/协商缓存 怎么用? - 掘金

    • 强缓存:浏览器在请求资源时,首先判断本地缓存是否存在有效的缓存副本,如果存在且未过期,则直接使用缓存副本,不发送请求到服务器。常见的强缓存控制头:Cache-Control和Expires.
    • 协商缓存:当强缓存失效时,浏览器会向服务器发送请求,服务器根据请求头中携带的信息来判断资源是否有更新。如果资源未发生变化,服务器返回304状态码,通知浏览器使用本地缓存,如果资源已经更新,服务器返回新的资源内容。常见的协商缓存控制头有 Last-ModifiedETag
  2. LocalStorage和SessionStorage:本地存储,将数据保存至浏览器

  • LocalStorage:保存的数据在浏览器关闭后仍然存在,除非手动清除或者使用js清除。
// 注意:移除特定的localStorage数据项

//方式一、
localStorage.removeItem('yourKey');

//方式二:直接f12键,点到选择"Application"选项卡,展开"LocalStorage"一栏,右键选择要清除的项目,选择"Clear"即可清除
  • SessionStorage:保存的数据仅在当前会话中有效,浏览器关闭数据清除。
  1. Cookie:存储少量的用户信息,与特定的域名相关联。可以设置Cookie的过期时间,控制信息的有效期。
  2. Service Worker缓存:Service Worker 是运行在浏览器背后的脚本,可以拦截网络请求并且缓存资源,实现离线访问,提高浏览速度。
11. 详细说一说Cookie?使用场景?

cookie和session:cookie和session都是用来跟踪浏览器用户身份的会话方式。

区别:

1.保持状态:cookie保存在本地浏览器端(俗称:客户端),session保存在服务器端

2.使用方式:

cookie机制

  • cookie机制:如果不在浏览器中设置过期时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消失。

  • Cookie不能进行跨域

  • cookie是服务器发给客户端的特殊信息,cookie是以文本的方式保存在客户端,每次请求时都带上它

cookie机制 应用场景:

  1. 保存用户登录状态。判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
  2. 保存上次登录的时间等信息。
  3. 跟踪用户行为。通过cookie记住用户操作行为,当下次再打开该页面时,自动默认该行为。比如说:不同用户所在地区的天气情况只要设置一次,下次打开就是之前设置的地方的天气。或者说网站的换肤功能等。
  4. 浏览计数
12. sessionStorage中你所说的不能在所有同源窗口中共享是什么意思?多个tab栏不可以共享?浏览器同源策略是什么?会话级别的储存方式的会话是什么意思?

sessionStorage是一种会话级别的储存方式,它与当前会话(即当前浏览器标签页或窗口)相关联。当用户在浏览器中打开一个页面时,会话开始;当用户关闭该页面时,会话结束。在同一个会话中,不同页面之间可以共享sessionStorage中的数据,但是不能在不同会话(不同浏览器标签页或窗口)之间共享数据。

换句话说,sessionStorage中的数据只能在同一个浏览器标签页或窗口中共享,而不能跨越不同的浏览器标签页或窗口。如果用户在同一浏览器中打开了多个标签页,每个标签页都拥有自己独立的sessionStorage,它们之间不能共享数据。

这与浏览器的同源策略相关。同源策略是一种安全机制,限制了来自不同源(域、协议、端口)的页面之间的交互。在同源策略下,一个浏览器标签页只能访问和操作属于同一源的数据,而不能访问其他源的数据。因此,sessionStorage中的数据也受到同源策略的限制,只能在同一源的页面之间共享。

综上所述,会话级别的储存方式中的“会话”指的是用户在浏览器中打开的一个会话期间,即从用户打开浏览器开始到用户关闭浏览器为止。在同一个会话期间,sessionStorage中的数据可以在同一浏览器标签页或窗口中共享,但不能跨越不同的会话(不同浏览器标签页或窗口)。

13. 讲一讲get和post的区别?
  • get和post都是网络请求的方法,post安全性更高
  1. get请求
    • 用来请求数据的,参数附在url中,显示在地址栏以明文的显示参数信息,容易被中间人窃取。
  2. post 请求
    • 一般用来提交数据,参数放在请求体中,不会暴露在地址栏,在请求抓包时,相对于get请求更难获取到明文数据,提升了数据的安全性。
14. 因为讲到了资源浪费,如果post携带简单的参数转为get通过url传会不会造成资源的浪费?到底什么情况下用post和get?

使用 POST 方法将简单参数转换为 GET 方法通过 URL 传递可能会导致一些资源浪费和安全风险,这取决于你的具体情况和需求。

首先,让我们来看一下 POST 和 GET 方法的区别:

  • GET 方法:通常用于请求获取服务器上的某个资源,参数会附加在 URL 的末尾,以查询字符串的形式传递。GET 请求可以被缓存,可以被书签保存,但是传输的数据量有限制(由浏览器和服务器设定,通常不超过 2048 个字符),因此适合传递少量的非敏感信息,比如搜索关键词等。
  • POST 方法:通常用于向服务器提交数据,参数不会附加在 URL 中,而是作为请求的实体内容发送给服务器。POST 请求通常用于提交表单数据、上传文件等场景,传输的数据量没有限制,而且更安全,因为参数不会明文显示在 URL 中。

现在来看一下何时使用 POST 和 GET 方法:

  • 使用 GET 方法:当你需要请求获取服务器上的资源,并且传递的参数是少量的、非敏感的信息时,可以使用 GET 方法。比如进行搜索、获取页面等场景。
  • 使用 POST 方法:当你需要向服务器提交大量数据、敏感信息(比如密码)、或者需要进行状态更新、文件上传等操作时,应该使用 POST 方法。POST 方法比 GET 方法更安全,因为参数不会暴露在 URL 中,而且传输的数据量没有限制。

总的来说,要根据具体的场景和需求来选择使用 GET 还是 POST 方法。如果只是简单的传递少量的非敏感信息,可以考虑使用 GET 方法;如果涉及到大量数据、敏感信息或者需要进行状态更新等操作,应该使用 POST 方法。

15. 缓存,get请求会缓存,请问有哪些方式清除缓存?除了开发者工具外还有哪些清除缓存(为用户角度)?
  • 使用手动清除浏览器缓存
  • 使用一些自动清楚缓存的扩展程序
  • 使用无痕模式浏览页面不会留下缓存
16. script标签中的defer和async的区别?

script标签中defer和async的区别

defer和async都是控制脚本加载和执行顺序的两个属性,它们可以在

区别主要是在脚本加载和执行时机上。

defer 属性用于确保脚本在 DOM 构建完成之前执行,而 async 属性则用于异步加载并立即执行脚本,不会阻塞页面的加载和渲染。

17.看我熟悉ES6,问我Symbol用过吗,使用场景?

Symbol 数据类型,用于表示独一无二的值。每个通过 Symbol() 函数创建的 Symbol 值都是唯一的

Symbol 的主要使用场景包括:

  1. 作为对象属性的键:可以使用 Symbol 作为对象的属性键,以确保属性名的唯一性,避免因为属性名冲突而导致意外覆盖或混淆的情况。例如:

    const mySymbol = Symbol('description');
    const obj = {
      [mySymbol]: 'This is a symbol property'
    };
    
  2. 定义常量:可以使用 Symbol 来定义常量,保证常量的唯一性。例如,可以使用 Symbol 定义一些操作的状态或类型

    const STATUS_PENDING = Symbol('pending');
    const STATUS_SUCCESS = Symbol('success');
    const STATUS_ERROR = Symbol('error');
    
  3. 使用内置 Symbol 值

    • ES6 提供了一些内置的 Symbol 值,如 Symbol.iteratorSymbol.toStringTag 等,用于定义对象的行为或特性。比如,可以使用 Symbol.iterator 实现自定义的迭代器。
  4. 私有属性和方法

    • 使用 Symbol 可以模拟私有属性和方法,因为外部无法直接访问到使用 Symbol 定义的属性或方法,从而实现一定程度的封装。

总的来说,Symbol 主要用于解决属性名冲突、定义常量、实现对象行为等场景,通过其唯一性和不可变性的特点,能够提高代码的可维护性和安全性。

18. BigInt用过吗,Number和BigInt可以相加吗?

BigInt 是 JavaScript 中的一种数据类型,用于表示任意精度的整数。相比于普通的 Number 类型,BigInt 可以表示更大范围的整数值,不受最大安全整数的限制。

//BigInt 可以通过在整数数字后面添加 n 后缀来创建
const bigIntNum = 1234567890123456789012345678901234567890n;

JavaScript 中的 Number 和 BigInt 类型是不同的类型,它们不能直接相加。会类型错误。

如果要相加,就需要类型一致。

  • 使用 BigInt() 函数将 Number 转换为 BigInt。
  • 使用 Number() 函数将 BigInt 转换为 Number。// 注意:转换 BigInt 到 Number 可能会丢失精度,因为 Number 类型有其表示范围的限制。
// 将 Number 转换为 BigInt 进行相加:
const num = 123;
const bigIntNum = 456n;
const result = BigInt(num) + bigIntNum;
console.log(result); // 输出:579n
19. 熟悉typeScript还是了解?如果一个interface 里面有abc需要定义一个新的并且拿到里面的ab 如何拿到?
20. v-for和v-if的优先级?在外层嵌套template,那在template里面v-if还是v-for?
21. 常见的css布局单位有哪些?你到底用过哪些?
  1. 像素(Pixel,px):最常见的单位之一,表示屏幕上的一个像素。它是一个绝对单位,不受屏幕或浏览器设置的影响。
  2. 百分比(Percentage,%):相对于父元素的百分比。例如,width: 50%; 表示元素宽度为父元素宽度的一半。
  3. 视窗宽度和高度(Viewport Width,vw):相对于视窗(浏览器窗口)宽度的百分比。例如,width: 50vw; 表示元素宽度为视窗宽度的一半。
  4. 视窗高度(Viewport Height,vh):相对于视窗(浏览器窗口)高度的百分比。例如,height: 80vh; 表示元素高度为视窗高度的 80%。
  5. 根元素字体大小(Root em,rem):相对于根元素的字体大小(html 元素)。这种单位的值不受父元素影响,方便响应式设计。
22. css文本省略···如何实现的?

通过 text-overflow 属性来实现文本的省略。结合 overflowwhite-space 属性,可以实现不同场景下的文本省略效果。

.ellipsis {
    white-space: nowrap; /* 禁止文本换行 */
    overflow: hidden; /* 溢出隐藏 */
    text-overflow: ellipsis; /* 文本溢出时显示省略号 */
}
<div class="ellipsis">这是一个很长的文本,如果超出容器宽度就会显示省略号。</div>
23. 手写promise.all方法,为什么要用push?会有什么问题?为什么要用forEach?循环的方法有哪些? forEach和for循环的区别是什么? forEach会中断吗?
//这个有问题(看下面的!!)
function custonPromiseall(promises)
{
    return new Promise((resolve,reject)=>
    {
        const results=[]
        let completCount=0
        promises.forEach((promise,index)=>
        {
           promise.then(result=>
           {
              results[index] =result
              completCount++
              if(completCount===promises.length)
                  resolve(results)
           })
            .catch(error=>{
               reject(error)
           })
        })
    })
}

//手写promise.all可能写的不够全,有几个问题(针对上面的写的那个)
1. 传入的数据不一定是数组,任何具有迭代器属性的值都可以传给all方法,因此获取长度最好不要用length,循环也不要用forEach,用for of,并且定义一个变量在外面,每循环一次就加一用来计算传入值的长度
2. 传入值内部不一定都是promise,因此在调用.then方法前,最好给包一层Promise.resolve()
3. 没有考虑传入空数据的问题,如果为空的则应该直接resolve一个空数组

//这个是大佬在评论区进行更改后的代码(***************)
Promise.myAll = function(pros) {
    return new Promise((resolve, reject) => {
        let proCount = 0
        let completedCount = 0
        for (const pro of pros) {
            let index = proCount
            proCount++
            Promise.resolve(pro).then(res => {
                completedCount++
                results[index] = res
                if (completedCount === proCount) {
                    resolve(results)
                }
            }, (err) => {
                reject(err)
            })
        }
        if (proCount === 0) {
            resolve([])
        }
    })
}

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值