社招-字节跳动前端面试

本文最后更新于:2021/08/05 , 星期四 , 21:22

前言

找了朋友帮忙内推了字节跳动-飞书。没想到响应非常迅速,本以为会过一段时间才会安排面试。没想到隔天就安排上了面试,本来还想在面字节之前先拿其他几家试水,然后再来面试,没想到字节效率太高了。

二面已结束,在字节面试就是享受,虽然个人非常紧张,但是一场面试下来真的收获非常多。全部问题都是基于个人简历来进行问答的,面试记录抽象了一下。

一面

现在回顾一下面试题,发现大佬好像问了好多关于函数式编程的问题,组内可能已经在进行函数式编程的实践了?

堆和栈的区别?

栈和堆没有本质区别,使用栈内存时,是从地址高位开始分配内存空间。使用堆内存时,是从地址低位开始分配空间。栈中存放的主要是执行上下文,函数调用栈等。堆中存放的是对象这种复杂数据。需要按地址访问。

递归会引发什么问题?为什么会爆栈?

过深的递归会引起爆栈。内存中特地用来存放函数调用的栈区内存是有限的,如果递归太深,就会出现只入栈不出栈的情况。

进程和线程的区别。

进程是资源分配的最小单位,线程是 CPU 调度的最小单位。

Chrome 都有什么进程。

1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程。

HTTPs 怎么回事?

第一版:使用对称加密

  1. 浏览器发送它所支持的加密套件列表和一个随机数 client-random,这里的加密套件是指加密的方法,加密套件列表就是指浏览器能支持多少种加密方法列表。

  2. 服务器会从加密套件列表中选取一个加密套件,然后还会生成一个随机数 service-random,并将 service-random 和加密套件列表返回给浏览器。

  3. 最后浏览器和服务器分别返回确认消息。

  4. 然后它们再使用相同的方法将 client-random 和 service-random 混合起来生成一个密钥 master secret,有了密钥 master secret 和加密套件之后,双方就可以进行数据的加密传输了。

第二版:使用非对称加密

  1. 首先浏览器发送加密套件列表给服务器。

  2. 然后服务器会选择一个加密套件,使用非对称加密时服务器上需要有用于浏览器加密的公钥和服务器解密 HTTP 数据的私钥,由于公钥是给浏览器加密使用的,因此服务器会将加密套件和公钥一道发送给浏览器。

  3. 最后就是浏览器和服务器返回确认消息。

  4. 在浏览器端向服务器端发送数据时,就可以使用该公钥来加密数据

第三版:对称加密和非对称加密搭配使用

  1. 首先浏览器向服务器发送对称加密套件列表、非对称加密套件列表和随机数 client-random;

  2. 服务器保存随机数 client-random,选择对称加密和非对称加密的套件,然后生成随机数 service-random,向浏览器发送选择的加密套件、service-random 和公钥;

  3. 浏览器保存公钥,并生成随机数 pre-master,然后利用公钥对 pre-master 加密,并向服务器发送加密后的数据;

  4. 最后服务器拿出自己的私钥,解密出 pre-master 数据,并返回确认消息。

  5. 服务器和浏览器就有了共同的 client-random、service-random 和 pre-master,然后服务器和浏览器会使用这三组随机数生成对称密钥。

第四版:添加数字证书

  1. 首先浏览器向服务器发送对称加密套件列表、非对称加密套件列表和随机数 client-random;

  2. 服务器保存随机数 client-random,选择对称加密和非对称加密的套件,然后生成随机数 service-random,向浏览器发送选择的加密套件、service-random 和 证书

  3. 浏览器验证证书,生成随机数 pre-master,利用公钥对 pre-master 加密,并向服务器发送加密后的数据;

  4. 最后服务器拿出自己的私钥,解密出 pre-master 数据,并返回确认消息。

  5. 服务器和浏览器就有了共同的 client-random、service-random 和 pre-master,然后服务器和浏览器会使用这三组随机数生成对称密钥。

证书验证的流程。

  1. 首先浏览器读取证书中相关的明文信息,采用 CA 签名时相同的 Hash 函数来计算并得到信息摘要 A

  2. 然后再利用对应 CA 的公钥解密签名数据,得到信息摘要 B;

  3. 对比信息摘要 A 和信息摘要 B,如果一致,则可以确认证书是合法的。同时浏览器还会验证证书相关的域名信息、有效时间等信息。

如何尽可能的多发包并且不丢包?

到目前为止不知道怎么答。

滑动窗口

滑动窗口详解

HTTP2 的多路复用是怎么回事?

在一个 TCP 连接中可以存在多条流。也就是可以发送多个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。

HTTP 中的响应头拥塞怎么回事?

HTTP/1.1 通过管道技术实现一次性发送多个请求,以期提高吞吐和性能。然而,这种技术在接收响应时,要求必须按照发送请求的顺序返回。如果,第一个请求被堵塞了,则后面的请求即使处理完毕了,也需要等待。

WEBPACK 构建如何优化?

详见构建优化

React 的生命周期

react 15:
挂载时:

  • constructor

  • componentWillMount

  • render

  • componentDidMount

更新:

  • componentWillRecieveProps(由父组件的更新触发)

  • shouldComponentUpdate

  • componentWillUpdate

  • render

  • componentDidUpdate

卸载:

  • componentWillUnmount

react 16:

挂载时:

  • constructor

  • getDerivedStatesFromProps

  • render

  • componentDidMount

