写给自己的新年礼物:前端面试题汇总

想写这篇文章已经很久了,作为一个技工,怎么能免的了一直学习呢! 也换过两份工作,所以深知每次面试就是一次大考。写这篇文章的目的是对技术的回顾,也是对自己成长的记录。虽然自己换工作的经历不太多,但每次都会留下一大堆面试题,每次换完工作,面试题就会被我闲置到一边,直到下次换工作会再次翻出来,而且自己也没做什么整理,一整个凌乱,所以就把这里当作笔记本,来记录下,因为知识点都是需要反复看的。(对于某些知识点如果我理解的不对,大家有更好的理解角度,也希望大家指出来,大家相互讨论,学习吧!)。

WechatIMG193.jpeg

闲话休提,书归正传。面试就是用较短的时间做到双方相互了解,所以我尽量用简短语言来描述事物本质,至于对于某些重要知识点的深入理解,我会在后续逐步整理出文,请关注、点赞、收藏!

小编最近已经离职了,也在寻求新的机会,如果刚好你的公司在招聘,给我个机会,聊一下呗!😄😄😄

如果有看到标题号顺序没对上,不要奇怪,那是因为关于某些问题我认为还可以再优化,还在整理中,我这么重视格式的人,不会忘记的,哈哈哈!!!


pie title 文章分为多个模块进行介绍
"http" : 11
"html" : 4
"css" : 5
"javascript" : 23
"react" : 9
"业务层面" : 1
"flutter" : 9
"其他" : 3

一、http 相关

http 相关知识,前端必备网络相关知识。

1. 地址栏输入 URL 到页面展现都经历了哪些过程?

1. 构建请求
2. 查找强缓存
3. DNS解析
4. 建立TCP连接(3次握手)
5. 发送HTTP请求(请求行/头/体)
6. 服务器处理收到的请求,将数据返回浏览器
7. 浏览器收到HTTP响应
8. 读取内容,解析html,浏览器渲染,
9. 生成dom树、解析css样式、js交互

2. 三次握手的过程?

  1. 从最开始双方都处于CLOSED状态。

  2. 客户端主动请求建立连接,发送 SYN到服务端 , 自己变成了SYN-SENT状态。

  3. 服务端接收到请求,针对客户端的SYN的确认应答,返回SYNACK(对应客户端发来的SYN),并请求建立连接,自己变成了SYN-REVD

  4. 客户端收到服务端的请求,对服务端SYN的确认应答,并发送ACK给服务端,自己变成了ESTABLISHED状态;

  5. 服务端收到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. httpshttp有什么区别?

https 不是 http 的对立面,两者在本质上是相同的,都采用相同的“超文本传输协议”,使请求的数据能够显示在网站上。

安全性:

http协议以明文方式发送内容,不提供任何方式的数据加密。http协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。https则是具有安全性的SSL / TLS加密传输协议,可防止通过 Internet 发送的数据(用户名,银行卡密码等)被第三方拦截和读取。

连接方式:

httphttps使用的是完全不同的连接方式,http 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 httpsTCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。用的端口也不一样,前者是80,后者是443

权威认证:

https 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的;http 则不需要。

总结:HTTPS相对复杂,拥有权威认证,也更安全,也是以后网站的普遍模式。HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。


5. http中的Keep-Alive有了解吗?

Keep-Alivehttp的一个头部字段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. http1http2 的区别?

  • http2采用二进制,而http1使用的是文本格式;
  • http2是多路复用的,而且无阻塞,只需一个连接即可实现并行;http1一个连接只能发送一个请求;
  • http1header带有大量信息,而且每次都要重复发送;http2使用encoder来减少需要传输的header大小,通讯双方各自缓存 一份header 信息字典表,既避免重复传输,又提升了传输速度。

7. 什么是websocket?

