昨日进行了滴滴的视频面试, 大概三个小时吧, 总共三面. 题目总体而言都是考察基础知识, 这一方面并没有太多的疑虑. 虽然说三面的一道手撕代码挂了, 但是还是收获满满. 在面试期间将遇到的题目偷偷写到了纸上, 花几天时间做个总结.

更新


[2019-11-8]

  • Initial Release

[2019-11-11]

Added

[2019-11-13]

Added

[2019-11-15]

Added

[2019-11-18]

Added

[2019-11-25]

Added

Changed

[2019-12-11]

Changed

  • 取消置顶

目录


一面


1. 自我介绍

2. 求一个数组的和

2.1 问题描述

给定一个数组: [1, 2, 3, 4, 5], 求数组中各项的和.

2.2 问题解决

使用 reduce 即可:

2.3 参考

3. URL 替换

3.1 问题描述

给定一个 URL(https://didi.com?a=duan&b=zhao&c=21), 和一个键数组(['a', 'b']), 要求从原字符串的参数去掉对应的键, 返回新的 URL(https://didi.com?c=21).

3.2 问题解决

  • 提取 URL 的参数至对象中
  • 删除指定的键
  • 重新拼接 URL

3.3 参考

4. a.coma.com:8080 是跨域吗?

4.1 问题描述

4.2 问题解决

这个问题, 要从两个方面入手:

  1. 应用层的协议
  2. 跨域

由于 a.com 的默认端口号为 80|443, 与 a.com:8080 的端口号不相同, 而同源策略规定了 协议域名端口号 这三者任一种不相同都会产生跨域, 所以 a.coma.com:8080 是跨域的.

4.3 参考

5. 一个 HTTP 请求的结构

5.1 问题描述

5.2 问题解决

POST 请求为例, 主要包括三个部分:

  • 请求行
  • 请求头
  • 请求体
1
2
3
4
5
6
7
8
9
POST /images/1.png HTTP/1.1

Origin: xxx
User-Agent: xxx

{
username: 'duanzhaoyang',
userpwd: 'xxxxxxxxxxxxxxx',
}

5.3 参考

6. 如何解决跨域?

6.1 问题描述

6.2 问题解决

跨域是什么?

向不同 协议域名端口号 的其它服务器发起请求时, 会产生跨域

为什么会产生跨域?

由于浏览器的同源策略的限制

怎么解决跨域?

  • jsonp
    • 通过 js 动态创建 script 标签, 利用 scriptimgiframevideo 等标签不受同源策略限制的特点, 携带一个回调函数, 向指定 URL 的服务器发起请求.
  • CORS
    • 服务端设置一个 Access-Control-Allow-Origin 响应头来允许特定的域名的请求
  • Websocket
  • Nginx

6.3 参考

7. linux 的文件操作命令

7.1 问题描述

  1. linux 创建目录, 使用哪个命令?
  2. 递归创建一个目录, 会创建同名的不存在的目录, 使用哪个命令?
  3. 查看目录的结构, 使用哪些命令?

7.2 问题解决

1
2
3
4
5
6
7
8
9
10
# 创建目录
mkdir xxx

# 递归创建目录, 并且创建路径上的同名的不存在的目录
mkdir -p

# 查看目录的结构
ls
ls -al
ll

7.3 参考

8. git fetchgit pull 的区别?

8.1 问题描述

8.2 问题解决

  • git pull = git fetch + git merge
  • git pull 拉取远程分支到本地, 并自动合并, 而 git fetch 则不会自动合并, 需要手动 git diff, 再合并

8.3 参考

9. git 回滚操作

9.1 问题描述

我想回退到某个古老的 commit 版本, 我该怎么做?

9.2 问题解决

1
2
3
4
5
# 1. 查看当前分支的 commit 记录
git log

# 2. 回退到指定版本
git reset --hard <commit_id>

上述的 操作二 会将 HEAD 指针指向指定的 commit. 此时, 如果我后悔了, 想回到最新的 commit, 应该怎么做?

1
2
3
4
5
# 1. 查看当前仓库所有的 commit 记录
git reflog

# 2. 回退到最新版本
git reset --hard <commit_id>

9.3 参考

10. ReactHooks 为什么要按照顺序调用?

10.1 问题描述

10.2 问题解决

Hooks单链表 的形式存储于函数组件的 Fiber.memorizedState 属性上, 在源码种, 通过 workInProgressHook 这个变量来指向对应的 Hook 对象.

我们可能在一个函数组件内部, 声明多个同类型的 Hooks, 比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React from 'react';

function TestHooks(props) {
// 1. useState
const [name, setName] = React.useState('duanzhaoyang');

// 2. useEffect
React.useEffect(() => {
console.log('componentDidMount');
}, []);

// 3. useState
const [age, setAge] = React.useState(21);

// 4. useEffect
React.useEffect(() => {
console.log('componentDidUpdate');
});
}

在上面的示例代码中, 我声明了两个 useState, 两者互不影响. 这是怎么做到的?

事实上, React 内部维护了一个名为 workInProgressHook 的变量, 代码执行的过程中, 每遇到一个 Hooks, 都会移动指针, 使得组件中定义的 HoooksFiber 上存储的 Hooks 一一对应.

这也引申出了一个问题: React 的 Hooks 为什么要写在最外部?

假如有这样一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from 'react';

function TestHooks2(props) {
// 1. useState
const [name, setName] = React.useState('duanzhaoyang');

// 2. useEffect
React.useEffect(() => {
console.log('componentDidMount');
}, []);

// 3. useState
if(name) {
const [age, setAge] = React.useState(21);
}

// 4. useEffect
React.useEffect(() => {
console.log('componentDidUpdate');
});
}

useState 被包裹在判断语句内, 假设该 if 语句成立, 则 Hooks 会以正常的模式渲染; 反之, 就会报错, 因为此时代码已经执行到了流程四(useEffect), 但是 workInProgressHook 依然指向流程三对应的 Hooks.

10.3 参考

11. ReactHooks 为什么要写在最外部?

11.1 问题描述

为什么不能将 Hooks 声明写在条件判断循环或者内层函数内部, 这会导致什么问题?

11.2 问题解决

参见: ReactHooks 为什么要按照顺序调用?

11.3 参考

12. 说说 React 的生命周期

12.1 问题描述

12.2 问题解决

1
2
3
4
5
6
7
8
9
10
11
12
13
// React-v16.4

// 1. 挂载阶段
// constructor -> getDerivedStateFromProps -> render -> componentDidMount

// 2. 更新阶段
// getDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate

// 3. 报错阶段
// componentDidCatch

// 4. 卸载阶段
// componentWillUnmount

12.3 参考

13. 说说 NodeJS 的中间件机制

13.1 问题描述

13.2 问题解决

13.3 参考

14. 分别说下 TSJS 的优点和缺点

14.1 问题描述

14.2 问题解决

TS 的优势

  • TS 可以看作是 Type + ES, 它是一个静态类型的语言, 提供了相对完善的静态类型检查, 与传统的 ES 相比, 可以在编写代码时很方便的发现错误, 而 ES 则需要在执行时才能发现
  • 与传统的 ES + Flow 模式相比, 避免了编写大量繁杂的函数注释, 更加适合大型应用. 我在阅读 React 源码的过程中, 有接触到 Flow, 但是给我的感觉是体验极差, 并不能跳转到对应的类型定义处
  • 最重要的一点就是, TS 在 ES 的基础上, 完善了类的概念, 包括 privateprotectedpublic, 同时也提出了一些新的概念, 比如接口等

14.3 参考

15. 实现 Promise

15.1 问题描述

15.2 问题解决

15.3 参考

16. 如何用 setTimeout 来模拟 setInterval?

16.1 问题描述

16.2 问题解决

16.3 参考

二面


17. 自我介绍

18. 你的职业规划提到了全栈, 那你现在是怎么准备的?

18.1 问题描述

18.2 问题解决

目前来说, 先去熟练前端的主流技术栈, 等工作之后再去根据实际情况, 探索公司的具体的后端技术栈, 向全栈方向发展.

18.3 参考

19. 你为什么要参与开源项目?

19.1 问题描述

19.2 问题解决

19.3 参考

20. 为什么要使用 TS, 它解决了什么问题?

20.1 问题描述

20.2 问题解决

其实这个问题, 一面的时候已经问到了, 个人认为, 使用 TS 主要有三个方面的优势:

  1. TS 是一个静态类型语言, 拥有较为完整的静态类型检查机制, 在编写代码时就可以发现潜在的错误, 而反观传统的 ES, 则需要在代码编译执行时才能揪出存在的错误.
  2. 实际上, 使用 ES + Flow 可以提供简单的类型检查, 但我个人认为这种方式是具有一定缺陷的——比如我在阅读 React 源码的过程中, 注意到 React 源码中有使用到了 Flow, 但是给我的感觉就是——用户体验很差, 不能跳转到类型声明的地方.
  3. TS 最大的优势在于, 它既继承了传统 ES 的诸多优秀特性, 又推出了一些新的特性(接口)等.

20.3 参考

21. VS Code 的插件是你独立开发的吧?

21.1 问题描述

21.2 问题解决

灵感来源

起初在 VS Coderepo 看到了国外开发者提的一个 issue, 原意就是他需要在 VS Code 的某个工作区文件夹内快速查找一个文件.

需求分析

实际上, VS Code 已经集成了查找文件的功能, 但是是有明显的缺陷的, 它只能在所有工作区文件夹内查找, 而我只需要在特定的文件夹内查找文件.

潜在问题

之前写过一个初始版本, 但是是存在一些问题的, 比如:

  • 不能输入提示
  • 无法处理回退时产生的问题

一直有一个重构插件的计划, 但是由于电脑使用过代理, 导致 VS Code 的插件开发服务器无法正常启动, 进而该计划一直被搁置, 目前仍未解决.

21.3 参考

22. 你目前正在参与的开源项目

22.1 问题描述

22.2 问题解决

22.3 参考

23. TS 在什么情况下使用 any?

23.1 问题描述

23.2 问题解决

场景一: 工具函数

any, 顾名思义, 就是 任意 的意思. 当一个函数, 需要接收任意类型的参数的时候, 可以使用它:

1
2
3
4
// 获取值的类型
function extractValueType(value: any): string {
return ({}).toString.call(null, value);
}

23.3 参考

24. 有了解哪些 redux 的异步中间件?

24.1 问题描述

24.2 问题解决

由于最早接触的是 redux-thunk, 其实也有很多优秀地数据管理库, 比如: redux-sagaredux-promise

对于 redux-thunk, 有必要了解下它的原理, 正所谓知其然知其所以然.

24.3 参考

25. redux-thunk 的原理是什么, 它的优势在哪里?

25.1 问题描述

25.2 问题解决

原生 dispatch 的劣势

redux 通过 dispatch 来派发一个 action 来操作数据, 那么假如我需要记录每次 dispatch 的记录, 应该怎么做? 最简单的方法应该是:

1
console.log('dispatch...', action);

但是这样会产生大量的重复代码, 所以需要对于原生的 dispatch 进行一层封装, 需要用到 applyMiddleware

applyMiddleware

redux 通过暴露出 applyMiddleware 方法, 使得开发者可以很方便的自定义 dispatch.

redux-thunk

redux-thunk 的原理很简单, 改造 dispatch, 返回一个接收 store 作为对象的函数, 在异步任务执行完成的时候再进行 dispatch.

redux-thunk 的优势

就一个词: 简单易用, 很容易理解

25.3 参考

26. 你对代码质量的要求是怎样的?

26.1 问题描述

26.2 问题解决

26.3 参考

27. CSS 的大小单位有哪些?

27.1 问题描述

27.2 问题解决

  • 百分比(%)
  • px
  • rem
    • 相对于 html 根元素来计算大小
  • em
    • 相对于父级元素来计算大小
  • vw
    • 浏览器视口的宽度

27.3 参考

28. 使用 CSS 在不占用多余空间的情况下画一个三角形

28.1 问题描述

注意题目, 要求不占用额外的空间

28.2 问题解决

28.3 参考

29. CSS 计算题

29.1 问题描述

给定一个容器 div(width: 100, height: 200) 和一个子代 div(width: 20, height: 10, padding: 10%), 求子代 div 元素的宽度.

1
2
3
<div class="parent">
<div class="child"></div>
</div>
1
2
3
4
5
6
7
8
9
10
.parent {
width: 100px;
height: 200px;
}

.child {
width: 20px;
height: 10px;
padding: 10%;
}

29.2 问题解决

坑点一: 盒子模型

  • IE 盒模型: 元素的宽度 = width + padding + border
  • W3C 盒模型: 元素的宽度 = width

坑点二: 百分比 margin/padding

元素的 margin 或者 padding 属性, 如果设置为 百分比, 则会以其父级的 width 来计算.

结果

  • IE 盒模型: div 宽度 = 20
  • W3C 盒模型: div 宽度 = 20 + 100 * 10% * 2 = 40

29.3 参考

三面


30. 将一串数字转换成中文读法

30.1 问题描述

  • 1234 => 一千二百三十四
  • 112341234 => 一亿一千两百三十四万一千两百三十四

30.2 问题解决

30.3 参考

31. 快速排序

31.1 问题描述

手写一个快速排序

31.2 问题解决

31.3 参考

32. 防抖和节流任选并实现

32.1 问题描述

32.2 问题解决

32.3 参考