不论是实习还是社招都该看看的八股

339 阅读14分钟

前言

对学生而言25秋招已经差不多结束了,对社招同事们而言大部分人心里也是想年底了还是老实点待完今年吧。 自己经过今年的历练学成长学习到了很多很多,结识了许多志同道合的未来程序员与在职大佬们。不出意外今年就先稳定下来了。给大家下列一些最最常见的一些八股吧,一定学会表达并理解。

以下Vue与React内容想要理解的话可能需要部分源码解读理解。

image.png

image.png

js 为什么设计为单线程

1. 简化编程模型

易学易用:单线程模型相对简单,更容易理解和使用。对于初学者来说,不需要深入了解复杂的并发编程概念,如锁、同步等问题。

避免竞态条件:多线程环境下容易出现竞态条件(race conditions),即多个线程同时访问和修改同一个数据,导致不可预测的结果。单线程模型避免了这些问题。

2. 避免内存和资源管理问题

内存管理:单线程环境下,内存管理和垃圾回收相对简单。JavaScript 的垃圾回收机制可以更有效地管理内存,而不必担心多线程环境下的复杂性。

资源竞争:多线程环境中,多个线程可能竞争相同的资源(如文件句柄、网络连接等),导致资源竞争和死锁问题。单线程模型避免了这些问题。

3. 浏览器环境的特殊性

DOM 操作:浏览器中的 DOM 是线程不安全的。如果允许多线程操作 DOM,可能会导致严重的同步问题和不一致的状态。单线程模型确保了 DOM 操作的安全性。

用户界面:浏览器的用户界面需要实时响应用户输入和事件。单线程模型确保了事件处理的顺序性和一致性,避免了多线程环境下的不确定行为。

4. 事件循环机制

异步编程:JavaScript 通过事件循环机制实现了高效的异步编程。事件循环允许 JavaScript 在单线程环境下处理多个任务,而不会阻塞主线程。

非阻塞 I/O:Node.js 等服务器端 JavaScript 环境利用事件循环和非阻塞 I/O 模型,实现了高性能的并发处理能力。

Vite 构建项目的过程

当使用 Vite 构建项目时,以下几个步骤会发生:

1.初始化项目:

创建一个新的 Vite 项目通常通过 npm init vite@latest 或 yarn create vite 命令完成,这会创建一个基于模板的新项目目录结构。

2.启动开发服务器:

使用 npm run dev 或 yarn dev 启动 Vite 的开发服务器。这个服务器提供了实时重载功能,能够快速预览代码更改效果。

3.解析模块:

Vite 会解析你的 JavaScript 文件,查找导入语句(import),并按需加载相应的模块。由于现代浏览器直接支持 ES 模块,Vite 利用了这一点来减少编译时间。

4.热模块替换(HMR)

当文件发生变化时,Vite 提供了 HMR 功能,这意味着浏览器可以部分刷新受影响的部分,而不是整个页面。这样可以显著减少开发过程中的等待时间。

5.代码拆分:

Vite 支持基于路由的代码拆分,这意味着只有当前需要的代码会被加载到浏览器中,其余部分则在需要时懒加载,从而减少初始加载时间。

6.生产构建:

当准备发布应用时,使用 npm run build 或 yarn build 命令来进行生产环境的构建。这一步会进行代码压缩、混淆等优化措施,以及生成静态资源文件,确保最终的应用体积小且性能好。

进程和线程的区别

进程是程序在一个给定的数据集合上的一次运行活动,它是系统进行资源分配和调度的一个独立的基本单位。

线程是进程中的一个执行单元,一个进程可以拥有多个线程。线程是操作系统能够进行运算调度的最小单位。

区别总结

1.资源隔离:进程有自己独立的内存空间,而线程共享所属进程的内存空间。

2.系统开销:创建和销毁进程涉及更多的系统资源分配,而线程则相对轻量。

3.通信机制:进程间的通信复杂,而线程间可以直接访问共享变量。

4.安全性:进程更安全,因为它们的隔离性更强;线程的安全性较差,容易受到其他线程的影响。

5.并发能力:进程可以更好地利用多处理器架构,但由于线程间切换开销小,它们也能提供较高的并发度。

进程的生命周期

创建(Creation)

当一个进程被启动时,操作系统会为其分配必要的资源,如内存空间、文件句柄等,并初始化进程控制块(Process Control Block, PCB)。PCB 是操作系统用来存储与进程相关的信息的结构,如进程的状态、上下文信息等。

就绪(Ready)

一旦进程被创建并且初始化完毕,它就会进入就绪状态,等待被调度。处于就绪状态的进程意味着它已经准备好运行,只需要得到处理器的时间片就可以开始执行。

