首页 > 字节互娱前端一面面经
头像
Red_Ferrari
编辑于 2021-08-08 20:49
+ 关注

字节互娱前端一面面经 内部员工回复

一切又从头

自我介绍,部门介绍

编码方式

不按套路出牌,只知道:

  • UTF-8:Unicode字符的实现方式之一,它使用1-4个字符表示一个符号,根据不同的符号而变化字节长度
  • ASCII

承认非科班不知道,没学过

其实有几种我知道的,补充:

  • Unicode:包含世界上所有的字符,是一个字符集
  • GBK/GB2312/GB18030:GBK/GB2312表示简体中文,GB18030表示繁体中文。

计算机网络

TCP在哪一层

TCP 3次握手 4次挥手

HTTP 1.0/1.1/2.0区别

  • HTTP 1.1

    • HTTP1.1默认开启长连接,在一个TCP连接上可以传送多个HTTP请求和响应。使用 TCP 长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
    • HTTP1.1支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
  • HTTP 2.0

    • 二进制分帧:HTTP 2 采用二进制格式传输数据,而非HTTP 1.x 的文本格式,二进制协议解析起来更高效。
    • 多路复用:HTTP 2 同域名下所有通信都在单个连接上完成。
    • 头部压缩:只发送头部数据的差异数据,而不是全部发送,减少头部的信息量
    • 服务端推送:服务端可以在发送页面HTML时主动推送其它资源,而不用等到浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把JS和CSS文件推送给客户端,而不需要客户端解析HTML时再发送这些请求。服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送RST_STREAM帧来拒收。主动推送也遵守同源策略,服务器不会随便推送第三方资源给客户端。

HTTP的keep-alive是干什么的?
HTTP1.0中,默认短连接,在HTTP请求头中加入Connection: keep-alive来告诉对方这个请求响应完成后不要关闭

keep-alive的优点:

  • 较少的CPU和内存的使用(由于同时打开的连接的减少了)
  • 允许请求和应答的HTTP管线化
  • 降低拥塞控制(TCP连接减少了)
  • 减少了后续请求的延迟(无需再次握手)
  • 报告错误无需关闭TCP连接

HTTP缓存

强缓存:HTTP 1.0 Expires 绝对时间,HTTP 1.1 Cache-Control 相对时间

Cache-Control字段:

  • public可以被所有⽤户缓存,包括终端和CDN等中间代理服务器
  • private只能被终端浏览器缓存,不允许中继缓存服务器进行缓存
  • no-cache,先缓存本地,但是在命中缓存之后必须与服务器验证缓存的新鲜度才能使用
  • no-store,不会产生任何缓存

协商缓存:
当第⼀次请求时服务器返回的响应头中没有Cache-Control和Expires或者Cache-Control和Expires过期抑或它的属性设置为no-cache时,那么浏览器第二次请求时就会与服务器进行协商。
如果缓存和服务端资源的最新版本是⼀致的,那么就无需再次下载该资源,服务端直接返回304 Not Modified 状态码,如果服务器发现浏览器中的缓存已经是旧版本了,那么服务器就会把最新资源的完整内容返回给浏览器,状态码就是200 OK。

协商缓存也分为2种:

Last-Modified/If-Modified-Since:
服务器的资源的最新修改时间,速度较快
ETag/If-None-Match
ETag是根据资源内容进行hash,生成一个信息摘要,只要资源内容有变化,即可确定客户端的缓存资源是否为最新,精度较⾼。

Event Loop与宏任务微任务:

  • 首先执行同步代码,这属于宏任务
  • 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
  • 执行所有微任务
  • 当执行完所有微任务后
  • 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数

前端安全

XSS和CSRF八股文

HTML与CSS

盒式模型

defer和async是什么,有什么区别

  • defer:异步加载脚本,script被异步加载后并不会立刻执行,而是等待文档被解析完毕后。
  • async:同样是异步加载脚本,区别是脚本加载完毕后立刻执行,这导致async属性下的脚本是乱序的,对于script有先后依赖关系的情况,并不适用。

Vue和React

Vue和React的区别

自作多情加了小程序的区别