更新:

  • getDerivedStatesFromProps (16.3 以前只有 New Props 会触发,16.4 以后 New Props、setState、forceUpdate 都可以触发)

  • shouldComponentUpdate

  • render

  • getSnapshotBeforeUpdate

  • componentDidUpdate

卸载:

  • componentWillUnmount

React 的组件类型

类组件和函数组件

函数组件:符合 React 设计理念,函数编程
类组件:面向对象编程思想

pureComponet 与 Component

  • PureComponent 将会在 shouldComponentUpdate 中对组件更新前后的 props 和 state 进行浅比较,并根据浅比较的结果,决定是否需要继续更新流程。

  • Component 没有实现 shouldComponentUpdate。

useCallback 和 useMemo

  • useCallback 返回一个 memoized 回调函数

  • useMemo 返回 memoized 值

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

forwardref

forwardref:转发 refs 到 DOM 组件、在高阶组件中转发 refs

Redux 的最佳实践

个人理解加官方文档翻译版
Redux 的最佳实践官方文档

算法

算法题就不放具体题了把。我看面试官那面标记了我的名字+题目。。。不知道直接放原题会不会有问题。

算法题一共两道,第一道考察:数据结构栈和链表 第二题考查函数式编程柯里化。

都不难,但是一紧张脑梗了。第一道题写了个蠢死了的算法,在面试官的引导下完成了优化。

第二题直接没了思路,面试结束后看了一下,发现还是很简单的,可以直接用扩展运算符。。。

柯里化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
let _args = [...arguments];

// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
let adder = function () {
let _adder = function () {
// [].push.apply(_args, [].slice.call(arguments));
_args.push(...arguments);
return _adder;
};

// 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = () => {
return _args.reduce((a, b) => {
return a + b;
});
};

return _adder;
};
// return adder.apply(null, _args);
return adder(..._args);
}

// 新版Chrome不会自动调取toString了,safari还可以
add(1, 2, 3, 4).toString();
add(1)(2)(3)(4).toString(); //10

二面

child_process.exec 怎么实现的提高效率?进程和 CPU 的关系?如果只有一个 CPU 还能提升效率吗?为什么?

child_process.exec 主要是开启了多进程。多个进程可以并行在多个 cpu 中计算,对于单 cpu,多个进程在这个单 cpu 中是并发运行,根据时间片读取上下文+执行程序+保存上下文。。同一个进程同一时间段只能在一个 cpu 中运行,如果进程数小于 cpu 数,那么未使用的 cpu 将会空闲。

首屏渲染时间优化方案?

  • 资源太大?资源压缩,传输压缩,代码拆分,Tree sharking,HTTP/2,缓存。

  • 首页内容太多?懒加载,预渲染,SSR

  • 时序问题?prefetch,preload

csp 是什么?会造成什么问题?

csp 是内容安全策略,是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。乱修改可能引起 XSS,数据包嗅探攻击。

typescript 中的 type 和 interface 的区别?

  • type 可以声明基本类型别名,联合类型,元组等类型

  • type 语句中还可以使用 typeof 获取实例的 类型进行赋值

  • interface 能够声明合并。

  • interface 使用 extends 扩展,type 使用交叉类型(&)扩展

useCallback 和 useRef?

  • userCallback 返回的是一个 memozied 函数

  • useRef 可以返回一个可变的 ref 对象,本质上,useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”。useRef()ref 属性更有用。它可以很方便地保存任何可变值。它创建的是一个普通 Javascript 对象。而 useRef() 和自建一个 {current: ...} 对象的唯一区别是,useRef 会在每次渲染时返回同一个 ref 对象。

hook 的使用注意事项?为什么?

不要在循环,条件或嵌套函数中调用 Hook。

在初始化阶段,会将 hook 的相关信息保存在一个 hook 对象内,然后 hook 对象之间以单向链表的形式串联。在更新阶段,按顺序去遍历之前构建好的链表,取出对应的数据信息进行渲染。hooks 的渲染是通过“依次遍历”来定位每个 hooks 内容的,这个过程就像从数组中依次取值一样,是完全按照顺序(或者说索引)来的。因此 React 不会看你命名的变量名是什么。如果前后两次读到的链表在顺序上出现差异,那么渲染的结果就会出问题。

代码题:

26 进制转换。

0->A, 1->B … 24->Y,25->Z,26->AA,27->AB…52->BA

1
2
3
4
5
6
7
8
9
10
11
12
13
const convert26 = (num)=>{
let real = num + 1;
let str = "";
while(real > 0){
let m = real % 26;
if(m === 0){
m = 26;
}
str = String.fromCharCode ( m + 64) + str;
real = (real - m) / 26
}
return str;
}

叶子结点的公共祖先节点

每个节点均含有指向其父节点的指针和 val,给两个叶子结点找到他们的公共祖先节点。要求空间复杂度为 O(1)

最先给了一个空间复杂度 O(n)的思路。

选择一个节点为开始遍历一遍存入 MAP,第二个节点遍历的时候判断 MAP 内是否存在即可。

后面问面试官的时候问了这道题,面试官说,这个可以当作两个链表的第一个公共节点来做。恍然大悟。然后还给我详细解说了,空间换时间,时间换空间的事情,豁然开朗。

思路就是:遍历完自己的节点后 交换位置继续遍历 最后二者的总步数是一样 相遇时即为所求第一个祖先节点

1
2
3
4
5
6
7
8
9
10
11
var getIntersectionNode = function(headA, headB) {
let firstH = headA, secondH = headB

while (firstH !== secondH) {
firstH = firstH ? firstH.next : headB
secondH = secondH ? secondH.next : headA
}

return firstH;

};

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!