运行(Running)

当进程被调度器选中并分配给处理器时,它便进入了运行状态。此时,进程正在执行指令。

阻塞(Blocked)

在运行过程中,进程可能因为等待某些事件的发生(如 I/O 请求完成)而暂时无法继续执行。这时,进程会被标记为阻塞状态。操作系统会将处理器分配给其他就绪状态的进程。一旦阻塞的原因消除,进程会回到就绪状态。

终止(Terminated)

进程正常结束执行或者由于错误而被异常终止时,它会进入终止状态。此时,操作系统会回收该进程占用的资源,并清理与之相关的数据结构。

线程里面的死锁,解决方法

死锁是指两个或多个线程在执行过程中因争夺资源而造成的一种僵局状态,即每个线程都在等待另一个线程释放资源。

1. 银行家算法

2. 占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源

3. 保证每次只有一个线程可以使用资源。这通常是通过使用锁

4. 锁的层次结构:为锁定义一个层次结构,确保线程总是先获取较低级别的锁,然后再尝试获取更高级别的锁。

  1. 一次性获取所有资源:在开始时请求所有需要的资源,而不是在运行时逐个请求

进程间的通信方式有哪些

1. 管道是一种半双工的通信方式,数据只能单向流动,即一个进程向管道中写入数据,另一个进程从中读取数据。

2. 消息队列允许进程间发送和接收消息,消息可以携带任意数量的数据。

3. 共享内存是一种快速的 IPC 方式,它允许两个或多个进程共享一块内存区域。

  1. 套接字是一种用于进程间通信的端点,可以跨越网络或在同一主机上使用。

HTTPS的工作原理如下:

TLS 握手分为几个步骤,主要包括:

1.客户端问候(Client Hello):客户端向服务器发送一个客户端问候消息,包含客户端支持的TLS版本、密码套件列表、随机数(Client Random)等信息。

2.服务器问候(Server Hello):服务器回应客户端,选择一个密码套件,并发送服务器随机数(Server Random)、证书等信息。

3.证书验证:客户端验证服务器的证书,确保服务器的身份。

4.客户端密钥交换(Client Key Exchange):客户端生成一个预主密钥(Pre-Master Secret),并使用服务器公钥对其进行加密,然后发送给服务器。

5.计算会话密钥:客户端和服务器各自使用预主密钥、客户端随机数和服务器随机数计算出会话密钥。

完成消息(Finished):双方发送完成消息,确认握手成功,使用会话密钥加密数据。

DNS服务器间的交互过程

当本地DNS服务器接收到一个域名解析请求时,它会根据以下步骤来查找该域名对应的IP地址:

1. 检查缓存:DNS服务器首先会在自己的缓存中查找是否有这个域名对应的记录。如果有,则直接返回给客户端。

2. 递归查询:如果没有找到记录,DNS服务器会作为客户端向其他的DNS服务器发起请求。这个过程是递归的,即本地DNS服务器会一直追踪直到找到答案为止。

3. 根DNS服务器:如果本地DNS服务器无法直接获取到所需的信息,它会询问根DNS服务器(root DNS server)。根DNS服务器不会包含具体的域名解析信息,但它知道顶级域名服务器的位置。

4. 顶级域名服务器(TLD Server) :顶级域名服务器会告诉本地DNS服务器去哪个权威DNS服务器寻找答案。

5. 权威DNS服务器:权威DNS服务器负责管理特定域名的所有者提供的DNS记录。如果权威DNS服务器有这个域名的记录,就会返回给本地DNS服务器;如果没有,则会返回一个指向更具体的权威DNS服务器的地址。

6. 返回结果:一旦找到正确的IP地址,这个信息会被返回给请求的DNS服务器,并最终返回给最初发出请求的客户端。

在整个过程中,DNS服务器之间通过UDP或TCP协议进行通信。通常情况下,DNS查询使用的是UDP协议,因为它简单、速度快;但是,当响应数据过大时,DNS服务器可能会切换到TCP协议来传输数据。

COS 预检请求options

OPTIONS 请求的主要目的确实是为了防止跨域请求直接发送带有复杂资源的请求,从而避免不必要的流量浪费和减轻服务器的并发处理压力。通过预检请求,服务器可以提前告知客户端是否允许实际请求,从而优化网络资源的使用。

React Hooks与类组件有什么差异

1. 状态管理和生命周期的封装: 

2. 状态逻辑的复用:在没有 Hooks 的情况下,复用状态逻辑通常需要使用高阶组件(HOC)或 Render Props,这使得代码难以理解和维护。