Vue v-model 支持双向绑定,相比于 React 来说使用更加方便,改变数据方式不同,Vue 修改状态相比来说要简单许多,React 需要使用 setState 来改变状态,并且使用这个 API 也有一些坑点。并且 Vue 的底层使用了依赖追踪,页面更新渲染已经是最优的了,但是 React 还是需要用户手动去优化这方面的问题。

React 需要使用 JSX,有一定的上手成本,并且需要一整套的工具链支持,但是完全可以通过 JS 来控制页面,更加的灵活。Vue 使用了模板语法,相比于 JSX 来说没有那么灵活,但是完全可以脱离工具链,通过直接编写 render 函数就能在浏览器中运行。

img

(redux的作者Dan Abramov,同时他也是React的核心成员之一)

Vue中的key的作用和diff算法

  • 同级比较,再比较子节点
  • 先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除),再比较都有子节点的情况(核心diff)
  • 递归比较子节点
  • 正常Diff两个树的时间复杂度是O(n^3),但实际情况下我们很少会进行跨层级的移动DOM,所以VueDiff进行了优化,从O(n^3) -> O(n),只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。
  • 新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的,keychildren中节点的唯一标识,以便能够在旧 children 的节点中找到可复用的节点。

O(n^3)怎么来的?

不是很清楚。。。

看大佬的说明

关于O(n^3)怎么计算出来的

问题描述

原问题标题“React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的? ”

  1. 这里的n指的是页面的VDOM节点数,这个不太严谨。如果更严谨一点,我们应该应该假设
    变化之前的节点数为m,变化之后的节点数为n。
  2. React 和 Vue 做优化的前提是“放弃了最优解“,本质上是一种权衡,有利有弊。

倘若这个算法用到别的行业,比如医药行业,肯定是不行的,为什么?

React 和 Vue 做的假设是:

  • 检测VDOM的变化只发生在同一层
  • 检测VDOM的变化依赖于用户指定的key

如果变化发生在不同层或者同样的元素用户指定了不同的key或者不同元素用户指定同样的key,
React 和 Vue都不会检测到,就会发生莫名其妙的问题。

但是React 认为, 前端碰到上面的第一种情况概率很小,第二种情况又可以通过提示用户,让用户去解决,因此
这个取舍是值得的。 没有牺牲空间复杂度,却换来了在大多数情况下时间上的巨大提升。
明智的选择!

基本概念

首先大家要有个基本概念。

其实这是一个典型的最小编辑距离的问题,相关算法有很多,比如Git中
,提交之前会进行一次对象的diff操作,就是用的这个最小距离编辑算法。

leetcode 有原题目,
如果想明白这个O(n^3), 可以先看下这个。

对于树,我们也是一样的,我们定义三种操作,用来将一棵树转化为另外一棵树:

  • 删除:删除一个节点,将它的children交给它的父节点
  • 插入:在children中 插入一个节点
  • 修改:修改节点的值

事实上,从一棵树转化为另外一棵树,我们有很多方式,我们要找到最少的。

直观的方式是用动态规划,通过这种记忆化搜索减少时间复杂度。

算法

由于树是一种递归的数据结构,因此最简单的树的比较算法是递归处理。

详细描述这个算法可以写一篇很长的论文,这里不赘述。
大家想看代码的,这里有一份
我希望没有吓到你。

确切地说,树的最小距离编辑算法的时间复杂度是O(n^2m(1+logmn)),
我们假设m 与 n 同阶, 就会变成 O(n^3)

看图吧:

img

如上所示, 左侧树a节点依次进行如下对比:

a->e、a->d、a->b、a->c、a->a

之后左侧树其它节点b、c、d、e亦是与右侧树每个节点对比, 算法复杂度能达到O(n^2)

查找完差异后还需计算最小转换方式,这其中的原理我没仔细去看,最终达到的算法复杂度是O(n^3)

将两颗树中所有的节点一一对比需要O(n²)的复杂度,在对比过程中发现旧节点在新的树中未找到,那么就需要把旧节点删除,删除一棵树的一个节点(找到一个合适的节点放到被删除的位置)的时间复杂度为O(n),同理添加新节点的复杂度也是O(n),合起来diff两个树的复杂度就是O(n³)

Vue和React的diff算法有什么区别:

