目前前端招聘标准越来越高,上面分享的面经很具有代表性,但是只有题目没有答案,所以我就挑选了字节的1面2面,并总结了正确答案,上一篇随手答的有好些错误,一次“金三银四的前端社招面经”的解答。整理不易。
一面
1、TCP 和 UDP 的区别和使用场景?
区别,UDP 和 TCP 都是运输层协议,UDP 只实现了最简单的进程通信。而 TCP 实现了面向链接,传输可靠,流量控制,拥塞阻塞的功能。
使用场景,UDP:视屏直播,DNS,RIP 路由选择协议。TCP 需要上面 4 种功能的应用。
总结,推荐看这篇文章,这一篇 TCP 总结请收下,如果想深入了解,可以看《计算机网络 自顶向下方法》
2、QUIC 基于 UDP 怎么保证可靠性?
TCP 中通过三次握手,给每一份数据包添加序列,以及重试机制,保证可靠性。而 QUIC 在 UDP 的基础上,增加了一层实现 TCP 类似的功能。
总结,可参考李兵的《浏览器工作原理与实践》专栏。
3、 讲一下同源策略和跨域方案?CORS 的几个头部是什么(头部指请求头和响应头)?
同源策略:如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源。浏览器默认两个相同的源可以相互访问资源和操作 DOM,这就带来了安全隐患,同源策略就是限制访问某些资源。
跨域:同源策略限制了 XMLHttpRequest、Fetch 等访问不同源的网站,这就出现了跨域问题。跨域方案:JSONP、CORS、IFrame、PostMessage,服务端层面可以用 Nginx、NodeJs 、Websocket 等。
相应头
- Access-Control-Allow-Origin
- Access-Control-Allow-Headers
- Access-Control-Allow-Methods
请求头
- Access-Control-Allow-Credentials
- Access-Control-Allow-Headers
- Access-Control-Allow-Methods
- Access-Control-Allow-Origin
- Access-Control-Expose-Headers
总结,同源策略和跨域相关的内容,看考李兵的《浏览器工作原理与实践》专栏。CORS 相关,看阮一峰的文章跨域资源共享 CORS 详解,如果没有 CORS 实践,很难答出 CORS 几个相应头和请求头的具体作用。
4、讲一下 React Fiber
下面引用来自“走进 React Fiber 的世界”这篇文章。
Fiber 可以理解为是一个执行单元,也可以理解为是一种数据结构。Fiber 可以理解为一个执行单元,每次执行完一个执行单元,React 就会检查现在还剩多少时间,如果没有时间则将控制权让出去。Fiber 还可以理解为是一种数据结构,React Fiber 就是采用链表实现的。每个 Virtual DOM 都可以表示为一个 Fiber。
总结,Fiber 是 React 实现异步更新 UI 的基础,看走进 React Fiber 的世界
5、vue 双向绑定原理?
使用 Object.defineProperty 或者 Proxy 实现数据拦截,在 getter 存储值时,在 setter 时更新值,并且更新 UI。
6、redux 和 mobx 的区别和使用场景?
- Redux 数据存储在单一的 store,而 Mobx 将数据分散到多个 store。
- Redux 需要手动处理数据变化后的操作(UI),而 Mobx 使用双向数据绑定,在数据变化时,直接更新 UI。
- Redux 是函数式思想,状态不可变,每次返回新的对象,行为更可预测。而 Mobx 是面向对象的思想,状态是可变的,行为有时候不可预测。
- Redux 异步处理需要借助中间件。而 Mobx 无需借助中间件,实现更简单。
7、typeof null?null instanceof Object?
"object",false。null instanceof Object 和 typeof null 都是 js 错误的设计。
8、typeof 可以判断哪些类型?instanceof 做了什么?
typeof 可以判断 number、boolean、symbol、string、object、undefined、function。无法判断 Date、RegExp 等复杂的对象。
这是 instanceof 的用法,但是 instanceof 的原理是什么呢?根据 ECMAScript 语言规范,我梳理了一下大概的思路,然后整理了一段代码如下 其实 instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
function new_instance_of(leftVaule, rightVaule) {
let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
while (true) {
if (leftVaule === null) {
return false;
}
if (leftVaule === rightProto) {
return true;
}
leftVaule = leftVaule.__proto__;
}
}
是来自这篇掘金文章的解释。浅谈 instanceof 和 typeof 的实现原理
9、 实现一个 bind 函数
Function.prototype.myBind = function (ctx, ...rest) {
if (typeof this !== "function") {
throw new Error("myBind is only used to Function");
}
const _this = this;
return function fn(...args) {
// 拼接参数
const newArgs = rest.concat(args);
// 外部有可能 new fn();
if (this instanceof fn) {
return new _this(newArgs);
}
return this.apply(ctx, newArgs);
};
};
10、求数组里面最大连续项的和
[1, -2, 3, 4, -1, 5]; 输出 11,最大值是 “3,4,-1,5” 的和
function getMaxSum(arr) {
let dp = new Array(arr.length).fill(0);
dp[0] = arr[0];
let maxSum = 0
for (let i = 1; i < arr.length; i++) {
dp[i] = Math.max(arr[i], dp[i -1] + arr[i]);
if (dp[i] > maxSum) {
maxSum = dp[i];
}
}
return maxSum;
}
console.log(getMaxSum([1, -2, 3, 4, -1, 5]));
11、 event loop
event loop不同的宿主环境有不同的表现。
在浏览器里,event loop分为macro task和micro task。loop从一个宏任务开始,先将执行栈中的任务清空,再将微任务推到执行栈中并清空,之后检查是否存在宏任务,若存在则取出一个宏任务,执行完成检查是否有微任务,以此循环。
在node中,为了保证时间函数到点执行,每一个event loop都会先检测setTimeout是否到期。以及新增nextTrick,再当前eventLoop结束时执行的回调,而当前注册的setImmediate事件将在下次event loop执行。
两者区别,浏览器会将微任务插入到当前宏任务执行栈中,微任务和当前宏任务属于一次event loop。而node中,会先清空当前任务队列,再将微任务加入任务队列,执行时机可能是下次event loop。
- 宏任务,有脚本执行,setTimeout等函数,用户交互等,ajax,I/O。
- 微任务,有promise,await async,mutation,intersectionObserver,postMessage,process.nextTrick等。
浏览器中的事件循环
nodeJs中的事件循环
细节看阮一峰的文章,www.ruanyifeng.com/blog/2014/1…
二面
1、怎么优化 h5 的加载速度?
工程和细节方向两个方向优化。 工程方向,打包内容,资源加载类型等 细节方向,熟悉浏览器特性,比如减少重绘重排。React框架,优化代码。渲染机制,懒加载(图片,资源等)。
详细到内容看这篇文章,前端性能优化总结
2、离线包怎么更新?怎么知道需要打开哪个离线包?
每次进入一个新页面,异步检查配置文件,是否需要更新。
通过版本号。
3、js bridge 通信原理?
以Android为例。
js bridge充当中间者的作用,让js和Android可以双向通信。Android将事件注入到前端上下文window中,有两种事件,一种是Android的回调事件,和React生命周期相似的事件;另一种是预留给前端主动调用Android的事件。这样前端既能主动调用Android,也能注册Android的某些事件。Android触发事件时,直接调用前端注册到JSBridge的事件。这就是双向通信了。
4、怎么实现 h5 页面秒开?
参考 前端性能优化总结
5、明明不是同一个语言,为什么 js 和 native 可以通信?
这就好像问,为什么JS能调用C++实现的原生方法。关键词,宿主环境。JS在Webview的宿主环境,而Webview在Android的宿主环境。所以通过Webview这个中间方就能通信。
6、怎么实现 js bridge 跨多个 app 共用?
封装成sdk。
7、grpc 相比 http 的优势?
grpc,google+ Remote Procedure Call。google的远程过程调用,虽然也是使用TCP/IP协议,但是框架屏蔽了底层细节。系列化方式可以是二进制,并且grpc使用的是传输层协议,更加高效。而http是WEB的通信协议,实现更加复杂,性能不如grpc。
8、rpc 的调用流程?前端怎么调用 grpc 的?
客户端发起请求,序列化数据,经过rpc,传输到指定服务端,反序列化数据,得到最终数据。
使用框架调用,Web gRPC 是 gRPC 在 Web 上的一个适配实现。
总结,rpc相关的,如果简历不写,面试官99%不会问的。
9、为什么要用 grpc?
性能更好,心智负担更小,像调本地函数一样调用。
10、服务发现为什么用 ip,而不用域名?
域名需要DNS解析。
11、怎么做 DNS 预解析?
将需要预解析的ip放到配置文件里,当应用启动时,开启一个新的线程去请求DNS服务器。
12、怎么实现移动端的布局?
可以通过百分比,rem,vw,vh等自适应方式实现布局。
13、iOS 下软键盘输入框遮挡遇到过问题么?怎么解决顶不起来的问题?
低版本IOS会出现,监听键盘弹起,自定义一个滚动事件,滚到输入框的位置。
14、实现两个大数相加
Leetcode 简单题
/**
* @param {string} a
* @param {string} b
* @return {string}
*/
var addStrings = function(a, b) {
let carry = 0;
let index = 0;
let result = [];
let ra = a.split("").reverse().join("");
let rb = b.split("").reverse().join("");
while(index + 1 <= ra.length || index + 1 <= rb.length) {
let _a = 0;
let _b = 0;
if (ra.charAt(index)) {
_a = ra.charAt(index);
}
if (rb.charAt(index)) {
_b = rb.charAt(index);
}
const total = Number(_a) + Number(_b) + carry;
if (total >= 10) {
result.push(total - 10);
carry = 1;
} else {
result.push(total);
carry = 0;
}
index++;
}
if (carry) {
result.push(1);
}
return result.reverse().join('');
};
addStrings('1', '2')
15、求一个数组最大子项的和,要求这些子项在数组中的位置不是连续的。
这是LeetCode的题目,198.打家劫舍。
[1,2,3,1] 输出4,1 + 3 = 4。[2,7,9,3,1] 输出12,2 + 9 + 1 = 12
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
if (nums.length === 0) return 0;
if (nums.length === 1) return nums[0];
let dp = new Array(nums.length);
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (let i = 2; i < nums.length; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[nums.length - 1];
};
rob([2,7,9,3,1]);
16、常用的 react hooks 方法
useState、useEffect、useMemo、useCallback、useReducer、useLayoutEffect、useRef、useContext。
17、useState 怎么做缓存的?
通过一个环形链表。每一个hook都会在Fiber链表上,而useState会将同一个state都更新操作都保存到一个环形链表上,如下图。
18、react fiber 是什么?
1面回答过
19、怎么解决 useState 闭包的问题?
这个是问怎么解决useEffect闭包问题吧,传入依赖项即可。或者将state值赋值给useRef,这样每次useRef上的值都是最新的值。
20、useReducer 比 redux 好在哪里?
react内置支持。支持依赖项不更新,不触发useReducer的回调。更加轻量,无需引用框架,无需维护一个大的store树。