Websocketh5提供的一种在单个TCP连接上进行全双工通信的协议,只需要服务器和浏览器通过HTTP协议进行握手之后,两者之间就直接可以创建持久性的连接,可以双向发送或接收信息,并且允许服务器主动向客户端推送数据,适合数据需要及时刷新的场景。

websocket特性:

  1. 性能高,http 是基于文本,websocket 是二进制
  2. 双向通信
  3. 在建立连接时还需要http 来通信,
  4. 天生支持跨域
  5. 天然支持加密,安全
  6. 如果断开会自动重连
websocket 封装库 socket.io

我们知道原生的websocket写起来是非常费脑细胞的,所以推荐一个库,socket.io,使用起来较为方便,并且它支持ie5,而且是跨域自动解析数据,非常非常推荐!!!!


8. websockethttp有什么区别?

相同点:

  1. 都是一样基于TCP的,都是可靠性传输协议。
  2. 都是应用层协议。

区别:

  1. WebSocket是双向通信协议,可以双向发送或接收信息;而HTTP是单向的,只能由客户端发起请求到服务端去请求数据,服务端只能作为被动方;
  2. http 是短连接,请求之后都会关闭连接,下次请求需要重新打开连接;websocket 是长连接,只需要通过一次请求来初始化连接,后面就可以进行双向数据通信

WebSocket连接的过程:

  1. 客户端发起http请求,经过3次握手后,建立起TCP连接;
  2. http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
  3. 服务器收到客户端的握手请求后,同样采用http协议回馈数据;
  4. 客户端收到连接成功的消息后,开始借助于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请求的时候,同时发送给服务器,而localStoagesessionStorage不会,最小,最大4KB可设置过期时间,默认当浏览器关闭进程的时候自动销毁。
  • indexdb: iE10以上,比较适合键值对较多的数据,如果数据结构比较复杂,同时对浏览器兼容性没什么要求,存储量最大50M


二、HTML 相关

1. 浅谈前端工程化介绍:模块化、组件化、规范化、自动化

前端工程化:指使用软件工程的技术与方法对前端开发的技术、工具、流程、经验、方案等指标标准化,它具备模块化组件化规范化自动化四大特性,主要目的是降低成本增加效率

截屏2022-12-16 20.36.38.png

  • 模块化:是指在文件层面上对代码与资源实现拆分与组装,将一个大文件拆分为互相依赖的小文件,再统一拼装与加载。各个模块功能独立,分模块来维护,组合方式更灵活,多人协作也互不干扰。例如:接口模块、资源模块、路由模块等。

  • 组件化:是指在功能开发场景中,将具备通用功能的交互设计划分为模板、样式和逻辑组成的功能单元,是具体某个功能的封装,实现了代码更高层次的复用性,提升开发效率。组件的封装也是对象的封装,同样要做到高内聚低耦合,例如分页器、table表格、form表单等。

  • 规范化:将一系列预设规范接入工程各个阶段,通过各项指标标准化开发者的工作流程,为每个开发者指明一个方向,引领着成员往该方向走。例如:eslint、stylelint、pre-commit等,拉齐代码标准,形成规范底线,方便不同人员等交叉维护。

  • 自动化:指将一系列繁琐重复的工作流程交由程序根据预设脚本自动处理,常见自动化场景包括但不限于自动化构建自动化测试自动化打包自动化发布自动化部署等。在保证效率的同时,又解放了双手。

总结:前端工程化不是某个具体的工具,而是对项目的整体架构与整体规划,使开发者能在未来可判断时间内动态规划发展走向,以提升整个项目对用户的服务周期。最终的目的是从手动处理流程全部替换为自动处理流程,以解放团队双手,让其他成员更专注于自身业务需求

参考掘金小册:《从 0 到 1 落地前端工程化》

关于git代码提交规范

关于代码提交规范,我认为是有必要去统一的,在日常开发中经常遇到一些千奇百怪的提交说明,例如中英文混合使用、各种不规范的英文单词等。这让Review代码的人会经常搞不清它们到底是干嘛的,导致后续代码维护成本巨大。

