想写这篇文章已经很久了,作为一个技工,怎么能免的了一直学习呢! 也换过两份工作,所以深知每次面试就是一次大考。写这篇文章的目的是对技术的回顾,也是对自己成长的记录
。(对于某些知识点如果我理解的不对,大家有更好的理解角度,也希望大家指出来,大家相互讨论,学习吧! )。
闲话休提,书归正传。面试就是用较短的时间做到双方相互了解
,所以我尽量用简短语言来描述事物本质,至于对于某些重要知识点的深入理解,我会在后续逐步整理出文,请关注、点赞、收藏!
小编最近已经离职了,也在寻求新的机会,如果刚好你的公司在招聘,给我个机会,聊一下呗!😄😄😄
如果有看到标题号顺序没对上,不要奇怪,那是因为关于某些问题我认为还可以再优化,还在整理中,我这么重视格式的人,不会忘记的,哈哈哈!!!这份特殊的【新年礼物】
,你可要收好啊!
mermaid
pie title 文章分为多个模块进行介绍
"http" : 11
"html" : 4
"css" : 5
"javascript" : 23
"react" : 9
"业务层面" : 1
"flutter" : 9
"其他" : 3
面试路上互助交流群
如果你也在找工作 或者你准备换工作 ,那么可以一起交流一下呀,也不介意潜水哈 😄,我只是希望面试的路上不是你我一个人独立奋斗 ,在大城市生活久了人都变的孤独了许多,我不希望面试完想找人分享找不到 。在群里我们可以共享资源、一同避一下那些踩雷的公司、📣 吐槽生活、互相激励等等,下面是群二维码,欢迎加群交流呦!💪 加油 准备冲刺2023!
一、http
相关
http
相关知识,前端必备网络相关知识。
1. 地址栏输入 URL
到页面展现都经历了哪些过程?
1. 构建请求
2. 查找强缓存
3. DNS解析
4. 建立TCP连接(3次握手)
5. 发送HTTP请求(请求行/头/体)
6. 服务器处理收到的请求,将数据返回浏览器
7. 浏览器收到HTTP响应
8. 读取内容,解析html,浏览器渲染,
9. 生成dom树、解析css样式、js交互
2. 三次
握手的过程?
从最开始双方都处于CLOSED
状态。
客户端主动请求建立连接,发送 SYN
到服务端 , 自己变成了SYN-SENT
状态。
服务端接收到请求,针对客户端的SYN
的确认应答,返回SYN
和ACK
(对应客户端发来的SYN
),并请求建立连接,自己变成了SYN-REVD
。
客户端收到服务端的请求,对服务端SYN
的确认应答,并发送ACK
给服务端,自己变成了ESTABLISHED
状态;
服务端收到ACK
之后,也变成了ESTABLISHED
状态。
另外需要提醒你注意的是,SYN
是需要消耗一个序列号的,下次发送对应的 ACK
序列号要加1
,因为
凡是需要对端确认的,一定消耗TCP
报文的序列号。 。
为什么是三次
不是四次
?
三次握手的目的是确认双方发送
和接收
的能力,那四次握手可以嘛?
当然可以,100
次都可以。但为了解决问题,三次就足够了,再多用处就不大了。
三次
握手过程中可以携带数据么?
首先说答案:第三次握手的时候,可以携带。前两次握手不能携带数据。
如果前两次握手能够携带数据,那么一旦有人想攻击服务器,那么他只需要在第一次握手中的 SYN
报文中放大量数据,那么服务器势必会消耗更多的时间和内存空间去处理这些数据,增大了服务器被攻击的风险。第三次握手的时候,客户端已经处于ESTABLISHED
状态,并且已经能够确认服务器的接收、发送能力正常,这个时候相对安全了,可以携带数据。
3. 网络分层是 5
层还是 7
层?
OSI 七层参考模型:
物理层 -> 数据链路层 -> 网络层(Ip)-> 传输层(TCP)- > 会话层-> 表现层- > 应用层(http)顶层
我们所知道的还有 TCP/IP
四层模型和 TCP/IP
五层模型。这又是怎么出来的,其实所谓的 TCP/IP
四层模型和 TCP/IP
五层模型是以 OSI
七层优化而来,把某些层进行合并了(会话层,表现层,应用层合并成应用层),其实本质上还是相同的,。
互联网 5层协议模型:
物理层 -> 数据链路层 -> 网络层(Ip)-> 传输层(TCP)->应用层(http)
物理层 :用物理手段将电脑连接起来,就像我们讲到的计算机之间的物理连线。主要用来传输0、1
信号,所有我们用另一层用来规定不同0、1
组合的意义是什么。
数据链路层 :在数据链路层规定一套协议,专门的给0、1
信号进行分组,以及规定不同的组代表什么意思,从而双方计算机都能够进行识别,这个协议就是"以太网协议"
网络层 :网络层的由来是因为在数据链路层中我们说说两台计算机之间的通信是分为同一子网络和不同子网络之间,那么问题就来了,怎么判断两台计算机是否在同一子网络(局域网)中?这就是网络层要解决的问题。
传输层 :传输层的主要功能就是为了能够实现"端口到端口"的通信。计算机上运行的不同程序都会分配不同的端口,所以才能使得数据能够正确的传送给不同的应用程序。
应用层 :应用层的功能就是规定了应用程序的数据格式。我们经常用得到的电子邮件、HTTP
协议、以及FTP
数据的格式,就是在应用层定义的。
4. https
和http
有什么区别?
https
不是 http
的对立面,两者在本质上是相同的,都采用相同的“超文本传输协议”,使请求的数据能够显示在网站上。
安全性:
http
协议以明文方式发送内容,不提供任何方式的数据加密。http
协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。https
则是具有安全性的SSL / TLS
加密传输协议,可防止通过 Internet
发送的数据(用户名,银行卡密码等)被第三方拦截和读取。
连接方式:
http
和https
使用的是完全不同的连接方式,http
连接建立相对简单, TCP
三次握手之后便可进行 HTTP
的报文传输。而 https
在 TCP
三次握手之后,还需进行 SSL/TLS
的握手过程,才可进入加密报文传输。用的端口也不一样,前者是80
,后者是443
。
权威认证:
https
协议需要向 CA
(证书权威机构)申请数字证书,来保证服务器的身份是可信的;http
则不需要。
总结: HTTPS
相对复杂,拥有权威认证,也更安全,也是以后网站的普遍模式。HTTPS
协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。
5. http
中的Keep-Alive
有了解吗?
Keep-Alive
是http
的一个头部字段Connection
中的一个值,它是保证我们的http
请求能建立一个持久连接。也就是说建立一次TCP
连接即可进行多次请求和响应的交互。它的特点就是只要有一方没有明确提出断开连接,则保持TCP
连接状态 ,减少TCP
连接和断开造成的额外开销。
HTTP/1.0
阶段:
在HTTP/1.0
中所有的连接默认都是关闭
的,如果客户端浏览器支持Keep-Alive
,那么就在HTTP
请求头中添加一个字段Connection: Keep-Alive
,当服务器收到附带有Connection: Keep-Alive
的请求时,它也会在响应头中添加一个同样的字段来使用Keep-Alive
。这样一来,客户端和服务器之间的HTTP
连接就会被保持,不会断开(超过Keep-Alive
规定的时间,意外断电等情况除外),当客户端发送另外一个请求时,就使用这条已经建立的连接。
HTTP/1.1
阶段
在HTTP/1.1
中所有的连接默认都是持久连接
的。除非在请求头或响应头中指明要关闭:Connection: Close
,这也就是为什么Connection: Keep-Alive
字段再没有意义的原因。目前大部分浏览器都是用http1.1
协议,也就是说默认都会发起Keep-Alive
的连接请求了。
6. http1
和http2
的区别?
http2
采用二进制,而http1
使用的是文本格式;
http2
是多路复用的,而且无阻塞,只需一个连接即可实现并行;http1
一个连接只能发送一个请求;
http1
的header
带有大量信息,而且每次都要重复发送;http2
使用encoder
来减少需要传输的header
大小,通讯双方各自缓存 一份header
信息字典表,既避免重复传输,又提升了传输速度。
7. 什么是websocket
?
Websocket
是h5
提供的一种在单个TCP
连接上进行全双工通信的协议,只需要服务器和浏览器通过HTTP
协议进行握手之后,两者之间就直接可以创建持久性的连接,可以双向发送或接收信息,并且允许服务器主动向客户端推送数据 ,适合数据需要及时刷新的场景。
websocket
特性:
性能高,http
是基于文本,websocket
是二进制
双向通信
在建立连接时还需要http
来通信,
天生支持跨域
天然支持加密,安全
如果断开会自动重连
websocket
封装库 socket.io
我们知道原生的websocket
写起来是非常费脑细胞的,所以推荐一个库,socket.io
,使用起来较为方便,并且它支持ie5
,而且是跨域自动解析数据,非常非常推荐!!!!
8. websocket
与http
有什么区别?
相同点:
都是一样基于TCP
的,都是可靠性传输协议。
都是应用层协议。
区别:
WebSocket
是双向通信协议,可以双向发送或接收信息;而HTTP
是单向的,只能由客户端发起请求到服务端去请求数据,服务端只能作为被动方;
http
是短连接,请求之后都会关闭连接,下次请求需要重新打开连接;websocket
是长连接,只需要通过一次请求来初始化连接,后面就可以进行双向数据通信
WebSocket
连接的过程:
客户端发起http
请求,经过3次
握手后,建立起TCP
连接;
http
请求里存放WebSocket
支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version
等;
服务器收到客户端的握手请求后,同样采用http
协议回馈数据;
客户端收到连接成功的消息后,开始借助于TCP
传输信道进行全双工通信。
9. 什么是http
缓存?
http
缓存指的是: 当客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有要请求资源的副本
,就可以直接从浏览器缓存中提取而不是从原始服务器中提取这个资源。
常见的http
缓存只能缓存get
请求响应的资源,对于其他类型的响应则无能为力,所以后续说的请求缓存都是指get
请求
分类:
根据是否需要再请求:可分为强制缓存和协商缓存 ,强制缓存如果生效,不需要再和服务器发生交互,而协商缓存不管是否生效,都需要与服务端发生交互;
根据是否可以被单个或者多个用户使用来分类,可分为私有缓存和共享缓存
配置:在请求的headers
中添加
cache-control: max-age=30 pragma:no-cache
如不想被缓存,则配置:
cache-control: no-store
10. 为什么要使用 http
缓存?
减少了冗余的数据传输,节省了网费。
缓解了服务器的压力, 大大提高了网站的性能
加快了客户端加载网页的速度
11. 都有哪些本地存储的方案?
前端缓存一般使用较多的有:localStorage、 sessionStorage、 cookie、indexdb
区别:
- localStorage : HTML5
新特性,只要在相同的协议、相同的主机名、相同的端口下,就能读取/修改同一份localStorage
数据,是永久存储,除非手动删除,大小5M
左右,IE8
以上,不能被爬虫抓取到
- sessionStorage : HTML5
新特性,比localStorage
更严苛一点,除了协议、主机名、端口外,还要求在同一窗口,也就是浏览器的标签,仅仅是会话级别的存储 ,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。
- cookie : 保存用户登录状态的数据会在每一次发送http
请求的时候,同时发送给服务器 ,而localStoage
和sessionStorage
不会,最小,最大4KB
,可设置过期时间 ,默认当浏览器关闭进程的时候自动销毁。
- indexdb : iE10
以上,比较适合键值对较多的数据,如果数据结构比较复杂,同时对浏览器兼容性没什么要求,存储量最大50M
。
二、HTML
相关
1. 浅谈前端工程化介绍:模块化、组件化、规范化、自动化
前端工程化:指使用软件工程的技术与方法对前端开发的技术、工具、流程、经验、方案等指标标准化
,它具备模块化 、组件化 、规范化 、自动化 四大特性,主要目的是降低成本
与增加效率
。
模块化:是指在文件层面上对代码与资源实现拆分与组装,将一个大文件拆分为互相依赖的小文件,再统一拼装与加载。各个模块功能独立,分模块来维护,组合方式更灵活,多人协作也互不干扰。例如:接口模块、资源模块、路由模块等。
组件化:是指在功能开发场景中,将具备通用功能的交互设计划分为模板、样式和逻辑组成的功能单元,是具体某个功能的封装,实现了代码更高层次的复用性,提升开发效率。组件的封装也是对象的封装,同样要做到高内聚低耦合 ,例如分页器、table
表格、form
表单等。
规范化:将一系列预设规范接入工程各个阶段,通过各项指标标准化开发者的工作流程,为每个开发者指明一个方向,引领着成员往该方向走。例如:eslint、stylelint、pre-commit
等,拉齐代码标准,形成规范底线,方便不同人员等交叉维护。
自动化:指将一系列繁琐重复的工作流程交由程序根据预设脚本自动处理,常见自动化
场景包括但不限于自动化构建
、自动化测试
、自动化打包
、自动化发布
和自动化部署
等。在保证效率的同时,又解放了双手。
总结:前端工程化
不是某个具体的工具,而是对项目的整体架构与整体规划 ,使开发者能在未来可判断时间内动态规划发展走向,以提升整个项目对用户的服务周期。最终的目的是从手动处理流程全部替换为自动处理流程 ,以解放团队双手,让其他成员更专注于自身业务需求 。
参考掘金小册:《从 0 到 1 落地前端工程化》
关于git
代码提交规范
关于代码提交规范,我认为是有必要去统一的,在日常开发中经常遇到一些千奇百怪的提交说明,例如中英文混合使用、各种不规范的英文单词等。这让Review
代码的人会经常搞不清它们到底是干嘛的,导致后续代码维护成本巨大。
我们不能只注重编码而不考虑代码质量与提交质量。Angular团队 制定的提交规范
是目前市面上公认为最合理、最系统、最流行的提交规范
。
Angular提交规范
的格式包括Header
、Body
和Footer
三个内容。Header
为必填项,Body
与Footer
为可缺省项,这些内容通过以下结构组成一个完整的提交格式。
```
():
空一行
# 空一行
```
##### Header
该部分仅书写一行,包括三个字段,分别是`type`、`scope`和`subject`。
- **type**:用于说明`commit`的提交类型,`必选`
- **scope**:用于说明`commit`的影响范围,可选
- **subject**:用于说明`commit`的细节描述,可选
`type`用于说明`commit`的提交类型,包括以下选项,相信这些选项已满足日常`95%`的应用场景。当然这些选项无需刻意记忆,我会引入命令自动完成这些提交工作。
类型 | 功能 | 描述 |
| :----------: | -- | ----------------- |
| **feat** | 功能 | 新增功能,迭代项目需求 |
| **fix** | 修复 | 修复缺陷,修复上一版本存在问题 |
| **docs** | 文档 | 更新文档,仅修改文档不修改代码 |
| **style** | 样式 | 变动格式,不影响代码逻辑 |
| **refactor** | 重构 | 重构代码,非新增功能也非修复缺陷 |
| **perf** | 性能 | 优化性能,提高代码执行性能 |
| **test** | 测试 | 新增测试,追加测试用例验证代码 |
| **build** | 构建 | 更新构建,改动构建工具或外部依赖 |
| **ci** | 脚本 | 更新脚本,改动CI或执行脚本配置 |
| **chore** | 事务 | 变动事务,改动其他不影响代码的事务 |
| **revert** | 回滚 | 回滚版本,撤销某次代码提交 |
| **merge** | 合并 | 合并分支,合并分支代码到其他分支 |
| **sync** | 同步 | 同步分支,同步分支代码到其他分支 |
| **impr** | 改进 | 改进功能,升级当前功能模块
`scope`用于说明`commit`的影响范围。简要说明本次改动的影响范围,例如根据功能可划分为`数据层`、`视图层`和`控制层`,根据交互可划分为`组件`、`布局`、`流程`、`视图`和`页面`。从`Angular`以往的提交说明来看,还是建议你在提交时对`scope`补全。
`subject`用于说明`commit`的细节描述。文字一定要精简精炼,无需太多备注,因为`Body`部分可备注更多细节,同时尽量遵循以下规则。
- 以动词开头
- 使用第一人称现在时
- 首个字母不能大写
- 结尾不能存在句号(`.`)
##### Body
该部分可书写多行,对`subject`做更详尽的描述,内容应包括`改动动机`与`改动前后对比`。
##### Footer
该部分只适用两种情况,分别是`不兼容变动`与`问题关闭`。
- **不兼容变动**:当前代码与上一版本不兼容,则以`BREAKING CHANGE`开头,关联`变动描述`、`变动理由`和`迁移方法`
- **问题关闭**:当前代码已修复某些`Issue`,则以`Closes`开头,关联目标`Issue`
示例一个常规提交:
```
// 类型:修复问题,影响范围:qa 环境,处理细节:全局切换主题开关无效 关联bug: #87697
fix(qa):invalid global theme color switch (#87679)
```
[参考掘金小册:《从 0 到 1 落地前端工程化》 ](https://juejin.cn/book/7034689774719860739?enter_from=course_center)
---
#### 2. `html5` 的新特性
语义化标签: ` `
新增的表单属性: ` placehoder 、 required 、 pattern、 min/max 、 autofocus、 mulitiple`
音视频标签: `、 `
绘图标签:``
存储: `sessionStorage 、 localStorage`
通信: `webSocket`
---
**语义化标签有什么作用?**
- 比较利于开发人员阅读,结构清晰明了;
- 利于`SEO`搜索引擎优化,搜索引擎也要分析我们的网页,可以很方便的寻找出网页的重点部分,排名靠前;
- 有利于特殊终端的阅读(盲人阅读器)。
---
#### 3. 关于页面白屏如何排查原因 ?
- 检查控制台是否有报错;
- 检查网络是否正常,看`network`资源加载情况,传输数据量是否过大;
- 在其他电脑打开是否一样白屏;
- 看`dom`结构,是否正常加载,是否是静态资源加载失败;
- 更换浏览器,检查是否是浏览器兼容问题。
---
#### 4. 前端优化常用的方法有哪些 ?
- 路由、组件、图片等懒加载,雪碧图;
- 减少`http`请求 :节流、防抖、缓存(`keep-alive`);
- `webpack` :打包压缩、`Loader` 、插件;
- 使用常量,避免全局变量;
- 减少重绘和回流,减少`table` 表格布局,减少`dom` 操作,`html` 层级嵌套不要太深;
- 及时消除对象引用,清除定时器,清除事件监听器;
- 使用`ssr` 预渲染。
---
### 三、`CSS` 相关
#### 1. `css` 盒模型:
`W3C`盒模型,又名标准盒模型,元素的宽高大小表现为内容的大小:
```
box-sizing:content-box
```
`IE` 盒模型,又名怪异盒模型,元素的宽高表现为内容 + 内边距 + 边框:
```
box-sizing:border-box
```
---
#### 2. 什么是`BFC`?
> `BFC`全称 `Block formatting context`(块级格式化上下文)。是web 页面中盒模型布局的css 渲染模式,是一个独立的渲染区域或一个隔离的独立容器。
**特性:**
- 块级元素,内部一个一个垂直排列
- 垂直方向的距离由两个元素中`margin` 的较大值决定
- `bfc` 区域不会与浮动的容器发生重叠
- 计算元素的高度时,浮动元素也会参与计算
- `bfc` 容器中,子元素不会影响外边元素
- 属于同一个`bfc`的两个相邻元素的外边距发生重叠,
**怎样使一个元素变成`BFC`区域**
> 可以通过设置css 属性来实现
- 设置浮动`float`但不包括`none`
- 设置定位,`absoulte`或者`fixed`
- 行内块显示模式,设置`display`为`inline-block`
- 设置`overflow`为`hidden`、`auto`、`scroll`
- 弹性布局,`flex`
**`BFC`解决了哪些问题:**
- 阻止元素被浮动元素覆盖
- 可以利用`BFC`解决两个相邻元素的上下`margin`重叠问题;
- 可以利用`BFC`解决高度塌陷问题;
- 可以利用`BFC`实现多栏布局(两栏、三栏、圣杯、双飞翼等)。
---
#### 3. `CSS3` 新特性总结
- 选择器: `E:last-child 、E:nth-child(n)、E:nth-last-child(n)`
- 边框特性:支持圆角、多层边框、彩色和图片边框
- 背景图增加多个属性:`background-image、background-repeat、background-size、background-position、background-origin和background-clip`
- 支持多种颜色模式和不透明度:加了`HSL、HSLA、RGBA` 和不透明度 `opacity`
- 支持过渡与动画: `transition` 、`animation`
- 引入媒体查询:`mediaqueries` ,支持为不同分辨率的设备设定不同的样式
- 增加阴影:文本阴影:`text-shadow`,盒子阴影:`box-shadow`
- 弹性盒模型布局:新的布局方式,用于创建具有多行和多列的灵活界面布局。
---
#### 4. 怎么用`css`实现 `品` 字形布局?
**满屏品字形**
html:
```
1
2
3
```
css:
```
.div1{
width:100%;
height:200px;
margin:auto;
background:red;
}
.div2{
width:50%;
height:200px;
float:left;
background:green;
}
.div3{
width:50%;
height:200px;
float:left;
background:blue;
}
```

**固定宽高品字形:**
html:
```
1
```
css:
```
.div1{
width:200px;
height:200px;
background:red;
margin:0 auto;
}
.div4{
border:1px solid red;
display: flex;
justify-content: center;
}
.div2{
width:200px;
height: 200px;
background:green;
}
.div3{
width:200px;
height: 200px;
background:blue;
}
```

---
#### 5. `flex`怎么实现一部分固定高度,一部分自适应?
> 下面就列举几个常用场景
**左侧固定,右侧自适应:**
html:
```
```
css:
```
*{
margin:0;
padding:0;
}
.box{
display: flex;
width: 100%;
height: 500px;
}
.div1{
width: 200px;
height:100%;
background:red;
}
.div2{
height:100%;
flex:1;
background:green;
}
```

**左右固定,中间自适应:**
html:
```
```
css:
```
*{
margin:0;
padding:0;
}
.box{
display: flex;
height: 200px;
width: 100%;
}
.div1{
width: 200px;
height: 100%;
background:red;
}
.div2{
height:100%;
flex:1;
background:green;
}
.div3{
width: 200px;
height: 100%;
background:blue;
}
```

**顶部固定,底部自适应:**
html:
```
```
css:
```
*{
margin:0;
padding:0;
}
.box{
display: flex;
min-height: 100vh;
width: 100%;
flex-direction: column;
}
.div1{
flex:0 0 100px;
background:red;
}
.div2{
height: 100px;
flex: auto;
background:green;
}
```

**顶部和底部固定高度,中间自适应**
html:
```
```
css:
```
*{
margin:0;
padding:0;
}
.box{
display: flex;
min-height: 100vh;
width: 100%;
flex-direction: column;
}
.div1{
flex:0 0 100px;
background:red;
}
.div2{
height: 100px;
flex: auto;
background:green;
}
.div3{
flex:0 0 200px;
background:blue;
}
```

#### 6. 居中的布局
> 在`css`面试题中,关于 `水平 + 垂直居中` 布局这个问题,可能是我在前端面试中被问到最多的,哈哈 那我们就来实现一下吧!
**html**
```
```
**第一种:(`flex`最方便,有兼容性问题:ie 8 以下不支持)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
display: flex;
justify-content: center;
align-items: center;
}
.center{
width:200px;
height: 200px;
background-color: blue;
}
```
**第二种:(四个方向拉扯)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
position: relative;
}
.center{
width:200px;
height: 200px;
background-color: blue;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
```
**第三种:(使用计算属性`calc`)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
position: relative;
}
.center{
width:200px;
height: 200px;
background-color: blue;
position: absolute;
left: calc(50% - 100px);
top: calc(50% - 100px);
}
```
**第四种:(使用转换属性`transform`)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
position: relative;
}
.center{
width:200px;
height: 200px;
background-color: blue;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
```
**第五种:(定位 + `margin-left:盒子的一半`)**
```
.box{
width:500px;
height: 500px;
background-color: yellow;
position: relative;
}
.center{
width:200px;
height: 200px;
background-color: blue;
position: absolute;
left: 50%;
top: 50%;
margin-left: -100px;
margin-top: -100px;
}
```
统一效果如下:
---
### 四、`JS` 相关
#### 1. 节流和防抖实现及其区别
- 防抖,是指触发事件后在 `n` 秒内函数只能执行一次,如果在 `n` 秒内再次触发,则会重新计算函数执行时间,即重新计时,每次触发事件时都取消之前的延时调用方法,这样一来,只有最后一次操作能被触发。
某段时间内只执行一次
例如:输入搜索,在`400`毫秒只能执行一次,如果又输入的搜索内容,则重新开始计算`400`秒
- 节流,就是指连续触发事件但是在 `n`秒中只执行一次函数。节流会稀释函数的执行频率。每次触发事件时都判断当前是否有等待执行的延时函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。
间隔时间执行,不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数
例如:点击事件连续点击了`n`次,但在`400`毫秒内只能执行一次
---
#### 2. `dom` 事件流是什么,如何阻止事件捕获
[见链接](https://juejin.cn/post/6844903518902583310)
---
#### 3. `js`的`es`系列的新语法有哪些?
> 包括但不限于`es6、7、8、9`....,它最大的特性就是:方便开发,提高工程性,开发效率高,更不容易犯错误
关于`es6`以上新特性兼容性: 大部分的新特性`ie`浏览器不支持,如需支持,可使用`babel`在线编译
配置:
```
script type='text/babel'
```
---
- **Number.isSafeInteger(): 是否在安全数内**
> 检测数字是否在安全数内,返回布尔值
```
Number.isSafeInteger(234334343434343); // true
Number.isSafeInteger(999999999999999999); // false
```
- **Number.isNaN(): 检查是否为 NaN**
> 结果返回布尔值。与`isNaN`的区别是,`Number.isNaN`会先检测参数是否为`Number` 类型,如果不是直接返回`false`,只有当参数为`Number`类型,才会再去判断是不是`NaN`。
```
Number.isNaN(NaN) // true
Number.isNaN(1) // false
Number.isNaN(true) // false
Number.isNaN(undefined) // false
Number.isNaN({}) // false
Number.isNaN("abc") // false
Number.isNaN("") // false
```
- **Math.sign(): 返回一个数字的符号,指示数字是正数、负数还是零**
> 一共有 5 种返回值,分别是 **1, -1, 0, -0, NaN.** 代表的各是**正数,负数,正零,负零,NaN**。
```
Math.sign(3); // 1
Math.sign(-3); // -1
Math.sign("-3"); // -1
Math.sign(0); // 0
Math.sign(-0); // -0
Math.sign(NaN); // NaN
Math.sign("foo"); // NaN
Math.sign(); // NaN
```
- **Math.imul(): 两数相乘**
> 返回两个参数的类`C` 的`32` 位整数乘法运算的结果
```
Math.imul(2, 4); // 8
Math.imul(-1, 8); // -8
Math.imul(-2, -2); // 4
Math.imul(0xffffffff, 5); // -5
```
- **允许顶层 await**
> 之前我们在写`await` 的时候,一定要在前面加一个`async`,现在我们支持在顶层直接使用。`Top await` 本身就是`esModule` 里的规范,所以需要在`script` 里加上 `type=“module”`
- **at(-1)可以直接拿到数组或者字符串的最后一位元素**
>支持数组和字符串,再也不用使用这种冗余的写法了 `arr[arr.length-1]`
```
[2,3,5].at(-1); // 5
'aazvxcvher'.at(-1); // r
```
- **Object.hasOwn**
> 代替 Object.prototype.hasOwnProperty.call
```
// 原来
Object.prototype.hasOwnProperty.call(obj, 'name');
// 现在
Object.hasOwn(obj, 'name');
```
- **允许 JavaScript 的数值使用下划线(`_`)作为分隔符**
> 可以间隔3位,也可以间隔两位,看起来比较好读
```
let budget = 1_000_000_000_000;
budget === 10 ** 12 // true
```
[其他新特性见链接](https://juejin.cn/post/7026696335575220238#heading-9)
---
#### 4. 关于 `ES6` 的模块化
**发展历史:**
没有模块 -> CMD(按需加载,加载数量并没有减少) -> AMD(异步加载模块) -> ES6 语言提供的模块化支持
**`export`:导出的几种方式**
```
// 导出普通变量(边定义边导出)
export let a=5;
// 导出常量
export const a=6;
// 导出函数
export function xx(){};
// 导出类
export class xxx(){} ;
// 导出默认成员
export default 'xxx';
// 从另一个模块导出
export * from './xx';
// 导出模块中的部分内容
export {xxx,xx,xx} from './xxx';
// 导出模块中的default
export {default} from './xx';
```
**`import`:引入的几种方式**
```
// 全部引入并重命名
import * as mod from '/xxx';
// 引入指定成员
import {a,b,c} from './xxx';
// 引入默认成员,配合 export default xxx;
import xxx from './xxx';
// 将模块的代码引入,不引入内部成员
import './x.jpg';
import './x.css';
// 异步引入
let promise = import ('./xx');
```
---
#### 5. `Object.prototype.toString.call()` 、 `instanceof` 以及 `Array.isArray()`判断数组的方法,区别?
> 如果只是用来判断数组, `Array.isArray` 优于`instanceof`。
- **Object.prototype.toString.call():**
这种方法对于所有基本的数据类型都能进行判断,即使是 `null` 和 `undefined` 。
- **instanceof**
`instanceof` 的内部机制是通过判断对象的原型链中是不是能找到类型的 `prototype`。使用`instanceof`判断一个对象是否为数组,`instanceof` 会判断这个对象的原型链上是否会找到对应的 `Array` 的原型,找到返回 `true`,否则返回 `false`。
- **Array.isArray():**
是`es6`新增的方法,可以检测出 `iframes`,运行效率比较高,所以当真正需要检测一个变量是不是数组时,先会检测浏览器是否支持`Array.isArray()`, 之后再用`Obejct.prototype.toString.call()`方法。
---
#### 5. `typescript` 中的`type` 和 `interface` 的区别 ?
> 一般情况下定义接口用`interface`,定义常量用`type`
**关于区别:**
- 都可以来描述对象和函数,实现继承的方式不同,`interface` 是通过 `extends`, `type`是通过`&`;
- 可以定义相同名称的`interface`,且会合并声明成员,`type`不可以声明同名的,会报错;
- `type` 可以定义基本类型 / 联合类型 / 元祖类型。
例:
```
type PartialPointX = { x: number };
type PartialPointY = { y: number };
// union(联合)
type PartialPoint = PartialPointX | PartialPointY;
// tuple(元祖)
type Data = [PartialPointX, PartialPointY];
// primitive(原始值)
type Name = Number;
```
---
#### 6. `Object.assign()` 、 `Object.create()` 、 `Object.defineProperty` 分别的用处 ?
- **`Object.assign`**
> 方法用于将自身所有可枚举属性的值从一个或多个源对象复制到目标对象,并且会返回一个新的目标对象。[参考链接](https://juejin.cn/post/6844903586451685389#heading-1)
- **`Object.create`**
>创建一个新对象,是把现有对象的属性,挂到新建对象的原型上,第一个参数添加到原型上,第二个参数,为添加的可枚举属性(即添加自身属性,不是原型上的)
例:
**复制对象:**
```
var a = {name:'a'};
a.__proto__.lastName="b";
// 想要创建一个一模一样的 a
b = Object.create(a.__proto__,Object.getOwnPropertyDescriptors(a))
console.log(b); // { name:'a' }
```
**`Object.create()`方法创建的对象时,属性是在原型下面的**
```
var a={ name:'hh'};
var b = Object.create(a);
console.log(b); // {}
console.log(b.name); // 'hh'
```

> 当创建一个以另一个空对象为原型,第二个参数是添加到新创建的对象的可枚举属性:
```
var a = Object.create({}, { p: { value: 19 } })
console.log(a); // {p: 19}
```
- **`Object.defineProperty`**
> 直接在一个对象上定义一个新属性,或者修改一个对象现有的属性,并返回这个对象。
```
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false
});
object1.property1 = 77; // throws an error in strict mode 不允许被修改
console.log(object1.property1); // 42
```
**区别:**
- `Object.defineProperty` 用于给对象添加新属性
- `Object.create` 用于将自身所有可枚举属性的值从一个或多个源对象复制到目标对象,并且会返回一个新的目标对象
- `Object.create()`创建一个新对象,是把现有对象的属性,挂到新建对象的原型上
---
#### 7. 什么是`Object`属性的可枚举性 ?
- 可枚举属性是指那些内部 `可枚举`(`enumerable`)标志设置为 `true` 的属性
- 对于通过直接的赋值和属性初始化的属性,该标识值默认为 `true`
- 对于通过`Object.defineProperty`等定义的属性,该标识值默认为 `false`
- 可枚举的属性可以通过`for...in`循环进行遍历。
---
#### 8. 浅拷贝和深拷贝 ?
- **浅拷贝** 如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址(新旧对象共享同一块内存),所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
- **深拷贝** 将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象(新旧对象不共享同一块内存),且修改新对象不会影响原对象。
---
#### 9. 深拷贝的方法 ?
>深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象(新旧对象不共享同一块内存),且修改新对象不会影响原对象
- **JSON.parse(JSON.stringify())**
```
let obj1 = {
name: 'Li',
age:18
}
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1 === obj2); // false
```
**关于`JSON.parse(JSON.stringify())`的缺点**
1. `obj`里面有`new Date()`,深拷贝后,时间会变成字符串的形式。而不是时间对象;
2. `obj`里有`RegExp`、`Error`对象,则序列化的结果会变成空对象`{}`;
3. `obj`里有`function`,`undefined`,则序列化的结果会把`function`或 `undefined`丢失;
4. `obj`里有`NaN`、`Infinity`和`-Infinity`,则序列化的结果会变成`null`;
5. `JSON.stringify()`只能序列化对象的可枚举的自有属性,如果obj中的对象是由构造函数生成的实例对象, 深拷贝后,会丢弃对象的`constructor`;
[参考文章](https://blog.csdn.net/weixin_40016215/article/details/117700135)
---
- **社区的手写方案:**
```
//函数拷贝
const copyObj = (obj = {}) => {
//变量先置空
let newobj = null;
//判断是否需要继续进行递归
if (typeof (obj) == 'object' && obj !== null) {
newobj = obj instanceof Array ? [] : {};
//进行下一层递归克隆
for (var i in obj) {
newobj[i] = copyObj(obj[i])
}
//如果不是对象直接赋值
} else newobj = obj;
return newobj;
}
```
- **lodash中的cloneDeep()**
```
import lodash from 'lodash';
let obj = {
a: {
c: 2,
d: [1, 9, 9, 2],
e:'张三'
},
b: 99
}
const newObj = lodash.cloneDeep(obj);
obj.b = 5;
console.log(newObj.b); // 99 不会被影响
```
---
#### 10. `var` 如何实现 `let` ?
**`var` 存在的问题**
- 可以重复声明
- 没有块级作用域
- 不能限制修改
---
**普通的`var`使用:**
```
for(var i=1;i<4;i++){
setTimeout(function(){
console.log(i);
},1000);
}
```

**改造后的实现:**
```
for (var i = 1; i < 4; i++) {
(function f(a) {
setTimeout(function () {
console.log(a);
}, 1000);
})(i);
}
```

---
#### 11. 什么是作用域 ?
> 作用域指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限
在`js`中大部分情况下,分为以下三种作用域:
**全局作用域**: 全局作用域为程序的最外层作用域,一直存在。
**函数作用域**: 函数作用域只有函数被定义时才会创建,包含在父级作用域内。
**块级作用域** : 所有语法块`{}`都会形成独立的块级作用域,例如:`if 、for`。
>由于作用域的限制,每段独立的执行代码块只能访问自己作用域和外层作用域中的变量,无法访问到内层作用域的变量。`ES6` 标准提出了使用 `let` 和 `const` 代替 `var` 关键字,来“创建块级作用域”。
```
/* 全局作用域开始 */
var a = 1;
function func () {
/* func 函数作用域开始 */
var a = 2;
console.log(a);
}
/* func 函数作用域结束 */
func(); // => 2
console.log(a); // => 1
/* 全局作用域结束 */
```
**除了全局作用域,我们是不能在一个作用域中访问其他作用域中的内容的。** 那么如果我们需要获取其他作用域中的变量,那怎么办?**闭包就这样诞生来了**!
[参考文章](https://juejin.cn/post/6844904165672484871)
---
#### 12. 什么是闭包?什么场景使用?有什么缺点?
> 官方解释:闭包是指有权访问另一个函数作用域中的变量的函数。例如:函数`a`可以拿到函数`b`内部作用域的变量。
**`闭包除了和作用域有关,也和垃圾回收的机制有关,正常的垃圾回收过程是:当一个函数执行时会给它分配空间,而当执行结束后会把空间收回。`**
##### **垃圾回收机制?**
>垃圾回收有两种方法:标记清除、引用计数。引用计数不太常用,标记清除较为常用。
**标记清除**是`javascript`中最常用的垃圾回收方式。当变量进入执行环境时,就标记这个变量为`进入环境`。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为`离开环境`。
```
var m = 0,n = 19 // 把 m,n 标记为进入环境。
var a= m + n // 把 a 标记为进入环境。
console.log(n) // n 标记为离开环境,等待垃圾回收。
```
---
> 但是当一个局部变量被另一个函数正在使用,那么它就不会被回收,就是形成了闭包环境。
例如:
```
function foo() {
var a = 2;
document.onclick=()=>{
alert(a); // a 不会被回收
}
}
show();
```
>我们了解作用域之间是可以嵌套的,我们把这种嵌套关系称为 **作用域链**。闭包的执行看起来像是开发者使用的一个小小的 “作弊手段” ——**绕过了作用域的监管机制,从外部也能获取到内部作用域的信息**。闭包的这一特性极大地丰富了开发人员的编码方式,也提供了很多有效的运用场景。
##### **用途:**
1. 能缓存作用域变量,私有化数据,避免全局变量的污染;
2. 利于代码封装(防抖、节流,选项卡),调用方法后内部值不会互相影响
##### **缺点:**
一直存在内存中,有可能导致内存泄露(**不是一定**),所以在不需要用到的时候及时把变量设置为`null`。
---
#### 13. 什么情况下会导致内存泄露 ?
**内存一般有三个阶段的生命周期: 分配期 -> 使用期 -> 释放期**
>程序的运行需要占用内存,当这些程序没有用到时,还不释放内存,就会引起内存泄漏。而内存泄漏,会让系统占用极高的内存,让系统变卡甚至崩溃。
- 意外的全局变量
- 被遗忘的定时器和回调函数(例如:未使用的闭包)
- 被遗忘的事件监听器
- 无限循环的引用函数(死循环)
- `console`保存大量数据在内存中
---
#### 14. 如何避免内存泄露 ?
> 其实总结一句话就是:尽早释放无用对象的引用,下面是具体方法:
- 减少不必要的全局变量,使用严格模式避免意外创建全局变量(现在编辑器都会提示)。
- 在你使用完数据后,及时解除引用(闭包中的变量,`dom`引用,定时器清除)。
- 梳理清楚逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。
- 避免过度使用闭包(尽量减少逻辑嵌套)。
---
#### 15. 数组中各遍历方法的返回值
- `filter` 返回一个判断结果为`true`组成的数组;
- `forEach` 没有返回值;
- `map` 返回每次函数调用的结果组成的新数组;
- `reduce` 迭代数组所有项,然后构建一个最终返回的值;
- `some` 如果该函数任意一项返回`true`,则返回`true`;
- `every` 如果该函数对每一项都返回`true`,则返回`true`;
---
#### 16. `map` 和 `filter` 使用区别 ?
**`map`:**
作用是生成一个新数组,遍历原数组,将每个元素拿出来做一些变换然后放入到新的数组中。
```
[1, 2, 3].map(v => v + 1) // -> [2, 3, 4]
```
**`filter`**
作用也是生成一个新数组,在遍历数组的时候将返回值为 `true` 的元素放入新数组,我们可以利用这个函数删除一些不需要的元素
```
let array = [1, 2, 4, 6];
let newArray = array.filter(item => item !== 6) ;
console.log(newArray) // [1, 2, 4]
```
---
#### 17. `Event loop`事件的循环机制
>在事件任务中分为微任务和宏任务两种
- 宏任务:包括整体代码`script`,定时器:`setTimeout,setInterval`
- 微任务:`Promise.then`(非`new Promise`),`process.nextTick`(`node`中)
---
**`Event Loop` 执行顺序如下所示:**
1. 首先执行同步代码,这属于宏任务
2. 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行,例如`promise.then`里的回调函数
3. 执行所有微任务
4. 当执行完所有微任务后,如有必要会渲染页面
5. 然后开始下一轮 `Event Loop`,执行宏任务中的异步代码,也就是 `setTimeout` 中的回调函数
---
#### 18. `setTimeout、Promise、Async/Await` 的区别
> 事件循环中分为宏任务队列和微任务队列。
- `promise.then`里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行
- `async`函数表示函数里面可能会有异步方法,`await`后面跟一个表达式,`async`方法执行时,遇到`await`会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。
- `settimeout`的回调函数放到宏任务队列里,等到执行栈清空以后执行;(最后执行)
---
#### 24. `js` 异步解决方案的发展历程?
> **异步操作**:多个操作可以一起进行,而互不干扰,**同步操作**:一次只能处理一个请求
1. 回调函数:当嵌套层级过多,容易产生回调地狱,强耦合,一旦有所改动,牵一发而动全身,错误管理困难。
2. `Promise` 就是为了解决`callback`的问题而产生的,`Promise` 实现了链式调用,也就是说每次 `then `后返回的都是一个全新 `Promise`,如果我们在 `then` 中 `return` ,`return` 的结果会被 `Promise.resolve()` 包装。让`Promise` 大放异彩的是`Promise.all`。缺点:无法取消`promise`,如果有逻辑,不能解决,如果有错误需要通过回调函数来捕获。
3. `Generator` 可以控制函数的执行,可以有顺序,可以中途停止,停止后再执行,写起来相对麻烦。
4. `async`、`await` 是异步的终极解决方案,当然也是语法糖,内部实现仍然是回调嵌套回调。优点是:代码清晰,不用像 `Promise` 写一大堆 `then` 链,处理了回调地狱的问题,用同步的书写方式写异步。缺点:`await` 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 `await` 会导致性能上的降低。
---
#### 21. 什么是`Promise`? 都有哪几个状态?
>`Promise` 是异步编程的一种解决方案,是一种语法糖,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,解决回调地狱难题,一旦新建它就会立即执行,无法中途取消`Promise`。
>`Promise` 是一个对象,它代表了一个异步操作的最终完成或者失败。由于它的`then`方法和`catch`、`finally`方法会返回一个新的`Promise`所以可以允许我们链式调用,解决了传统的回调地狱问题。
**`Promise`有三种状态:**
- `pending`,进行中。
- `resolved` (也可以叫`fulfilled`),已成功。
- `rejected`,已失败。
**`Promise`对象的状态改变,只有两种可能:从`pending`变为`fulfilled`和从`pending`变为`rejected`,`then` 和 `catch` 都会返回一个新的`promise`**
**`then()`**
方法可以接收两个回调函数作为参数。第一个回调函数是`Promise`对象的状态变为`resolved`时调用,处理操作成功后的业务,第二个回调函数是`Promise`对象的状态变为`rejected`时调用,处理操作异常后的业务。
**`catch()`**
只接受一个参数,用于处理操作异常后的业务, 一般来说,不要在`then`方法里面定义 `Reject` 状态的回调函数(即`then`的第二个参数),总是使用`catch`方法。这样可以处理 `Promise` 内部发生的错误。`catch`方法返回的还是一个 `Promise` 对象,因此后面还可以接着调用`then`方法。
**`finally()`**
方法用于指定不管 `Promise` 对象最后状态如何,都会执行的操作, 方法的回调函数不接受任何参数, 这表明,`finally`方法里面的操作,应该是与状态无关的,不依赖于 `Promise` 的执行结果,最终也是返回一个`promise` 函数
**`all()`**
方法用于将多个 `Promise` 实例,包装成一个新的 `Promise` 实例。`Promise.all`方法接收一个`数组`作为参数,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调,一旦抛出异常,也只有第一个抛出的错误会被捕获,但不影响其他异步任务。
举例:
`p1、p2、p3`都是` Promise` 实例
```
const p = Promise.all([p1, p2, p3]);
```
**`p`的状态由`p1、p2、p3`决定,分成两种情况。**
- 只有`p1、p2、p3`的状态都变成`fulfilled`,`p`的状态才会变成`fulfilled`,此时`p1、p2、p3`的返回值组成一个数组,传递给`p`的回调函数。
- 只要`p1、p2、p3`之中有一个被`rejected`,`p`的状态就变成`rejected`,此时第一个被`reject`的实例的返回值,会传递给`p`的回调函数。
**`race()`**
方法返回一个 `promise`,一旦迭代器中的某个 `promise` 解决或拒绝,就会返回结果。`race`含有竞速的意思,将多个`Promise`放在一个数组中,数组中有一个`promise`最先得到结果,不管是" 完成(`resolved`)"还是" 失败(`resolved`)" , 那么这个 `race` 方法就会返回这个结果。
**`race()`跟`all()`的区别:**
`race()`根据第一个请求来返回结果,若第一个成功,全局都成功,第一个失败,全局都失败,返回结果的顺序,时间快的先返回。`all()` 是全部成功,才会成功,只有一个失败,就是失败,成功返回的结果根据请求的参数的顺序,无关请求的快慢。
---
#### 22. 事件委托/事件代理?
>事件委托是利用事件的`冒泡原理`来实现的
**何为事件冒泡呢?**
就是事件从最深的节点开始,然后逐步`向上传播`事件
只指定一个事件处理程序,就可以管理某一类型的所有事件。注册事件的话应该注册在父节点上。假如我们要给`100`个`li`同时添加一样的事件,那么我们就需要和浏览器交互`100`次,如果要用事件委托,就会将所有的操作放到js程序里面,只对它的父级(如果只有一个父级)这一个对象进行操作,与`dom`的操作就只需要交互一次,这样就能大大的减少与`dom`的交互次数,效率高,不用`for`循环去添加事件,提高性能。
---
#### 23. 什么是跨域?
> 跨域的产生来源于现代浏览器所通用的同源策略限制,当一个请求`url`的协议、域名、端口三者均一样的情况下,才允许访问相同的`cookie、localStrage`,以及访问页面`dom`或者发送`ajax`请求。原因是为了保证用户信息安全,防止恶意网站窃取数据。
同源:
```
http://www.example.com:8080/index.html
http://www.example.com:8080/home.html
```
跨域:
```
http://www.example.com:8080/index.html
http://www3.example.com:8080/index.html
```
但是还有两种情况,http默认的端口为80,https默认的端口号为443,所以以下不是跨域:
```
http://www.example.com:80 === http://www.example.com
https://www.example.com:443 === https://www.example.com
```
---
**如何解决跨域 ?**
>其实跨域请求产生时,请求是发出去了,也是有响应的,只是因为浏览器同源策略,它认为此时是不安全的,拦截了我们的返回结果,不将数据传递我们使用罢了。
---
- **CORS 全称是"跨域资源共享"(Cross-origin resource sharing)**
> `CORS`支持所有类型的`HTTP`请求,是跨域http请求的根本解决方案。
后端来处理,修改响应头,允许任何网站访问:
```
'Access-Control-Allow-Origin': '*',
```
- **jsonp**
>jsonp是利用`
```
实现步骤:
1. 声明一个回调函数,用于接收数据;
2. 创建一个`script` 标签,把`script`的`scr` 赋值为跨域的`api` 接口地址;
3. 将回调函数的名字当参数挂在scr上,告诉对方我的回调函数的名称,用于执行完成后来调用我的函数
配合`jquery`来实现:
```
...
$.ajax({
url:"http://sp0.baidu.com.23d8s8dfj923kjs7823jshdsdf/su",
data:{wd:'qq'},
dataType:"jsonp",
jsonp:"cb", // 告诉它回调函数的名字,以便于它生成一个函数来给我们用
}).then(({s})=>{
console.log(s);
},res=>{
console.log('失败')
);
...
```
---
- **websocket**
>跨域问题的产生无非就是去服务器上获取数据;而`websocket`是和服务器建立了双工连接,连接只要建立了,就能通讯了,既然能通讯,那我想要什么数据跟后台说,后台再发送给前端数据,这样利用通信就不会有跨域的问题啦。这种方式是通过建立长链接来请求,相比普通请求会比较耗费性能!
`Websocket` 支持跨域,因`websocket`原生`api`用起来不太方便,推荐使用`socket.io`的库,使用过`node.js`配合前端。
---
- **webpack**
> 通过代理的方式,一般用于开发环境
```
module.exports = {
devServer: {
proxy: {
"/api": {
target: "http://localhost:3000",
pathRewrite: {"/api": ""} // 将/api替换掉
}
}
}
}
```
访问 `http://localhost:8080/api/test `就会被代理到 `http://localhost:3000/test` 上,而如果`JSESSIONID`设置了`httpOnly`不能重写`cookie`到`target`,故无效
[参考文章](https://juejin.cn/post/6844903767226351623)
---
#### 24. 关于重绘和重排 ?
- 重绘:某些元素的外观被改变,例如:元素的填充颜色
- 重排:局部或者整体布局发生改变,需要重新生成布局,重新排列元素。
**重绘不一定导致重排,但重排一定会导致重绘**。重排的开销要远远大于重绘,会破坏用户体验,并且让UI展示非常迟。所以我们要尽量减少页面重排次数。
**如何减少重排:**
- 脱离文档流,使用`position:absolute 、fixed`
- 避免频繁操作样式
- 动画样式启用GPU加速
- 减少重排范围,尽可能将样式加在具体元素上,而不是它的父级
[参考文章](https://juejin.cn/post/6844904083212468238)
---
#### 25. 进程和线程的区别 ?
> 一个程序至少有一个进程,第一个叫主进程,其余都是子进程,一个进程可以包含多个线程。他们是包含的关系。
```mermaid
stateDiagram-v2
程序 --> 进程1
程序 --> 进程2
进程1 --> 线程1
进程1 --> 线程2
进程2 --> 线程3
进程2 --> 线程4
```
1. 线程的性能高,但安全性低,同一个进程之内的线程之间共享内存,共享计数器。
2. 进的性能低,但安全性高,每个进程有自己独立的内存,独立的计数器,进程和进程之间一般是不共享数据的;
3. 进程要分配一大部分的内存,而线程只需要分配一部分栈就可以了。
4. 进程是资源分配的最小单位,线程是程序执行的最小单位。
5. 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行
`nodejs`和`javascript`是多进程语言
创建进程:
```
js: new Worker()
node:cluster.for()
```
[参考文章](https://juejin.cn/post/6844903801321685000)
---
### 六、`React` 相关
#### 1. `vue` 和 `react` 的区别
[见链接](https://juejin.cn/post/7026696335575220238#heading-40)
---
#### 2. 如何理解受控组件和非受控组件?
>`React` 中受控和非受控的概念通常跟 `form` 表单组件相关,比如 `input` ,通过区分与 `input` 进行数据交互的方式,组件被分成两个不同的派系,受控与非受控。
- 受控组件:
`React` 中受控组件的是指表单元素的控制是交给 `React` ,表单元素的值是完全交由组件的 `state` 控制。
- 非受控组件
非受控组件指表单元素的状态并不受 `React` 组件状态的影响,表单元素的值存储于 `DOM` 元素中。如果要 `React` 组件要获取 `DOM` 元素的值,需要通过绑定 `ref` 的方式去获取。
---
#### 3. `React` 解决了前端开发中的哪些痛点?
- 组件化开发,一切皆组件,提升代码重用率;
- `jsx`的书写方式,更符合趋势,提高开发效率;
- 虚拟`DOM`解决了以往需要大量操作`DOM`带来的性能问题 。
---
#### 4. `react`的自定义`hooks`怎么实现?
[自定义hooks](https://juejin.cn/post/6844904038668976142#heading-35)
---
#### 5. `useMemo`的使用方式?
[详见链接](https://juejin.cn/post/6844904038668976142#heading-34)
---
#### 6. `React`中`Fiber` 机制怎么理解
[详见链接](https://juejin.cn/post/7026696335575220238#heading-59)
---
#### 7. `React-hooks` 原理
[详见链接](https://juejin.cn/post/7026696335575220238#heading-78)
---
#### 8. `UseState` 和 `this.setState` 对比有什么区别?
>这两个是`react` 两种组件书写方式中的状态管理。`useState` 是使用在函数组件中的,`setState` 是用在类组件中
在更新之前有区别,`useState`有优化策略,如果你上次的`value`和下一次的一样,会触发`eagerState`策略,不会走到更新。`this.setState`则不会处理,会走到`scheduleUpdateOnFiber`。这也是他们在触发时最大的不同。
---
### 七、其他业务层面
#### 1. 创建一个组件库,除基本组件外,应该关注的点有哪些?组件发布时,关注的点有?
- 文档:保持和代码同步的更新频率,同步最新更新的记录到文档中,设定一个主要负责人,定期审查;
- 更新规则:非`bug fix`的问题,提前收集整理多方需求,确认上线时间要求,合理安排排期,最多一迭代一更新;
- 测试:在测试资源充足的情况下,安排测试人员对组件进行测试;如无测试资源,保证组件功能说明文档详尽,待业务提测后一同进行测试。
#### 2. 按钮级别的权限怎么做 ?
**首先将权限分层来管理**
例如:菜单权限、模块权限、按钮权限。再将其进行分层判断,如果没有菜单权限,即不会再进行模块和按钮权限判断。一定程度上避免一些bug 产生。
**关于具体的按钮权限:**
可以通过配置来实现,将按钮做成字典表,然后实现一个鉴权的函数,通过判断当前角色是否有权限来显示按钮,当角色没有权限时,不显示操作权限。
---
### 算法
#### 1. 实现找到最大字符并输出其个数
```
const array1 =[1,3,5,6,8,8,8];
function foo(arr) {
const maximum = Math.max(...arr);
const maximumArr = arr.filter(function(item,index){
return item === maximum;
});
return {maximum, "length":maximumArr.length}
}
```
打印结果:

---
#### 2. 数组去重
去重的方法不止一种,下面推荐几个比较常用的方法:
使用`es6`新增的方法:[new Set](https://juejin.cn/post/6844903555682271245#heading-19)
使用filter 方法: [filter](https://juejin.cn/post/6844903555682271245#heading-20)
使用 includes : [includes](https://juejin.cn/post/6844903555682271245#heading-21)
---
#### 3. 冒泡排序
第一种:
```
let arr = [10, 9, 7, 5, 7, 8, 9, 3, 2, 0];
let len = arr.length;
for (let j = 0; j < len - 1; j++){
for (let i = 0; i < len - 1 - j; i++){
if (arr[i]>arr[i+1]) {
[arr[i + 1], arr[i]] = [arr[i], arr[i + 1]];
}
}
}
// [0, 2, 3, 5, 7, 7, 8, 9, 9, 10]
```
第二种:
```
let arr = [10, 9, 7, 5, 7, 8, 9, 3, 2, 0];
let len = arr.length;
for (var i = 0; i < len; i++){
for (var j = i + 1; j < len; j++){
if (arr[i] > arr[j]) {
[arr[i],arr[j]]=[arr[j],arr[i]]
}
}
}
// [0, 2, 3, 5, 7, 7, 8, 9, 9, 10]
```
#### 4. 展开多维数组
**最简洁:**
```
let arr = [1, 2, 4, 6,[5, 4, 5,[98, 3], [34], [7]]];
arr.toString().split(',').map(Number);
```
步骤解析:

---
**使用递归来处理:**
```
let arr = [1, 2, 4, 6,[5, 4, 5,[98, 3], [34], [7]]];
function f(arr=[],newArr=[]) {
arr.forEach((item) => {
if (!Array.isArray(item)) {
newArr.push(item);
} else {
f(item, newArr);
}
});
return newArr;
}
f(arr);
```
打印结果:

---
### 八、`flutter` 相关
> 关于`flutter`相关面试题,网上资源是比较少的,而且偏向极端化,就是上来就问各种实现原理,哈哈哈,参考的资料比较少,所以下面这些题一些是源于社区,另一部分是我自己的猜测。另一方面就是面试`flutter `的有一半以上是做客户端的,有安卓或者`ios` 开发经验,所以我也只能从前端角度去选取一些题作为参考,后面如果面试中有问题,我会更新到文档中。
[Dart语法知识点看这里](https://juejin.cn/post/7098649277827645448)
#### 1. 什么是`flutter` ?
>`Flutter` 是 `Google` 推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。开发者可以通过 `Dart` 语言开发 `App`,一套代码同时运行在 `iOS` 和 `Android`平台。在`2022年5月12日`中,`Flutter 3.0`正式发布。当前最新的`Flutter3.0`提供了对`macOS`和`Linux`桌面应用程序支持,改进`Firebase`集成、新的生产力和性能提升、添加`Apple Silicon`支持。截至目前,`Flutter`终于实现了对`iOS、Android、Web、Windows、macOS、Linux`六大平台的稳定支持,完成了其对跨平台的愿景。
[详细介绍](https://flutter.cn/docs)
---
#### 2. `dart` 中 `..` 的用法
[参考链接](https://juejin.cn/post/7098649277827645448#heading-38)
---
#### 3. `Dart`中 `??` 与 `??=` 的区别
> A??B
> 左边如果为空返回右边的值,否则不处理。
> A??=B
> 左边如果为空把B的值赋值给A
[参考链接](https://juejin.cn/post/7098649277827645448#heading-17)
---
#### 4. `const`和`final`的区别
**相同点**
1、必须初始化
2、只能赋值一次,且不可被修改
**不同点**
1、`final`可修饰实例变量、`const`不可以修饰实例变量
2、访问类中`const`修饰的变量需要`static`修饰
3、`const`修饰的`List`集合任意索引不可修改,`final`修饰的可以修改
4、`const` 用来修饰变量 只能被赋值一次,在编译时赋值,`final` 用来修饰变量 只能被赋值一次,在运行时赋值
5、`final` 只可用来修饰变量, `const` 关键字即可修饰变量也可用来修饰 常量构造函数
当`const`修饰类的构造函数时,它要求该类的所有成员都必须是`final`的。
---
#### 5. `Minxins` `extends` `implement` 之前的关系
- `extends`: 如果是复用已有类的实现,使用继承 `extends`
- `Minxins`: 类似于多继承,是在多类继承中重用一个类代码的方式 可以使用`with` 来连接继承多个类
示例:
```
void main() {
var d = D();
d.a(); // 这里D就可以继承A B C 的所有方法
}
// 假设有A B C 三个类,类D需要继承前面的三个,但是又没有多继承的方法
class A {
void a() {
print("A.a()...");
}
}
class B {
void b() {
print("B.b()...");
}
}
class C {
void c() {
print("C.c()...");
}
}
class D extends A with B, C {}
// 当继承的A B C 中都有同名的方法,最后调用的顺序和这个继承顺序相关,最后继承的谁,调用的就是谁
```
- `implement`: 如果只是使用已有类的外在行为,使用接口 `implements`
示例:
```
void main() {
var student = Student();
}
abstract class Person {
void run();
}
class Student implements Person {
@override
void run() {
print('Student...');
}
}
```
---
#### 6. `await for`
大概意思就是`await for`是不断获取`stream`流中的数据,然后执行循环体中的操作。
```
Stream stream = new Stream.fromIterable(['不开心', '面试', '没', '过']);
main() async{
print('上午被开水烫了脚');
await for(String s in stream){
print(s);
}
print('晚上还没吃饭');
}复制代码
```
输出为
```
上午被开水烫了脚
不开心
面试
没
过
晚上还没吃饭
```
`wait for` 和 `listen`的作用很相似,都是获取流中数据然后输出,但是正如`await for`中的`await`所示,如果`stream`没有传递完成,就会一直阻塞在这个位置,上面没吃饭是最后输出的,下面给个`listen`的实例,一看就懂。
```
Stream stream = new Stream.fromIterable(['不开心', '面试', '没', '过']);
main(){
print('上午被开水烫了脚');
stream.listen((s) { print(s); });
print('晚上还没吃饭');
}
```
---
#### 7. `Flutter` 如何与 `Android、iOS` 通信 ?
`Flutter` 通过 `PlatformChannel` 与原生进行交互,其中 `PlatformChannel` 分为三种:
1. BasicMessageChannel:用于传递字符串和半结构化的信息。
1. MethodChannel:用于传递方法调用。Flutter主动调用Native的方法,并获取相应的返回值。
1. EventChannel:用于数据流(event streams)的通信。
---
#### 8. `main()` 和`runApp()` 函数在`flutter`的作用分别是什么?有什么关系吗?
> main函数是类似于java语言的程序运行入口函数
>
> runApp函数是渲染根widget树的函数
>
> 一般情况下runApp函数会在main函数里执行
---
#### 9. 什么是`widget` 在`flutter`里有几种类型的`widget`,分别有什么区别?
> widget在flutter里基本是一些UI组件
>
> 有两种类型的widget,分别是statefulWidget 和 statelessWidget两种
>
> statelessWidget不会自己重新构建自己,但是statefulWidget会
---
#### 10. `StatefulWidget` 和`statelessWidget`的生命周期都有哪些?
**StatefulWidget**
- `initState()`:`Widget` 初始化当前 `State`,在当前方法中是不能获取到 `Context` 的,如想获取,可以试试 `Future.delayed()`
- `didChangeDependencies()`:在 `initState()` 后调用,`State`对象依赖关系发生变化的时候也会调用。
- `deactivate()`:当 `State` 被暂时从视图树中移除时会调用这个方法,页面切换时也会调用该方法,和Android里的 `onPause` 差不多。
- `dispose()`:`Widget` 销毁时调用。
- `didUpdateWidget`:`Widget` 状态发生变化的时候调用。
**statelessWidget**:
- 构造函数
- `build`方法
[参考文章](https://juejin.cn/post/6844903777879736327)
---
### 九、其他随机问题
> 这项记录的是一些开放性问题,没有标准答案,但也不能全靠随机应变,我也只是给大家提供一些思路和小的方法,大家仅供参考!
#### 1. 自我介绍
- 首先自报家门,然后告诉面试官自己从事本项工作时长,分别在哪几家公司,分别负责的项目和具体使用到的技术栈。
- 加分项:如果有博客或者开源项目可以介绍一下,或者最近自己学习了哪些新的技能知识
- 加分项:自己平常工作中的一些好的习惯,例如:沟通能力、管理能力、学习能力
#### 2. 你的优缺点?
> 这个问题是个开放性的问题,也是面试官让你自己**充分介绍他所不了解的你**。在平常的生活中如果有人问到这个问题,我们可能不太重视,想到哪点说哪点,也不会做整理和优化,**当然我也不是教给大家说谎话,我们要从实际出发,真实阐述自己的(与当前应聘职位相关的)优点和缺点**。下面我给大家分享一篇我认为可以很好阐述这个问题的文章:[链接地址](https://zhuanlan.zhihu.com/p/139952856)
#### 3. 以后的职业规划
>这个问题同样开放性比较大,大概是可以分为两个方向: 技术的深度or广度、管理层
两个方向,有不同的重点,在技术领域,无论是深度还是广度,都需要的是耐心、自律能力、持续学习能力等硬技能,而在管理方向,则需要精进自己的沟通能力、共情能力、协调能力等软技能,根据自己的想法应答即可。
#### 4. 你平常的学习途径 ?
> 这个问题因人而异,不同的人有不同的学习习惯,进而就会选择不同的学习途径,下面就列出我的学习途径给大家参考。
- 掘金社区 / 掘金小册 / csdn / sf
- b站 学习资料
- 某专项技术的官网
- 微信公众号
- 犀牛书(抄书)
---