xxx游-前端开发工程师
说一说操作系统中线程和进程的定义和区别?为什么一个进程要分出多个线程?
定义:
| 特性 | 进程 | 线程 |
|---|---|---|
| 定义 | 程序的一次执行实例 | 进程内的执行单位 |
| 资源 | 拥有独立的内存空间 | 共享进程的内存空间 |
| 创建开销 | 大 | 小 |
| 通信 | 进程间通信困难 | 线程间通信简单 |
| 同步 | 进程独立 | 需要同步机制 |
为什么使用多线程:
- 提高响应性 - UI 线程不阻塞
- 充分利用多核 - 并发执行
- 资源共享 - 内存空间共享,通信高效
- 简化编程 - 相比多进程
你了解哪些排序算法
- 冒泡排序 - O(n²)
- 快速排序 - O(n log n)
- 合并排序 - O(n log n)
- 插入排序 - O(n²)
- 选择排序 - O(n²)
- 堆排序 - O(n log n)
- 计数排序 - O(n+k)
口喷快速排序步骤?基准值一般取哪个值?
快速排序步骤:
- 选择基准值(Pivot)
- 分区:小于基准放左边,大于基准放右边
- 递归排序左右两个子数组
代码实现:
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[Math.floor(arr.length / 2)];
const left = arr.filter(x => x < pivot);
const middle = arr.filter(x => x === pivot);
const right = arr.filter(x => x > pivot);
return [...quickSort(left), ...middle, ...quickSort(right)];
}基准值选择:
- 第一个元素 - 简单但可能退化
- 最后一个元素 - 简单但可能退化
- 中间值(推荐)- 平衡性好
- 随机选择 - 避免最坏情况
什么是单链表?什么是循环链表?
单链表: 每个节点有数据和指向下一个节点的指针
头 → 节点1 → 节点2 → 节点3 → null循环链表: 最后一个节点指向第一个节点(形成环)
头 → 节点1 → 节点2 → 节点3 → 头(循环)单链表实现:
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkedList {
constructor() {
this.head = null;
}
append(data) {
const newNode = new Node(data);
if (!this.head) {
this.head = newNode;
} else {
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
}
}手撕单链表和循环链表?
单链表完整实现:
class SinglyLinkedList {
constructor() {
this.head = null;
this.size = 0;
}
append(data) {
const node = new Node(data);
if (!this.head) {
this.head = node;
} else {
let current = this.head;
while (current.next) current = current.next;
current.next = node;
}
this.size++;
}
insert(data, index) {
if (index < 0 || index > this.size) return false;
const node = new Node(data);
if (index === 0) {
node.next = this.head;
this.head = node;
} else {
let current = this.head;
let previous;
let count = 0;
while (count < index) {
previous = current;
current = current.next;
count++;
}
node.next = current;
previous.next = node;
}
this.size++;
return true;
}
remove(index) {
if (index < 0 || index >= this.size) return null;
let current = this.head;
if (index === 0) {
this.head = current.next;
} else {
let previous;
let count = 0;
while (count < index) {
previous = current;
current = current.next;
count++;
}
previous.next = current.next;
}
this.size--;
return current.data;
}
}
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}循环链表实现:
class CircularLinkedList extends SinglyLinkedList {
append(data) {
const node = new Node(data);
if (!this.head) {
this.head = node;
node.next = this.head; // 关键:指向自己
} else {
let current = this.head;
while (current.next !== this.head) { // 循环条件改变
current = current.next;
}
current.next = node;
node.next = this.head; // 新节点指向头
}
this.size++;
}
}说一说get和post有什么区别?
| 特性 | GET | POST |
|---|---|---|
| 用途 | 获取资源 | 提交数据 |
| 参数位置 | URL 查询字符串 | 请求体 |
| 安全性 | 低(可见) | 较高(隐藏) |
| 缓存 | 默认缓存 | 不缓存 |
| 数据长度 | 限制(URL 长度) | 无限制 |
| 编码 | URL 编码 | 多种编码方式 |
| 幂等性 | 幂等 | 非幂等 |
使用示例:
// GET
fetch('/api/users?page=1&limit=10');
// POST
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'John' })
});讲一讲 osi 七层协议
| 层 | 名称 | 功能 | 协议 |
|---|---|---|---|
| 7 | 应用层 | 应用程序、网络服务 | HTTP、HTTPS、FTP、SMTP |
| 6 | 表示层 | 数据加密、格式转换 | SSL/TLS、JPEG、GIF |
| 5 | 会话层 | 建立、维护、结束会话 | HTTP、HTTPS |
| 4 | 传输层 | 端到端通信、流量控制 | TCP、UDP |
| 3 | 网络层 | 路由、IP 寻址 | IP、ICMP、IGP |
| 2 | 数据链路层 | MAC 寻址、物理传输 | PPP、以太网 |
| 1 | 物理层 | 物理设备、信号传输 | 电压、光纤 |
TCP 和 HTTP 分别在哪一层
- TCP - 传输层(第 4 层)
- HTTP - 应用层(第 7 层),基于 TCP
TCP 和 UDP 的区别
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠性 | 可靠 | 不可靠 |
| 速度 | 慢 | 快 |
| 开销 | 大 | 小 |
| 用途 | 文件传输、邮件、HTTP | 音视频、DNS、游戏 |
| 顺序 | 保证顺序 | 不保证顺序 |
TCP 怎么保证稳定的连接?说一说三次握手四次挥手过程?
三次握手(建立连接):
客户端 服务器
| |
|-- SYN --> | (1) 客户端发送 SYN
| |
| <-- SYN-ACK --| (2) 服务器回复 SYN-ACK
| |
|-- ACK --> | (3) 客户端发送 ACK
| |
| 连接已建立 |四次挥手(关闭连接):
客户端 服务器
| |
|-- FIN --> | (1) 客户端发送 FIN
| |
| <-- ACK --| (2) 服务器回复 ACK
| |
| <-- FIN --| (3) 服务器发送 FIN
| |
|-- ACK --> | (4) 客户端发送 ACK
| |
| 连接已关闭 |为什么 TCP 可靠:
- 三次握手建立可靠连接
- 序列号标记每个字节
- 校验和验证数据完整性
- 重传机制处理丢失的包
- 流量控制和防拥塞
一个请求由哪些部分构成
HTTP 请求:
请求行
GET /api/users HTTP/1.1
请求头
Host: example.com
User-Agent: Mozilla/5.0
Content-Type: application/json
Authorization: Bearer token
空行
请求体(可选)
{ "name": "John" }HTTP 响应:
状态行
HTTP/1.1 200 OK
响应头
Content-Type: application/json
Content-Length: 100
Set-Cookie: xxx=yyy
空行
响应体
{ "id": 1, "name": "John" }说一说常见的请求头的字段
| 请求头 | 说明 |
|---|---|
Accept | 客户端接受的内容类型 |
Accept-Encoding | 接受的编码方式(gzip、deflate) |
Accept-Language | 接受的语言 |
Authorization | 身份认证凭证 |
Cache-Control | 缓存策略 |
Content-Type | 请求体的内容类型 |
Content-Length | 请求体的长度 |
Host | 目标服务器地址 |
Referer | 来源页面 |
User-Agent | 客户端信息 |
If-Modified-Since | 条件请求,检查资源是否修改 |
If-None-Match | 条件请求,检查 ETag 是否匹配 |
讲一讲你了解的状态码
| 状态码 | 说明 | 例子 |
|---|---|---|
| 1xx | 信息 | 100 Continue |
| 2xx | 成功 | 200 OK、201 Created、204 No Content |
| 3xx | 重定向 | 301 Moved、302 Found、304 Not Modified |
| 4xx | 客户端错误 | 400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found |
| 5xx | 服务器错误 | 500 Internal Server Error、502 Bad Gateway、503 Service Unavailable |
说一说为什么会有跨域这个问题?怎么解决?
原因: 浏览器同源策略(协议、域名、端口相同才算同源)
解决方案:
-
CORS - 后端设置响应头
res.set('Access-Control-Allow-Origin', '*'); -
代理 - 开发环境或同源代理
-
JSONP - 仅支持 GET
-
PostMessage - 跨窗口通信
正向代理和反向代理的区别
| 特性 | 正向代理 | 反向代理 |
|---|---|---|
| 位置 | 客户端侧 | 服务器侧 |
| 作用 | 隐藏客户端 | 隐藏服务器 |
| 用途 | VPN、科学上网 | 负载均衡、CDN |
| 区别 | 客户端知道代理 | 客户端不知道代理 |
输入一个 url 地址到页面渲染的过程
- DNS 解析 - URL → IP 地址
- TCP 连接 - 三次握手建立连接
- HTTP 请求 - 浏览器发送请求
- 服务器响应 - 返回 HTML、CSS、JS
- 解析 HTML - 构建 DOM 树
- 加载资源 - 并行加载 CSS、JS、图片
- 解析 CSS - 构建 CSSOM 树
- 构建渲染树 - 合并 DOM 和 CSSOM
- 布局 - 计算元素位置和大小
- 绘制 - 填充像素点
- 合成 - GPU 加速渲染
Js 和 Css 加载会不会影响 Dom 的解析
| 资源 | 影响 | 说明 |
|---|---|---|
| CSS | 不阻塞 | CSS 放在 head,不阻塞 DOM 解析 |
| JS | 阻塞 | JS 执行会阻塞 DOM 解析(内联或普通加载) |
优化:
<!-- JS 延迟加载,不阻塞 DOM -->
<script defer src="script.js"></script>
<!-- JS 异步加载 -->
<script async src="script.js"></script>原型和原型链
原型: 对象的隐式属性 __proto__,指向构造函数的 prototype
原型链: 对象通过 __proto__ 链接形成的继承链
const obj = {};
obj.__proto__ === Object.prototype;
Object.prototype.__proto__ === null; // 原型链顶端怎么通过原型链进行继承?最顶端会找到哪里?
原型链继承:
function Parent() {
this.x = 1;
}
function Child() {
this.y = 2;
}
Child.prototype = new Parent();
const child = new Child();
console.log(child.x); // 1(通过原型链找到)原型链最顶端:
child.__proto__ = Child.prototype (Parent 实例)
→ Parent.prototype
→ Object.prototype
→ null(最顶端)判断一个对象的属性是否为自己的属性, 不是原型链上的属性?
const obj = { x: 1 };
Object.setPrototypeOf(obj, { y: 2 });
// hasOwnProperty:仅检查自身属性
console.log(obj.hasOwnProperty('x')); // true
console.log(obj.hasOwnProperty('y')); // false
// in 操作符:检查自身和原型链
console.log('x' in obj); // true
console.log('y' in obj); // truelet const var 的区别
| 特性 | var | let | const |
|---|---|---|---|
| 作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
| 提升 | 提升(undefined) | 不提升 | 不提升 |
| 重新声明 | ✅ 可以 | ❌ 不能 | ❌ 不能 |
| 重新赋值 | ✅ 可以 | ✅ 可以 | ❌ 不能 |
| 初始值 | 可选 | 可选 | 必须 |
说一说对闭包的理解
闭包: 函数能够访问外层作用域的变量
function outer() {
const x = 1;
return function inner() {
return x;
};
}
const fn = outer();
console.log(fn()); // 1常见用途:
- 数据隐藏 - 私有变量
- 工厂函数 - 创建带状态的函数
- 回调函数 - 保存上下文
手撕防抖
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 使用
const search = debounce((query) => {
console.log(`搜索: ${query}`);
}, 500);
input.addEventListener('input', (e) => {
search(e.target.value);
});讲讲事件循环机制?微任务会一次性清空一个, 还是全部清除? 执行微任务的过程中又产生了微任务会什么时候执行? 什么时候会产生?
事件循环流程:
- 执行同步代码
- 微任务队列清空(全部执行)
- 宏任务执行一个
- 重复 2-3 步
微任务全部清除:
Promise.resolve()
.then(() => {
console.log('1');
Promise.resolve().then(() => console.log('2'));
})
.then(() => {
console.log('3');
});
// 输出:1 2 3
// 微任务队列不是逐个清除,而是一直执行直到清空微任务产生时机:
- Promise.then/catch/finally
- async/await
- MutationObserver
- queueMicrotask()
有哪些写异步的方法
- 回调函数 - Callback
- Promise - then/catch
- async/await - 基于 Promise
- 生成器 - Generator(yield)
- 事件监听
- 发布订阅
- RxJS - Observable
用过 React 的 Hooks 吗?
常用 Hooks:
// useState:状态
const [count, setCount] = useState(0);
// useEffect:副作用
useEffect(() => {
// 组件挂载、更新时执行
return () => {
// 清理函数
};
}, [dependencies]);
// useContext:上下文
const value = useContext(MyContext);
// useReducer:复杂状态管理
const [state, dispatch] = useReducer(reducer, initialState);
// useMemo:记忆值
const memoValue = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]);
// useCallback:记忆函数
const memoFn = useCallback(() => {
doSomething(a, b);
}, [a, b]);
// useRef:保持引用
const inputRef = useRef(null);讲讲 Promise 的 all 和 race 方法
Promise.all
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(values => {
console.log(values); // [1, 2, 3]
});
// 一个失败,整体失败
Promise.all([
Promise.resolve(1),
Promise.reject('error')
]).catch(err => {
console.log(err); // 'error'
});Promise.race
Promise.race([
Promise.resolve(1),
new Promise(r => setTimeout(() => r(2), 100))
]).then(value => {
console.log(value); // 1(首个完成)
});vue 的生命周期
Vue 2:
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
Vue 3(Composition API):
- setup(替代 beforeCreate、created)
- onBeforeMount
- onMounted
- onBeforeUpdate
- onUpdated
- onBeforeUnmount
- onUnmounted
Http1.1 和 Http2.0 的区别
| 特性 | HTTP/1.1 | HTTP/2.0 |
|---|---|---|
| 多路复用 | ❌ 每个请求一个连接 | ✅ 一个连接多个流 |
| 头压缩 | ❌ 原始格式 | ✅ HPACK 压缩 |
| 服务器推送 | ❌ 无 | ✅ 有 |
| 二进制分帧 | ❌ 文本 | ✅ 二进制 |
| 性能 | 基准 | 快 2-3 倍 |
讲讲 Https 的握手过程
客户端 服务器
| |
|-- ClientHello---| (1) 发送支持的加密套件
| |
|<-- ServerHello --| (2) 选择加密套件、发送证书
| |
|<-- Certificate --| (3) 服务器证书
| |
|<-- ServerKeyExchange --| (4) 密钥交换
| |
|-- ClientKeyExchange--| (5) 客户端密钥交换
| |
|-- ChangeCipherSpec--| (6) 通知改用加密
| |
|-- Finished ---| (7) 握手完成
| |
|<-- ChangeCipherSpec--| (8) 服务器通知
| |
|<-- Finished --| (9) 服务器握手完成
| |
| 加密通信开始 |强缓存和协商缓存
| 特性 | 强缓存 | 协商缓存 |
|---|---|---|
| 是否请求 | ❌ 不请求 | ✅ 发请求验证 |
| 状态码 | 200 | 304 |
| 头设置 | Expires、Cache-Control | Last-Modified、ETag |
| 性能 | 最快 | 一般 |
强缓存:
Cache-Control: max-age=3600 // 1小时内直接使用
Expires: Wed, 21 Oct 2025 07:28:00 GMT协商缓存:
Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT
ETag: "abc123"
If-None-Match: "abc123"302 是什么? 403 是什么? 401 是什么?
| 状态码 | 说明 |
|---|---|
| 302 | 临时重定向(Found) |
| 403 | 禁止访问(Forbidden) |
| 401 | 未授权(Unauthorized) |
哪些地方需要用到缓存
- HTTP 缓存 - 浏览器缓存
- LocalStorage/SessionStorage - 客户端缓存
- 数据库缓存 - Redis、Memcached
- CDN 缓存 - 静态资源分发
- 浏览器内存缓存 - 计算结果
- 接口响应缓存 - API 结果
CPU 的调度算法
- FCFS - 先来先服务
- SJF - 最短作业优先
- 优先级调度 - 按优先级
- 时间片轮转 - 轮询
- 多级反馈队列 - 组合算法
链表的结构? 跟数组的区别
链表结构: 节点 = 数据 + 指针
对比:
| 特性 | 数组 | 链表 |
|---|---|---|
| 访问 | O(1) | O(n) |
| 插入 | O(n) | O(1) |
| 删除 | O(n) | O(1) |
| 内存 | 连续 | 离散 |
| 大小 | 固定(通常) | 动态 |
Last updated on