我们不能只注重编码而不考虑代码质量与提交质量。Angular团队制定的提交规范是目前市面上公认为最合理、最系统、最流行的提交规范

Angular提交规范的格式包括HeaderBodyFooter三个内容。Header为必填项,BodyFooter为可缺省项,这些内容通过以下结构组成一个完整的提交格式。

<type>(<scope>): <subject>
# 空一行
<body>
# 空一行
<footer>
Header

该部分仅书写一行,包括三个字段,分别是typescopesubject

  • 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 落地前端工程化》


2. html5 的新特性

语义化标签: <header> <footer> <nav> <article> <aside>

新增的表单属性: placehoder 、 required 、 pattern、 min/max 、 autofocus、 mulitiple

音视频标签: <audio>、 <video>

绘图标签:<canvas>

存储: 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
  • 行内块显示模式,设置displayinline-block
  • 设置overflowhiddenautoscroll
  • 弹性布局,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
  • 支持过渡与动画: transitionanimation
  • 引入媒体查询:mediaqueries ,支持为不同分辨率的设备设定不同的样式
  • 增加阴影:文本阴影:text-shadow,盒子阴影:box-shadow
  • 弹性盒模型布局:新的布局方式,用于创建具有多行和多列的灵活界面布局。


4. 怎么用css实现 字形布局?

满屏品字形

html:

  <div class='div1'>1</div>
  <div class='div2'>2</div>
  <div class='div3'>3</div>

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;
}

截屏2022-12-13 14.18.40.png

固定宽高品字形:

html:

  <div class='div1'>1</div>
  <div class='div4'>
    <div class='div2'>2</div>
    <div class='div3'>3</div>
  </div>

css:

.div1{
  width:200px;
  height:200px;
  background:red;
  margin:0 auto;
}

.div4{
  margin: auto;
  border:1px solid red;
  display: flex;
  justify-content: center;
}

.div2{
  width:200px;
  height: 200px;
  background:green;
}

.div3{
  width:200px;
  height: 200px;
  background:blue;
}

截屏2022-12-13 14.16.28.png


5. flex怎么实现一部分固定高度,一部分自适应?

下面就列举几个常用场景

左侧固定,右侧自适应:

html:

  <div class='box'>
    <div class='div1'>1</div>
    <div class='div2'>2</div>
  </div>

css:

.box{
  display: flex;
  width: 100%;
  height: 500px;
  flex-direction: row;
}
.div1{
    width: 200px;
    height:100%;
    background:red;
}

.div2{
    height:100%;
    flex:1;
    background:green;
}

截屏2022-12-13 14.53.18.png

左右固定,中间自适应:

html:

  <div class='box'>
    <div class='div1'>1</div>
    <div class='div2'>2</div>
    <div class='div3'>3</div>
  </div>

css:

.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;
}

截屏2022-12-13 15.37.37.png

顶部固定,底部自适应:

html:

  <div class='box'>
    <div class='div1'>1</div>
    <div class='div2'>2</div>
  </div>

css:

*{
  margin:0;
  padding:0;
}

.box{
  display: flex;
  min-height: 100vh;
  width: 100%;
  flex-direction: column;
}

.div1{
    width: 100%;
    flex:0 0 100px;
    background:red;
}

.div2{
    width: 100%;
    height: 100px;
    flex: auto;
    background:green;
}

截屏2022-12-13 18.15.50.png

顶部和底部固定高度,中间自适应

html:

  <div class='box'>
    <div class='div1'>1</div>
    <div class='div2'>2</div>
    <div class='div3'>3</div>
  </div>

css:

*{
  margin:0;
  padding:0;
}

.box{
  display: flex;
  min-height: 100vh;
  width: 100%;
  flex-direction: column;
}

.div1{
    width: 100%;
    flex:0 0 100px;
    background:red;
}

.div2{
    width: 100%;
    height: 100px;
    flex: auto;
    background:green;
}

