算法刷题
大厂面试还是很注重算法题的,尤其是字节跳动,算法是问的比较多的,关于算法,推荐《LeetCode》和《算法的乐趣》,这两本我也有电子版,字节跳动、阿里、美团等大厂面试题(含答案+解析)、学习笔记、Xmind思维导图均可以分享给大家学习。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
写在最后
最后,对所以做Java的朋友提几点建议,也是我的个人心得:
-
疯狂编程
-
学习效果可视化
-
写博客
-
阅读优秀代码
-
心态调整
- 箭头函数没有自己的执行上下文,this 是外层函数的 this。
栈空间和堆空间:数据是如何存储的
动态语言:在使用时需要检查数据类型的语言。
弱类型语言:支持隐式转换的语言。
JavaScript 中的 8 种数据类型,它们可以分为两大类——原始类型和引用类型。
原始类型数据存放在栈中,引用类型数据存放在堆中。堆中的数据是通过引用与变量关系联系起来的。
从内存视角了解闭包:词法扫描内部函数,引用了外部函数变量,堆空间创建一个“closure”对象,保存变量。
垃圾回收:垃圾数据如何自动回收
-
栈中数据回收:执行状态指针 ESP 在执行栈中移动,移过某执行上下文,就会被销毁;
-
堆中数据回收:V8 引擎采用标记-清除算法;
-
V8 把堆分为两个区域——新生代和老生代,分别使用副、主垃圾回收器;
-
副垃圾回收器负责新生代垃圾回收,小对象(1 ~ 8M)会被分配到该区域处理;
-
新生代采用 scavenge 算法处理:将新生代空间分为两半,一半空闲,一半存对象,对对象区域做标记,存活对象复制排列到空闲区域,没有内存碎片,完成后,清理对象区域,角色反转;
-
新生代区域两次垃圾回收还存活的对象晋升至老生代区域;
-
主垃圾回收器负责老生区垃圾回收,大对象,存活时间长;
-
新生代区域采用标记-清除算法回收垃圾:从根元素开始,递归,可到达的元素活动元素,否则是垃圾数据;
-
为了不造成卡顿,标记过程被切分为一个个子标记,交替进行。
编译器和解析器:V8 如何执行一段 JavaScript 代码的
-
计算机语言可以分为两种:编译型和解释型语言。编译型语言经过编译器编译后保留机器能读懂的二进制文件,比如 C/C++,go 语言。解释型语言是在程序运行时通过解释器对程序进行动态解释和执行,比如 Python,JavaScript 语言。
-
编译型语言的编译过程:编译器首先将代码进行词法分析、语法分析,生成抽象语法树(AST),然后优化代码,最后生成处理器能够理解的机器码;
-
解释型语言解释过程:解释器会对代码进行词法分析、语法分析,并生产抽象语法树(AST),不过它会再基于抽象语法树生成字节码,最后根据字节码执行程序;
-
AST 的生成:第一阶段是分词(词法分析),将一行行源码拆解成一个个 token(语法上不可再分、最小单个字符)。第二阶段是解析(语法分析),将上一步生成的 token 数据,根据语法规则转为 AST,这一阶段会检查语法错误;
-
字节码存在的意义:直接将 AST 转化为机器码,执行效率是非常高,但是消耗大量内存,从而先转化为字节码解决内存问题;
-
解释器 ignition 在解释执行字节码,同时会手机代码信息,发现某一部分代码是热点代码(HotSpot),编译器把热点的字节码转化为机器码,并保存起来,下次使用;
-
字节码配合解释器和编译器的计数实现称为即时编译(JIT)。
消息队列和事件循环:页面是怎么活起来的
-
每个渲染进程都有一个主线程,主线程会处理 DOM,计算样式,处理布局,JavaScript 任务以及各种输入事件;
-
维护一个消息队列,新任务(比如 IO 线程)添加到消息队列尾部,主线程循环地从消息队列头部读取任务,执行任务;
-
解决处理优先级高的任务:消息队列的中的任务称为宏任务,每个宏任务中都会包含一个微任务队列,在执行宏任务的过程中,如果 DOM 有变化,将该变化添加到微任务队列中;
-
解决单个任务执行时长过久:JavaScript 通过回调功能来规避。
webapi:setTimeout 是怎么实现的
-
JavaScript 调用 setTimeout 设置回调函数的时候,渲染进程会创建一个回调任务,延时执行队列存放定时器任务;
-
当定时器任务到期,就会从延时队列中取出并执行;
-
如果当前任务执行时间过久,会影响延时到期定时器任务的执行;
-
如果 setTimeout 存在嵌套调用(5 次以上),判断该函数方法被阻塞,那么系统会设置最短时间间隔为 4 秒;
-
未激活的页面,setTimeout 执行最小间隔是 1000 毫秒,目的是为了降低加载损耗;
-
延时执行时间最大值是 24.8 天,因为延时值是以 32 个 bit 存储的;
-
setTimeout 设置的回调函数中的 this 指向全局 window。
webpai:XMLHttpRequest 是怎么实现的
-
XMLHttpRequest onreadystatechange 处理流程:未初始化 -> OPENED -> HEADERS_RECEIVED -> LOADING -> DONE;
-
渲染进程会将请求发送给网络进程,然后网络进程负责资源下载,等网络进程接收到数据后,利用 IPC 通知渲染进程;
-
渲染进程接收到消息之后,会将 xhr 回调函数封装成任务并添加到消息队列中,等主线程循环系统执行到该任务的时候,会根据相关状态来调用回调函数。
宏任务和微任务:不是所有的任务都是一个待遇
-
消息队列中的任务为宏任务。渲染进程内部会维护多个消息队列,比如延时执行队列和普通消息队列,主线程采用 for 循环,不断地从这些任务队列中取出任务并执行;
-
微任务是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前;
-
V8 在执行 javascript 脚本时,会为其创建一个全局执行上下文,同时会创建一个微任务队列;
-
执行微任务过程中产生的微任务不会推迟到下个宏任务中执行,而是在当前宏任务中继续执行;
使用 Promise 告别回调函数
-
使用 Promise 解决了回调地狱问题,消灭嵌套和多次处理;
-
模拟实现 Promise
function Bromise(executor) {
var _onResolve = null
this.then = function (onResolve) {
_onResolve = onResolve
}
function resolve(value) {
setTimeout(() => {
_onResolve(value)
}, 0)
}
executor(resolve, null)
}
async await 使用同步方式写异步代码
-
生成器函数是一个带星号函数,而且是可以暂停执行和回复执行的;
-
生成器函数内部执行一段代码,遇到 yield 关键字,javascript 引擎返回关键字后面的内容给外部,并且暂停该函数的执行;
-
外部函数可以同步 next 方法恢复函数的执行;
-
协程是一种比线程更加轻量级的存在,协程可以看成是跑在线程上的任务,一个线程可以存在多个协程,但是同时只能执行一个协程,如果 A 协程启动 B 协程,A 为 B 的父协程;
-
协程不被操作协同内核所管理,而完全由程序所控制,这样性能提升;
-
await xxx
会创建一个 Promise 对象,将xxx
任务提交给微任务队列; -
暂停当前协程的执行,将主线程的控制权力转交给父协程执行,同时将 Promise 对象返回给父协程,继续执行父协程;
-
父协程执行结束之前会检查微任务队列,微任务队列中有
resolve(xxx)
等待执行,触发 then 的回调函数; -
回调函数被激活后,会将主线程的控制权交给协程,继续执行后续语句,完成后将控制权还给父协程。
页面性能分析:利用 chrome 做 web 性能分析
- Chrome 开发者工具(简称 DevTools)是一组网页制作和调试的工具,内嵌于 Google Chrome 浏览器中。它一共包含了 10 个功能面板,包括了 Elements、Console、Sources、NetWork、Performance、Memory、Application、Security、Audits 和 Layers。
DOM 树:JavaScript 是如何影响 DOM 树构建的
-
HTML 解析器(HTMLParse)负责将 HTML 字节流转换为 DOM 结构;
-
HTML 解析器并不是等整个文档加载完成之后再解析,而是网络进程加载流多少数据,便解析多少数据;
-
字节流转换成 DOM 三个阶段:1、字节流转换为 Token;2、维护一个 Token 栈,遇到 StartTag Token 入栈,遇到 EndTag Token 出栈;3、为每个 Token 创建一个 DOM 节点;
-
JavaScript 文件和 CSS 样式表文件都会阻塞 DOM 解析;
渲染流水线:CSS 如何影响首次加载时的白屏时间?
-
DOM 构建结束之后,css 文件还未下载完成,渲染流水线空闲,因为下一步是合成布局树,合成布局树需要 CSSOM 和 DOM,这里需要等待 CSS 加载结束并解析成 CSSOM;
-
CSSOM 两个作用:提供给 JavaScript 操作样式表能力,为布局树的合成提供基础样式信息;
-
在执行 JavaScript 脚本之前,如果页面中包含了外部 CSS 文件的引用,或者通过 style 标签内置了 CSS 内容,那么渲染引擎还需要将这些内容转化为 CSSOM,因为 JavaScript 有修改 CSSOM 的能力,所以在执行 JavaScript 之前,还需要依赖 CSSOM。也就是说 CSS 在部分情况下也会阻塞 DOM 的生成。
分层和合成机制:为什么 CSS 动画比 JavaScript 高效
-
显示器固定刷新频率是 60HZ,即每秒更新 60 张图片,图片来自显卡的前缓冲区;
-
显卡的职责是合成新的图像,保存在后缓冲区,然后后缓冲区和前缓冲区互换,显卡更新频率和显示前刷新频率不一致,就会造成视觉上的卡顿;
-
渲染流水线生成的每一副图片称为一帧,生成一帧的方式有重排、重绘和合成三种;
-
重排会根据 CSSOM 和 DOM 计算布局树,重绘没有重新布局阶段;
-
生成布局树之后,渲染引擎根据布局树特点转化为层树,每一层解析出绘制列表;
-
栅格线程根据绘制列表中的指令生成图片,每一层对应一张图片,合成线程将这些图片合成一张图片,发送到后缓存区;
-
合成线程会将每个图层分割成大小固定的图块,优先绘制靠近视口的图块;
页面性能:如何系统优化页面
-
加载阶段:减少关键资源个数,降低关键资源大小,降低关键资源的 RTT 次数;
-
交互阶段:减少 JavaScript 脚本执行时间,避免强制同步布局:操作 DOM 的同时获取布局样式会引发,避免布局抖动:多次执行强制布局和抖动,合理利用 CSS 合成动画:标记 will-change,避免频繁的垃圾回收;
-
CSS 实现一些变形、渐变、动画等特效,这是由 CSS 触发的,并且是在合成线程中执行,这个过程称为合成,它不会触发重排或者重绘;
虚拟 DOM:虚拟 DOM 和真实 DOM 有何不同
-
当有数据更新时, React 会生产一个新的虚拟 DOM,然会拿新的虚拟 DOM 和之前的虚拟 DOM 进行比较,这个过程找出变化的节点,然后将变化的节点应用到 DOM 上;
-
最开始的时候,比较两个 DOM 的过程是在一个递归函数里执行的,其核心算法是 reconciliation。通常情况,这个比较过程执行很快,不过虚拟 DOM 比较复杂时,执行比较函数可能占据主线程比较久的时间,这样会导致其他任务的等待,造成页面卡顿。React 团队重写了 reconciliation 算法,称为 Fiber reconciler,之前老的算法称为 Stack reconciler;
PWA:解决 web 应用哪些问题
-
PWA(Progressive Web App),渐进式 Web 应用。一个渐进式过渡方案,让普通站点过渡到 Web 应用,降低站点改造代价,逐渐支持新技术,而不是一步到位;
-
PWA 引入 ServiceWorker 来试着解决离线存储和消息推送问题,引入 mainfest.json 来解决一级入口问题;
-
暗转了 ServiceWorker 模块之后,WebApp 请求资源时,会先通过 ServiceWorker,让它判断是返回 Serviceworker 缓存的资源还是重新去网络请求资源,一切的控制权交给 ServiceWorker 来处理;
-
在目前的 Chrome 架构中,Service Worker 是运行在浏览器进程中的,因为浏览器进程生命周期是最长的,所以在浏览器的生命周期内,能够为所有的页面提供服务;
WebComponent:像搭积木一样构建 web 应用
-
CSS 的全局属性会阻碍组件化,DOM 也是阻碍组件化的一个因素,因为页面中只有一个 DOM,任何地方都可以直接读取和修改 DOM;
-
WebComponent 提供了对局部试图封装能力,可以让 DOM、CSSOM 和 JavaScript 运行在局部环境中;
-
template 创建模版,查找模版内容,创建影子 DOM,模版添加到影子 DOM 上;
-
影子 DOM 可以隔离全局 CSS 和 DOM,但是 JavaScript 是不会被隔离的;
HTTP1:HTTP1 性能优化
-
HTTP/0.9 基于 TCP 协议,三次握手建立连接,发送一个 GET 请求行(没有请求头和请求体),服务器接收请求之后,读取对应 HTML 文件,数据以 ASCII 字符流返回,传输完成断开连接;
-
HTTP/1.0 增加请求头和响应头来进行协商,在发起请求时通过请求头告诉服务器它期待返回什么类型问题、什么形式压缩、什么语言以及文件编码。引入来状态吗,Cache 机制等;
-
HTTP/1.1 改进持久化连接,解决建立 TCP 连接、传输数据和断开连接带来的大量开销,支持在一个 TCP 连接上可以传输多个 HTTP 请求,目前浏览器对于一个域名同时允许建立 6 个 TCP 持久连接;
-
HTTP/1.1 引入 Chunk transfer 支持动态生成内容:服务器将数据分割成若干任意大小的数据块,每个数据块发送时附上上个数据块的长度,最后使用一个零长度的块作为发送数据完成的标志。在 HTTP/1.1 需要在响应头中设置完整的数据大小,如 Content-Length。
HTTP2:如何提升网络速度
-
HTTP/1.1 主要问题:TCP 慢启动;同时开启多条 TCP 连接,会竞争固定宽带;对头阻塞问题;
-
HTTP/2 在一个域名下只使用一个 TCP 长连接和消除对头阻塞问题;
-
多路复用的实现:HTTP/2 添加了二进制分帧层,将发送或响应数据经过二进制分帧处理,转化为一个个带有请求 ID 编号的帧,服务器或者浏览器接收到响应帧后,根据相同 ID 帧合并为一条完整信息;
-
设置请求优先级:发送请求可以设置请求优先级,服务器可以优先处理;
-
服务器推送:请求一个 HTML 页面,服务器可以知道引用了哪些 JavaScript 和 CSS 文件,附带一起发送给浏览器;
-
头部压缩:对请求头和响应头进行压缩;
HTTP3:甩掉 TCP、TCL 包袱,构建高效网络
-
虽然 HTTP/2 解决了应用层面的对头阻塞问题,不过和 HTTP/1.1 一样,HTTP/2 依然是基于 TCP 协议,而 TCP 最初是为了单连接而设计;
-
TCP 可以看成是计算机之间的一个虚拟管道,数据从一端发送到另一端会被拆分为一个个按照顺序排列的数据包,如果在传输过程中,有一个数据因为网络故障或者其他原因丢失,那么整个连接会处于暂停状态,只有等到该数据重新传输;
-
由于 TCP 协议僵化,也不可能使用新的协议,HTTP/3 选择了一个折衷的方法,基于现有的 UDP 协议,实现类似 TC 片多路复用,传输可靠等功能,称为 QULC 协议;
-
QULC 实现类似 TCP 流量控制,传输可靠功能;集成 TLS 加密功能;实现多路复用功能;
同源策略:为什么 XMLHttpRequst 不能跨域请求
-
协议、域名和端口号相同的 URL 是同源的;
-
同源策略会隔离不同源的 DOM、页面数据和网络通信;
-
页面可以引用第三方资源,不过暴露出诸如 XSS 问题,引入内容安全策略 CSP 限制;
-
默认 XMLHttpRequest 和 Fetch 不能跨站请求资源,引入跨域资源共享(CORS)进行跨域访问控制;
跨站脚本攻击 XSS:为什么 cookie 中有 httpOnly 属性
-
XSS 跨站脚本,往 HTML 文件中注入恶意代码,对用户实施攻击;
-
XSS 攻击主要有存储型 XSS 攻击、反射型 XSS 攻击和 DOM 的 XSS 攻击;
-
阻止 XSS 攻击:服务器对脚本进行过滤或转码,利用 CSP 策略,使用 HttpOnly;
CSRF 攻击:陌生连接不要随便点
-
CSRF 跨站请求伪造,利用用户的登录状态,通过第三方站点攻击;
-
避免 CSRF 攻击:利用 SameSite(三种模式:Strict、Lax、None) 让浏览器禁止第三方站点发起请求携带关键 Cookie;验证请求的来源站点,请求头中的 Referer 和 Origin 属性;利用 CSRF Token;
沙盒:页面和系统之间的隔离墙
-
浏览器被划分为浏览器内核和渲染内核两个核心模块,其中浏览器内核石油网络进程、浏览器主进程和 GPU 进程组成的,渲染内核就是渲染进程;
-
浏览器中的安全沙箱是利用操作系统提供的安全技术,让渲染进程在执行过程中无法访问或者修改操作系统中的数据,在渲染进程需要访问系统资源的时候,需要通过浏览器内核来实现,然后将访问的结果通过 IPC 转发给渲染进程;
-
站点隔离(Site Isolation)将同一站点(包含相同根域名和相同协议的地址)中相互关联的页面放到同一个渲染进程中执行;
-
实现站点隔离,就可以将恶意的 iframe 隔离在恶意进程内部,使得它无法继续访问其他 iframe 进程的内容,因此无法攻击其他站点;
HTTPS:让数据传输更安全
-
在 TCP 和 HTTP 之间插入一个安全层,所有经过安全层的数据都会被加密或者解密;
-
对称加密:浏览器发送加密套件列表和一个随机数 client-random,服务器会从加密套件中选取一个加密套件,然后生成一个随机数 service-random,返回给浏览器。这样浏览器和服务器都有相同 client-random 和 service-random,再用相同的方法将两者混合生成一个密钥 master secret,双方就可以进行数据加密传输了;
后话
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
对于面试,说几句个人观点。
面试,说到底是一种考试。正如我们一直批判应试教育脱离教育的本质,为了面试学习技术也脱离了技术的初心。但考试对于人才选拔的有效性是毋庸置疑的,几千年来一直如此。除非你有实力向公司证明你足够优秀,否则,还是得乖乖准备面试。这也并不妨碍你在通过面试之后按自己的方式学习。
其实在面试准备阶段,个人的收获是很大的,我也认为这是一种不错的学习方式。首先,面试问题大部分基础而且深入,这些是平时工作的基础。就好像我们之前一直不明白学习语文的意义,但它的意义就在每天的谈话间。
所谓面试造火箭,工作拧螺丝。面试往往有更高的要求,也迫使我们更专心更深入地去学习一些知识,也何尝不是一种好事。