不知道了。

  1. Vue 2.X进行diff时,调用patch打补丁函数,一边比较一边给真实的DOM打补丁

  2. Vue2.X对比节点,当节点元素类型相同,但是className不同时,认为是不同类型的元素,删除重新创建,而react则认为是同类型节点,进行修改操作

  3. ① Vue 2.X的列表比对,采用从两端到中间的方式,旧集合和新集合两端各存在两个指针,两两进行比较,如果匹配上了就按照新集合去调整旧集合,每次对比结束后,指针向队列中间移动;
    ② 而react则是从左往右依次对比,利用元素的index和标识lastIndex进行比较,如果满足index < lastIndex就移动元素,删除和添加则各自按照规则调整;
    ③ 当一个集合把最后一个节点移动到最前面,react会把前面的节点依次向后移动,而Vue只会把最后一个节点放在最前面,这样的操作来看,Vue的diff性能是高于react的

  4. Vue3.x借鉴了 ivi算法和 inferno算法。在创建VNode时就确定其类型,以及在mount/patch的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。(实际的实现可以结合Vue3.x源码看)

    该算法中还运用了动态规划的思想求解最长递归子序列

更多推荐阅读:聊一聊Diff算法(React、Vue2.x、Vue3.x)

vue 项目如何性能优化

这一问我没明白什么意思,没答上来,找了一下其实可以说的很多的

代码层面:

  • 合理使用 v-ifv-show
  • 区分 computedwatch 的使用
  • v-for 遍历为 item 添加 key
  • v-for 遍历避免同时使用 v-if
  • 通过 addEventListener添加的事件在组件销毁时要用 removeEventListener 手动移除这些事件的监听
  • 图片懒加载
  • 路由懒加载
  • CDN加载依赖
  • 第三方插件按需引入
  • SSR服务端渲染,首屏加载速度快,SEO效果好

Webpack 层面优化:

  • 对图片进行压缩
  • 使用 CommonsChunkPlugin 插件提取公共代码
  • 提取组件的 CSS
  • 优化 SourceMap
  • 构建结果输出分析,利用 webpack-bundle-analyzer 可视化分析工具

手撕代码

比较两个版本号 version1 和 version2。

如果 version1 > version2 返回 1,如果 version1 < version2 返回 -1, 除此之外返回 0。

你可以假设版本字符串非空,并且只包含数字和 . 字符。

. 字符不代表小数点,而是用于分隔数字序列。

例如,2.5 不是“两个半”,也不是“差一半到三”,而是第二版中的第五个小版本。

你可以假设版本号的每一级的默认修订版号为 0。例如,版本号 3.4 的第一级(大版本)和第二级(小版本)修订号分别为 3 和 4。其第三级和第四级修订号均为 0。

示例 1:

输入: version1 = "0.1", version2 = "1.1"

输出: -1

示例 2:

输入: version1 = "1.0.1", version2 = "1"

输出: 1

示例 3:

输入: version1 = "7.5.2.4", version2 = "7.5.3"

输出: -1

示例 4:

输入:version1 = "1.01", version2 = "1.001"

输出:0

解释:忽略前导零,“01” 和 “001” 表示相同的数字 “1”。

示例 5:

输入:version1 = "1.0", version2 = "1.0.0"

输出:0

解释:version1 没有第三级修订号,这意味着它的第三级修订号默认为 “0”。

我写的完整版代码:

function compare(version1, version2) {
    let arr1 = version1.split('.'), arr2 = version2.split('.')
    const len1 = arr1.length, len2 = arr2.length;
    const len = Math.max(len1, len2);
    while(arr1.length < len) {
        arr1.push(0);
    }
    while(arr2.length < len) {
        arr2.push(0);
    }
    for (let i = 0; i < len; i++) {
        let num1 = parseInt(arr1[i]), num2 = parseInt(arr2[i]);
        if (num1 < num2) {
            return -1;
        } else if (num1 > num2) {
            return 1;
        }
    }
    return 0;
}

console.log(compare("0.1", "1.1"));
console.log(compare("1.0.1", "1"));
console.log(compare("7.5.2.4", "7.5.3"));
console.log(compare("1.01", "1.001"));
console.log(compare("1.0", "1.0.0"));

更多模拟面试

全部评论

(13) 回帖
加载中...
话题 回帖

推荐话题

相关热帖

历年真题 真题热练榜 24小时
技术(软件)/信息技术类
查看全部

热门推荐