.div3{
   width: 100%;
   flex:0 0 200px;
   background:blue;
}

截屏2022-12-13 18.12.53.png


四、JS 相关

1. 节流和防抖实现及其区别

  • 防抖,是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内再次触发,则会重新计算函数执行时间,即重新计时,每次触发事件时都取消之前的延时调用方法,这样一来,只有最后一次操作能被触发。
    某段时间内只执行一次
    例如:输入搜索,在400毫秒只能执行一次,如果又输入的搜索内容,则重新开始计算400

  • 节流,就是指连续触发事件但是在 n秒中只执行一次函数。节流会稀释函数的执行频率。每次触发事件时都判断当前是否有等待执行的延时函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。
    间隔时间执行,不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数
    例如:点击事件连续点击了n次,但在400毫秒内只能执行一次


2. dom 事件流是什么,如何阻止事件捕获

见链接


3. jses系列的新语法有哪些?

方便开发,提高工程性,开发效率高,更不容易犯错误

关于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(): 两数相乘

返回两个参数的类C32 位整数乘法运算的结果

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

其他新特性见链接


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():

这种方法对于所有基本的数据类型都能进行判断,即使是 nullundefined

  • instanceof

instanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。使用instanceof判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false

  • Array.isArray():

es6新增的方法,可以检测出 iframes,运行效率比较高,所以当真正需要检测一个变量是不是数组时,先会检测浏览器是否支持Array.isArray(), 之后再用Obejct.prototype.toString.call()方法。


5. typescript 中的typeinterface 的区别 ?

一般情况下定义接口用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

方法用于将自身所有可枚举属性的值从一个或多个源对象复制到目标对象,并且会返回一个新的目标对象。参考链接

  • 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'

image.png

当创建一个以另一个空对象为原型,第二个参数是添加到新创建的对象的可枚举属性:

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
  • 社区的手写方案:

//函数拷贝
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);    
}

image.png

改造后的实现:

for (var i = 1; i < 4; i++) {
  (function f(a) {
    setTimeout(function () {
      console.log(a);
    }, 1000);
  })(i);
}

image.png


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 

/* 全局作用域结束 */

除了全局作用域,我们是不能在一个作用域中访问其他作用域中的内容的。 那么如果我们需要获取其他作用域中的变量,那怎么办?闭包就这样诞生来了

参考文章


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. 什么情况下会导致内存泄露

程序的运行需要占用内存,当这些程序没有用到时,还不释放内存,就会引起内存泄漏。也就是说不再用到的内存,没有及时释放,就被称为内存泄漏。而内存泄漏,会让系统占用极高的内存,让系统变卡甚至崩溃。

  • 意外的全局变量
  • 被遗忘的定时器和回调函数
  • 当不需要setInterval或者setTimeout时,定时器没有被clear,定时器的回调函数以及内部依赖的变量都不能被回收,造成内存泄漏。
  • 注意程序逻辑,避免无限循环的引用(死循环)
  • 没有清理dom元素的引用
  • console保存大量数据在内存中

14. 如何避免内存泄露

  • 减少不必要的全局变量,使用严格模式避免意外创建全局变量。
  • 在你使用完数据后,及时解除引用(闭包中的变量,dom引用,定时器清除)。
  • 梳理清楚逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。
  • 避免过度使用闭包。

15. 数组中各遍历方法的返回值

  • filter 返回一个判断结果为true组成的数组;
  • forEach 没有返回值;
  • map 返回每次函数调用的结果组成的新数组;
  • reduce 迭代数组所有项,然后构建一个最终返回的值;
  • some 如果该函数任意一项返回true,则返回true
  • every 如果该函数对每一项都返回true,则返回true

16. mapfilter 使用区别 ?

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中)

18. Event Loop 执行顺序如下所示:

  1. 首先执行同步代码,这属于宏任务
  2. 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行,例如promise.then里的回调函数
  3. 执行所有微任务
  4. 当执行完所有微任务后,如有必要会渲染页面
  5. 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数