3. 代码的可读性和可维护性:类组件的生命周期方法和状态管理常常分散在多个地方,使得代码难以追踪和维护。

4. This问题需要常用aplly,等修改this指向的方法

5. 优化渲染过程:虽然 Hooks 本身不会直接改变 React 的渲染机制,但它们使得编写更高效的组件变得更加容易。例如,你可以利用 useMemo 和 useCallback 来缓存昂贵的计算结果或函数引用,防止不必要的重渲染。

react的fiber解决了什么问题

fiber的主要优势在于:

可中断的渲染:Fiber 允许将大的渲染任务拆分成多个小的工作单元(Unit of Work),使得 React 可以在空闲时间执行这些小任务。当浏览器需要处理更高优先级的任务时(如用户输入、动画),可以暂停渲染,先处理这些任务,然后再恢复未完成的渲染工作。

使得React能够在长时间的任务中穿插其他任务,如用户输入处理。通过检索flags与subtreeFlags来检测该fiber是不是渲染完成了。

(flags 字段用于标记当前 Fiber 节点的副作用(side effects),例如插入、删除、更新等。

subtreeFlags 字段用于标记当前 Fiber 节点及其子树中的副作用。

如果一个 Fiber 节点的 flags 和 subtreeFlags 字段都没有设置副作用标记,说明该节点及其子树已经渲染完毕。)

优先级调度:在 Fiber 架构下,React 可以根据不同任务的优先级决定何时更新哪些部分。React 会优先更新用户可感知的部分(如动画、用户输入),而低优先级的任务(如数据加载后的界面更新)可以延后执行。

双缓存树 优化Diff (Fiber Tree) :Fiber 架构中有两棵 Fiber 树——current fiber tree(当前正在渲染的 Fiber 树)和 work in progress fiber tree(正在处理的 Fiber 树)。React 使用这两棵树来保存更新前后的状态,从而更高效地进行比较和更新。 

任务切片:在浏览器的空闲时间内(利用 requestIdleCallback思想),React 可以将渲染任务拆分成多个小片段,逐步完成 Fiber 树的构建,避免一次性完成所有渲染任务导致的阻塞。

Vue与react区别

1.书写规则:Vue使用模板字符串的写法,React使用tsx语法

2.数据管理:

Vue使用defineProperty与Proxy代理的数据数据劫持,与Watcher与dep收集对应依赖数据的回调,来触发响应式数据更改

React使用useState,useReducer,hooks来实现数据驱动视图,这些hooks以链表的形式存在组件级别的function的fiber节点上,当某个state发生改变,将会使得整个函数组件重新执行

3.视图更新:

Vue是将数据发生改变的响应式数据改变的需要更新的回调函数收集起来,再数据发生改变后执行这些回调,而不是执行整个script脚本,所以Vue是局部视图更新的渲染

而React是通过以useState,useReducer为更新的依赖,当状态发生改变,整个当前的函数组件将重新执行,生成新的fiber,并重新渲染,当执行到更高级的任务,会暂停当前任务的执行,去执行优先级高的任务(fiber任务级别,切片任务级别增量渲染)

4. 事件管理:

Vue是将真实的事件绑定在真实DOM上,而React是使用事件代理来实现的事件触发任务,减少了绑定事件的优化

虚拟DOM的优点

性能优化:虚拟DOM能够减少直接对真实DOM的操作次数,因为DOM操作是非常耗费资源的。通过比较前后两个虚拟DOM树的差异(diff算法),只对发生变化的部分进行实际的DOM更新,可以显著提升页面的响应速度和用户体验。

跨平台支持:由于虚拟DOM是基于JavaScript实现的,因此它可以很容易地被移植到不同的环境和平台上使用,例如服务器端渲染(SSR)、移动设备等。

简化开发:框架如React利用虚拟DOM的概念使得开发者无需关心具体的DOM操作细节,只需关注数据的变化即可,这简化了复杂的UI逻辑处理。

虚拟DOM的缺点

内存消耗:虽然虚拟DOM提高了渲染效率,但是它需要额外的内存空间来存储虚拟DOM树及其状态,对于大规模的应用或资源受限的设备来说,可能会成为一个负担。

初次渲染延迟:首次加载应用时,构建虚拟DOM树并将其转换为真实DOM的过程会增加一定的初始化时间,导致首屏加载速度变慢。

复杂性增加:对于一些简单的应用场景,引入虚拟DOM机制可能反而增加了不必要的复杂度。此外,理解虚拟DOM的工作原理以及如何高效地使用相关框架,也需要一定的学习成本。