21/11/22更一波+预告,11/26二面,非常感谢面试官大大能给二面的机会,虽然这周是考试周,但是我会好好准备的!
21/11/26 最后一更XD,
字节跳动技术中台日常实习二面凉经-反面教材中的反面教材在此
被面试官大大全程吊打了,但也反思了很多问题!算是个扎心帖了!
算法题比较丢人。easy级别的题没A出来,算法思路是正确的(面试官说得)但是代码写得太丑陋了(我自己说得)
以为会被算法题挂掉,但是我和一个大二的学弟都是没A出来算法题的情况下收到二面通知的,感觉二面再A不出来就撒由那拉了🥺
面试官大大很和善~一直跟我说别这么客气,叫同学就行😂
这次面试整体难度真的很小!可能是因为看我学习时间只有三个多月的缘故吧XD(之前看面经也没见过这么简单的)
上来先是根据自我介绍关于学习路线/简历上关于专业课(数据挖掘、强化学习)问了下为啥不继续搞人工智能了XD
答:不太感兴趣(总之就是这个意思 巴拉巴拉把往事说了一下XD)
聊项目
接手的外包项目刚做了三周不到,所以问得不深。
- 你在这个大型项目中主要负责什么
- 一些交互的效果是如何做出来的
- 看项目是交通相关的,能不能说一下路口信息如何更新到组件中
JS30demos项目 问了一句
- 如何想到做这个项目的呢
答 为了巩固下JS基础,感觉比较重要 然后没有细问
计算机网络
了解HTTPS么?说一下HTTPS的一个传输过程
- 非对称加密建立HTTPS连接
- 然后使用对称加密进行报文的收发
追问-说一下非对称加密是如何利用公钥私钥解决安全问题的(之前第一问里有挖坑)
当时答得有点乱,下来整理了一下,感觉清晰不少~
另外搜索非对称算法时,网上好多文章没说的一点就是这个非对称算法是用来干啥的,根据图解HTTP的内容,我的理解是以下几个步骤:
- 【1】客户端有私钥A + 公钥A; 服务器有私钥B + 公钥B
- 【2】规则——私钥A+公钥A = 密钥对A(公钥A加密的内容只有私钥A可以解密!)
- 【3】为了建立安全的通信道路(不被坏人篡改、冒名顶替(窃听就木有办法辣,不过也不怕!我们这个是非对称加密!被窃听了也不会被破解的)),客户端把手里的公钥发送给服务器
- 【4】服务器使用拿到的公钥A进行重要报文(也就是图解HTTP提到的 “稍后的共享密钥加密中要使用的密钥”)的加密,之后再发给客户端
- 【5】客户端手里有私钥A,直接解密这个重要报文,安全地获得共享密钥!妙啊!
这样互联网上的不法分子就没辙辣!
另外还可以使用数字证书解决公开密钥的传输问题 - 图解HTTP很香啊!
JS基础
说下JS有哪些数据类型?
Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。
那引用数据类型和原始数据类型有什么区别么?
- 栈:原始数据类型(Undefined、Null、Boolean、Number、String)
- 堆:引用数据类型(对象、数组和函数)
两种类型的区别在于存储位置的不同:
- 原始数据类型直接存储在栈(stack)中的简单数据段
- 占据空间小、大小固定
- 属于被频繁使用数据,所以放入栈中存储;
- 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。
- 如果存储在栈中,将会影响程序运行的性能;
- 引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。
- 当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
说一下new一个构造函数过程中发生了什么
艾玛太经典了,我还写过一篇文章说这里哩
说了一下 在红宝书看到过bulabulabula
另外拓展了一下——判断构造函数的返回值类型,如果res为对象类型,new Person的最终结果为res 而非我们想要的那个实例对象!
追问-刚才说的第三步提到了this,那么new过以后,this指向哪里呢?
指向这个新创建的对象(红宝书提到了)
拓展:可以看看之前写的那篇文章中的例子 Fn即为构造函数 很明显它是指向这个新创建的Object空对象的
浏览器原理
这里被问了蛮多的~可能也是因为我在第一个问题的时候说得内容(挖的坑)太多吧!
浏览器的渲染过程
【1】解析 HTML 构建DOM树
【2】解析CSS 构建CSSOM树
【3】利用上面两个树构建渲染树(渲染树的节点即为“渲染对象”)
【4】渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以被称作“回流”)这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
【5】上述几步过后,布局结束;最后进行绘制,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。
记住这张图:
完整过程中的一些细节——
- 首先解析收到的文档,根据文档定义构建一棵 DOM 树,DOM 树是由 DOM 元素及属性节点组成的。
- 然后对 CSS 进行解析,生成 CSSOM 规则树。
- 根据 DOM 树和 CSSOM 规则树构建渲染树。渲染树的节点被称为渲染对象,渲染对象是一个包含有颜色和大小等属性的矩形,渲染对象和 DOM 元素相对应,但这种对应关系不是一对一的,不可见的 DOM 元素不会被插入渲染树。还有一些 DOM元素对应几个可见对象,它们一般是一些具有复杂结构的元素,无法用一个矩形来描述。
- 当渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
- 布局阶段结束后是绘制阶段,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。
追问-解析文档(HTML)过程中,是将HTML都解析完了再去生成渲染树麽?
注意:这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html 都解析完成之后再去构建和布局 render 树(渲染树)。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
追问-script中的脚本文件、link中的css文件的解析/执行会阻塞文档解析麽?如何阻塞?
- 脚本的加载会阻塞文档解析
如果没有 defer 或 async 属性,浏览器会立即加载并执行相应的脚本。它不会等待后续加载的文档元素,读取到就会开始加载和执行,这样就阻塞了后续文档的加载。
下图可以直观的看出三者之间的区别:
所以script要放在底部/加async defer关键字
拓展学习:
JavaScript 的加载、解析与执行会阻塞文档的解析,也就是说,在构建 DOM 时,HTML 解析器若遇到了 JavaScript,那么它会暂停文档的解析,将控制权移交给 JavaScript 引擎,等 JavaScript 引擎运行完毕,浏览器再从中断的地方恢复继续解析文档。
也就是说,如果想要首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。
- 当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。
- CSS 如何阻塞文档解析?
理论上,既然样式表不改变 DOM 树,也就没有必要停下文档的解析等待它们。
面试官也提到了这里,CSS的解析并不阻塞文档~
- 但是可以拓展一下,JS的脚本会因为CSSOM树没被构建好而延迟执行,所以!要把script放在底部/加async defer关键字
然而,存在一个问题,JavaScript 脚本执行时可能在文档的解析过程中请求样式信息(比如根据样式获取元素节点),如果样式还没有加载和解析,脚本将得到错误的值,显然这将会导致很多问题。所以——
如果浏览器尚未完成 CSSOM 的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟 JavaScript 脚本执行和文档的解析,直至其完成 CSSOM 的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建 CSSOM,然后再执行 JavaScript,最后再继续文档的解析。这样就会间接阻塞了文档解析
追问-什么情况会阻止浏览器渲染
前面问的细节,这个问题比较全面了就
要明确——首先渲染的前提是生成渲染树
- 所以 HTML 和 CSS 肯定会阻塞渲染。
如果你想渲染的越快,你越应该降低一开始需要渲染的文件大小,并且扁平层级,优化选择器。
- 浏览器在解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方重新开始。
也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。
拓展知识
并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性
- 当 script 标签加上 defer 属性以后,表示该 JS 文件会并行下载,但是会放到 HTML 解析完成后顺序执行,所以对于这种情况你可以把 script 标签放在任意位置。
- 同理,对于没有任何依赖的 JS 文件可以加上 async 属性,表示 JS 文件下载和解析不会阻塞渲染(async属性不能保证JS文件的执行是按顺序的~)。
记住下面这个图就好~
蓝色代表 js 脚本网络加载时间,红色代表 js 脚本执行时间,绿色代表 html 解析
框架
说下Vue中的key有啥用?
21/12/20更新 使用antdUI组件库做项目时 看着a-table组件对于列表项的实现方式有了一些感悟:
一个自己尝试造轮子的同学跟我聊到:他写得table组件有一个缺陷就是 没有设置key 会出现一些渲染性能的问题
比如:一个1 2 3 4 5为序的列表 删掉了3 就会变为1 2 3 4 原来的4 5都会前提(这样最后两个元素都要重新渲染!)而antd的a-table在填充数据时用到了——
for (let i = 0; i < 100; i++) {
data.push({
key: i.toString(),
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
一个table组件的写法就很体现功力啊!~
这个因为Vue没咋学过,说得比较模糊,但是结合之前学习React残留下来的一点记忆强答了一波
官方文档——
key
的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
面试官给出解释——如果不用key的话,在列表增删的时候,渲染效率会出问题(和虚拟DOM有关)
但是如果key使用错误了,在列表顺序需要变更时,则容易发生顺序错误!
其他
了解CDN麽?
听说过这个,在一些开源仓库的readme中一些资源是挂在cdn上的,比如图片和视频。
面试官说cdn是用来存储一些资源的类似服务器的东西,一些上线的项目会就近使用这其中的资源,比较快~
CDN的全称是
Content Delivery
Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。
算法题
删除字符串-力扣easy中稍难一些的一题
与leetcode1047(利用栈可以巧妙解决)类似,但是要麻烦一些
- 想了一两分钟,先说思路,使用双指针挨个删除,面试官说可以,让我写代码
然后噩梦开始😂
几分钟吧大概代码就写好了,但是第一遍运行超时,就开始慌了。
现在想想不应该慌,应该从头,从while循环(很明显是超时的罪魁祸首)一点点看!但是当时也是因为紧张,就瞎改一堆,越改越乱!反省!
既然难以做到一遍AC,就应该建立起完整的找错体系!
- 超时:找while循环是不是推出条件错了
- 答案与结果不符合:把数据代入代码走一遍!
正确答案(自我反省)如下:
/* 删除字符串中出现大于等于2的相邻字符 */
function removeDuplicates(str){
let i = 0, j = 1;
// 主要错误1:while(str[j] !== null) 链表写多了!!退出条件写成这样了我晕!主要就错这里了...还是有点慌
while(str[j]){
if(str[i] === str[j]){
str = deleteStr(str, i, j);
i = 0;
j = 1;
}
else{
i++;
j++;
}
}
return str;
}
function deleteStr(str, i, j){
// 删除[i,j]范围内的字符串
let start = i;
let end;
while(str[i] === str[j]){
i++;
j++;
}
end = i;
// 主要错误2:删除字符串[i,j]范围内的数居然一时间忘记了!我晕!当时这么写的:str.split("").splice(start, end-start+1).join("")
// 这根本改不掉字符串啊...当时我在想啥...
// 主要是超时我也不太好console.log()来debug,所以下回出现超时问题时第一时间把while那里的问题全弄掉!
let arr = str.split("");
arr.splice(start, end - start + 1)
str = arr.join("");
return str;
}
console.log(removeDuplicates("abbbaca"));// "ca"