19. setTimeout、Promise、Async/Await 的区别

事件循环中分为宏任务队列和微任务队列。

promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;
async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。
其中settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行;(最后执行)


24. js 异步解决方案的发展历程?

异步操作:多个操作可以一起进行,而互不干扰,同步操作:一次只能处理一个请求

  1. 回调函数:当嵌套层级过多,容易产生回调地狱,强耦合,一旦有所改动,牵一发而动全身,错误管理困难。
  2. Promise 就是为了解决callback的问题而产生的,Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,如果我们在 thenreturnreturn 的结果会被 Promise.resolve() 包装。让Promise 大放异彩的是Promise.all。缺点:无法取消promise,如果有逻辑,不能解决,如果有错误需要通过回调函数来捕获。
  3. Generator 可以控制函数的执行,可以有顺序,可以中途停止,停止后再执行,写起来相对麻烦。
  4. asyncawait 是异步的终极解决方案,当然也是语法糖,内部实现仍然是回调嵌套回调。优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题,用同步的书写方式写异步。缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。

21. 什么是Promise? 都有哪几个状态?

Promise 是异步编程的一种解决方案,是一种语法糖,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,解决回调地狱难题,一旦新建它就会立即执行,无法中途取消Promise

Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。由于它的then方法和catchfinally方法会返回一个新的Promise所以可以允许我们链式调用,解决了传统的回调地狱问题。

Promise有三种状态:

  • pending,进行中。
  • resolved (也可以叫fulfilled),已成功。
  • rejected,已失败。

Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected,thencatch 都会返回一个新的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的状态都变成fulfilledp的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • 只要p1、p2、p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。


22. 事件委托/事件代理?

事件委托是利用事件的冒泡原理来实现的

何为事件冒泡呢?

就是事件从最深的节点开始,然后逐步向上传播事件
只指定一个事件处理程序,就可以管理某一类型的所有事件。注册事件的话应该注册在父节点上。假如我们要给100li同时添加一样的事件,那么我们就需要和浏览器交互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


六、React 相关

1. Vuereact 的区别

见链接


3. 如何理解受控组件和非受控组件?

React 中受控和非受控的概念通常跟 form 表单组件相关,比如 input ,通过区分与 input 进行数据交互的方式,组件被分成两个不同的派系,受控与非受控。

  • 受控组件:
    React 中受控组件的是指表单元素的控制是交给 React ,表单元素的值是完全交由组件的 state 控制。

  • 非受控组件
    非受控组件指表单元素的状态并不受 React 组件状态的影响,表单元素的值存储于 DOM 元素中。如果要 React 组件要获取 DOM 元素的值,需要通过绑定 ref 的方式去获取。


4. React 解决了前端开发中的哪些痛点?

  • 组件化开发,一切皆组件,提升代码重用率;
  • jsx的书写方式,更符合趋势,提高开发效率;
  • 虚拟DOM解决了以往需要大量操作DOM带来的性能问题 。

5. react的自定义hooks怎么实现?

自定义hooks


6. useMemo的使用方式和原理是?

详见链接

原理:


7. ReactFiber 机制怎么理解

详见链接


8. React-hooks 原理

详见链接


9. UseStatethis.setState 对比有什么区别?

这两个是react 两种组件书写方式中的状态管理

useState 是使用在函数组件中的,setState 是用在类组件中

更新之前也有区别,useState有优化策略,如果你上次的value和下一次的一样,会触发eagerState策略,不会走到更新。
this.setState则不会处理,会走到scheduleUpdateOnFiber。这也是他们在触发时最大的不同。


七、其他业务层面

1. 创建一个组件库,除基本组件外,应该关注的点有哪些?组件发布时,关注的点有?

  • 文档:保持和代码同步的更新频率,同步最新更新的记录到文档中,设定一个主要负责人,定期审查;
  • 更新规则:非bug fix的问题,提前收集整理多方需求,确认上线时间要求,合理安排排期,最多一迭代一更新;
  • 测试:在测试资源充足的情况下,安排测试人员对组件进行测试;如无测试资源,保证组件功能说明文档详尽,待业务提测后一同进行测试。


八、flutter 相关

3. dart.. 的用法

级联符号 .. 可以让你连续操作相同的对象,不单可以连续地调用函数,还可以连续地访问方法,这样做可以避免创建临时变量,从而写出更流畅的代码,流式编程更符合现代编程习惯和编程风格

7. await for

大概意思就是await for是不断获取stream流中的数据,然后执行循环体中的操作。

Stream<String> stream = new Stream<String>.fromIterable(['不开心', '面试', '没', '过']);
main() async{
  print('上午被开水烫了脚');
  await for(String s in stream){
    print(s);
  }
  print('晚上还没吃饭');
}复制代码

输出为

上午被开水烫了脚
不开心
面试
没
过
晚上还没吃饭

wait forlisten的作用很相似,都是获取流中数据然后输出,但是正如await for中的await所示,如果stream没有传递完成,就会一直阻塞在这个位置,上面没吃饭是最后输出的,下面给个listen的实例,一看就懂。

Stream<String> stream = new Stream<String>.fromIterable(['不开心', '面试', '没', '过']);
main(){
  print('上午被开水烫了脚');
  stream.listen((s) { print(s); });
  print('晚上还没吃饭');
}

8. constfinal的区别

相同点

1、必须初始化
2、只能赋值一次,且不可被修改

不同点

1、final可修饰实例变量、const不可以修饰实例变量
2、访问类中const修饰的变量需要static修饰
3、const修饰的List集合任意索引不可修改,final修饰的可以修改
4、const 用来修饰变量 只能被赋值一次,在编译时赋值
final 用来修饰变量 只能被赋值一次,在运行时赋值
5、final 只可用来修饰变量, const 关键字即可修饰变量也可用来修饰 常量构造函数
const修饰类的构造函数时,它要求该类的所有成员都必须是final的。

10. Flutter 如何与 Android iOS 通信?

Flutter 通过 PlatformChannel 与原生进行交互,其中 PlatformChannel 分为三种:

  1. BasicMessageChannel:用于传递字符串和半结构化的信息。
  2. MethodChannel:用于传递方法调用。Flutter主动调用Native的方法,并获取相应的返回值。
  3. EventChannel:用于数据流(event streams)的通信。

11. Dart中  ?? 与  ??= 的区别

A??B
左边如果为空返回右边的值,否则不处理。
A??=B
左边如果为空把B的值赋值给A

12. main()  和runApp()  函数在flutter的作用分别是什么?有什么关系吗?

main函数是类似于java语言的程序运行入口函数

runApp函数是渲染根widget树的函数

一般情况下runApp函数会在main函数里执行

13. 什么是widgetflutter里有几种类型的widget,分别有什么区别?

widget在flutter里基本是一些UI组件

有两种类型的widget,分别是statefulWidget 和 statelessWidget两种

statelessWidget不会自己重新构建自己,但是statefulWidget会

9. StatefulWidget 的生命周期

  • initState():Widget 初始化当前 State,在当前方法中是不能获取到 Context 的,如想获取,可以试试 Future.delayed()
  • didChangeDependencies():在 initState() 后调用,State对象依赖关系发生变化的时候也会调用。
  • deactivate():当 State 被暂时从视图树中移除时会调用这个方法,页面切换时也会调用该方法,和Android里的 onPause 差不多。
  • dispose():Widget 销毁时调用。
  • didUpdateWidget:Widget 状态发生变化的时候调用。

statelessWidget生命周期:

  1. 构造函数
  2. build方法

参考文章


九、其他

平常的学习途径

掘金社区/ 掘金小册 / csdn / sf

b站 学习资料

专项技术的官网

犀牛书(抄书)


自我介绍


有什么兴趣爱好

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容