css
1.HTML5新增了哪些内容
- 语义化更好的内容标签(header,footer,main,nav,aside,article,section)
- 音频 ,视频标签(audio, video)
- 画布(canvas)
- 表单控件 calendar , date , time , email , url , search , tel , file , number
- 地理位置API(geolocation)
- 拖拽释放API(Drap and drop)
- WebAPI:localStorage、sessionStorage、webworker、websocket
2.CSS3新增了哪些内容
- 颜色: 新增 RGBA , HSLA 模式
- 文字阴影(text-shadow)
- 边框: 圆角(border-radius) 边框阴影 : box-shadow
- 盒子模型: box-sizing
- 背景:background-size background-origin background-clip
- 渐变: linear-gradient , radial-gradient
- 过渡 : transition 可实现属性的渐变
- 自定义动画 animate @keyfrom
- 媒体查询 多栏布局 @media screen and (width:800px) {…}
- border-image 图片边框
- 2D 转换/3D 转换;transform: translate(x,y) rotate(x,y) skew(x,y) scale(x,y)
- 字体图标 iconfont/icomoon
- 弹性布局 flex
3.什么是HTML语义化
- HTML语义化是指使用合适的HTML标签做合适的事,既是不使用css属性也可以将网页内容以文档的形式展现出来。
- 有助于提高网站的可访问性和搜索引擎优化(SEO),便于浏览器、搜索引擎以及其他设备(如屏幕阅读器)解析和理解网站内容。
4.常见的布局方式
- 表格布局:由 <table> 标签定义。每个表格均有若干行( <tr>),每行被分割为若干单元格(<td>)。
- 浮动布局:通过float属性创建浮动元素,实现浮动布局。浮动布局会脱离文档流,容易造成父元素高度塌陷问题。
- 定位布局:通过postion属性创建定位元素,以精确控制网页对象的显示位置,布局精准,不会出现错行和误差问题;缺点是缺乏灵活性,栏目之间不能够协同变化,还会存在叠加等风险。
- 流动布局:流动布局是HTML默认的布局方式,通过百分比控制元素尺寸,随文档流自上而下按顺序动态分布。
- 弹性布局:和浮动一样都用于网页布局,但flex比浮动要更强大。flex不会产生脱标现象,布局网页更灵活、更简单
- 栅格布局:与弹性布局相似,栅格系统也是由栅格容器包裹栅格元素进行使用。对于栅格布局来说,它的思想实际上非常简单,将一块区域绘制成格子,然后将元素填进去即可
5.弹性布局和栅格布局
- Flex布局:
Flexible Box
的缩写,也称为弹性布局
。W3C于
2009年提出。是一种以轴线为标准的一种布局方式。 - Grid布局:
Grid 布局
又称为网格布局
,微软于 2010 年提出。是一种将容器划分为单元格的布局形式。 - 总结:布局的操作性和复杂度上来看,
Grid
布局要比Flex
布局强大,但是Grid会有一些浏览器兼容性、响应式设计、性能优化等问题
6.圣杯布局和双飞翼布局
- 最终效果相同,两侧宽度固定,中间宽度自适应(三栏布局)
- 圣杯布局代码结构上更加自然和直观,在平时的开发中更容易形成这样的布局结构;
- 双飞翼布局由于不使用定位,所以代码更加简洁,允许页面的最小宽度小于圣杯布局。
7.px、em、rem、vwwh、%
- px 像素(Pixel),固定长度单位,1px 就是一个像素点;
- % 相对长度单位,相对于父元素的长或宽的占比;
- em 是相对长度单位,相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸。它会继承父级元素的字体大小因此并不是一个固定的值;
- rem 是 CSS3 新增的一个相对单位(root em,根 em),使用 rem 为元素设定字体大小时,仍然是相对大小,但相对的只是 HTML 根元素;
- vwvh 相对长度单位,相对于浏览器窗口的占比,相当于把浏览器长宽分成一百份,1vw等于浏览器宽度的1%;
- calc()方法可以进行不同单位之间的运算
8.媒体查询
- 作用:媒体查询允许根据不同的设备或屏幕尺寸应用不同的样式。是创建响应式页面的关键。
- 语法:@media 媒体类型 and (媒体特性) { body { background-color: lightblue; } }
- 媒体类型:
all
: 适用于所有设备。print
: 主要用于打印机和打印预览模式。screen
: 主要用于电脑屏幕、平板、智能手机等。speech
: 适用于基于语音识别的设备。
- 媒体特性:
width
和height
: 描述设备的显示区域大小。orientation
: 检测设备的方向(竖屏或横屏)。[aspect-ratio](){"sa":"re_dqa_zy","icon":1}
: 设备的宽高比。color
、color-index
、[monochrome](){"sa":"re_dqa_zy","icon":1}
: 与颜色显示相关的特性。resolution
: 设备的分辨率
9.css盒模型
- 盒子模型分为两种:
- W3C 标准的盒子模型(标准盒模型)可以通过box-sizing:content-box来设置;
- IE 标准的盒子模型(怪异盒模型)可以通过box-sizing:border-box来设置;
- 标准盒模型与怪异盒模型的表现效果的区别之处:
- 标准盒模型下盒子的大小 = content + border + padding + margin
- 怪异盒模型下盒子的大小=width(content + border + padding) + margin
10.css选择器权重
!Important > 行内样式 > ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性
11.如何实现一个元素水平居中
- 块级元素
- postion:abslute;
top:50%;
left:50%;
transform:translate(-50%,-50%); - display:flex;
justify-content:center;
align-items:center; - display: grid;
place-items: center;
- postion:abslute;
- 行内元素
- text-align:center;
line-height:20px;
- text-align:center;
12.css中哪些属性是可以继承的、哪些是不可以继承的
- 能继承的属性
- 字体系列属性:font、font-size、font-weight、font-family、font-style;
- 文本系列属性:
- 内联元素:color、line-height、text-align
- 块级元素:text-align、text-indent;
- 元素可见性:visibility
- 不能继承的属性
- 盒子模型的属性:display、overflow、width、height、min-width、min-height、max-width、max-height、margin、padding、border;
- 背景属性:background、background-color、background-image;
- 定位属性:float、clear、position、top、right、bottom、left;
13.Input元素type属性值
- text 默认。定义单行输入字段,用户可在其中输入文本。默认是 20 个字符;
- password 定义密码字段。字段中的字符会被遮蔽;
- search 定义用于搜索的文本字段;
- number 定义带有 spinner 控件的数字字段;
- email 定义用于 e-mail 地址的文本字段;
- url 定义用于 URL 的文本字段;
- radio 定义单选按钮。
- checkbox 定义复选框。
- button 定义可点击的按钮(大多与 JavaScript 使用来启动脚本)
- reset 重置按钮,用于重置表单数据。
- submit 定义提交按钮。提交按钮向服务器发送数据。
- range 滑动条,用于选择一个范围内的值
- color 定义拾色器。
- image 定义图像作为提交按钮;
- file 定义输入字段和 "浏览..." 按钮,供文件上传
- date 定义日期字段(带有 calendar 控件)
- month 定义日期字段的月(带有 calendar 控件)
- time 定义日期字段的时、分、秒(带有 time 控件)
14.display和visibility的区别
-
占用空间:dispaly:none 设置该属性后,该元素下的元素都会隐藏,占据的空间消失;visibility:hidden 设置该元素后,元素虽然不可见了,但是依然占据空间的位置。display:none 会引起回流(重排)和重绘 visibility:hidden 会引起重绘。
-
继承性:visibility 具有继承性,其子元素也会继承此属性,若设置 visibility:visible,则子元素会显示。
-
动画:transition动画中支持 visibility 属性,但是不支持 display,因为 transition 可以延迟执行,因此配合 visibility 使用纯 CSS 实现 hover 延时显示效果可以提高用户体验。
-
计数器:visibility 不会影响计数器的计算,虽然隐藏掉了,但是计数器依然继续运行着。
15.BFC模式
- BFC(Block formatting context)直译为"块级格式化上下文"。它是一个独立的渲染区域,并且与这个区域外部毫不相干。它定义了元素如何在其所在的块级容器中布局,以及与其他元素的关系和相互作用。
- BFC模式的作用:
- 解决margin塌陷的问题;
- 避免外边距margin重叠(margin合并);
- 清除浮动;
-
阻止元素被浮动元素覆盖;
- 哪些元素会生成 BFC:
- 浮动元素(元素的float不是none)
- 绝对定位元素(元素的position为absolute 或 fixed)
- display为inline-block、table-cell、flex、grid或 inline-grid
- overflow值不为visible的块元素
- contain 值为layout、content 或 paint的元素
16.如何清除浮动
- 方案1 BFC模式:让标准流中父容器变为BFC模式,这样就可以保证父容器中子元素渲染不会影响外界了(即:不会引起父容器高度变化)
- 方案2 clear:利用clear样式来清除浮动引起的父容器高度塌陷。在浮动元素后添加一个元素,并设置其clear属性为both。这会使得该元素下移,直到其顶部在所有前面的左浮动和右浮动元素下面。
- 方案3 伪元素:在父元素中添加一个伪元素(::before,::after),并设置其clear属性为both。这种方法的优点是不需要添加额外的HTML元素。
17.如何解决margin塌陷问题
- **第一种情况:**两个同级元素,垂直排列,上面的盒子给 margin-bottom 下面的盒子给margin-top,那么他们两个的间距会重叠,以大的那个计算。
- 解决方法:两个外边距不同时出现
- **第二种情况:**两个父子元素,内部的盒子给 margin-top,其父级也会受到影响,同时产生上边距,父子元素会进行粘连。解决方案:
- 为父盒子设置 border
- 为父盒子设定 padding 值
- 为父盒子添加 overflow:hidden;
- 为父盒子添加 position:fixed;
- 为父盒子添加 display:table;
18.伪类和伪元素
- 伪类
- a:link 选取用户未访问的 标签,而且鼠标没有悬停在其上。
- a:visited 选取已被访问过的 标签。
- a:hover 选取鼠标悬停在其上的 标签。
- a:active 选取用户正在点击的 标签。
- p:target 选择器可用于选取当前活动的目标元素。
- p:checked 选中 单选框或复选框被选中 的元素
- p:disabled 选中禁用状态下的表单控件。
:first-child
匹配第一个子元素。- :last-child 匹配最后一个子元素。
- p:nth-child(n) 选择属于其父元素的第 n 个子元素
- p:nth-last-child(n) 选择属于其父元素的倒数第 n 个子元素
- p:nth-of-type(n) 选择属于其父元素特定类型的第 n 个子元素
- p:nth-last-of-type(n) 选择属于其父元素特定类型的倒数第 n 个元素
- 伪元素
- ::before 在元素前插入一个伪元素
- ::after 在元素后面插入一个伪元素
::first-letter
匹配元素中文本的首字母。::first-line
匹配元素中第一行的文本(只能在块元素中使用)。::selection
匹配被用户选中的部分。
19.伪类和伪元素的区别
- 伪类存在的意义是为了通过选择器,格式化DOM树以外的信息以及不能被常规CSS选择器获取到的信息。伪元素可以创建一些文档语言无法创建的虚拟元素。
- 伪类用单冒号:表示;而伪元素用双冒号::表示。
- 一个选择器可以同时使用多个伪类(但有的伪类会互斥);而一个选择器只能同时使用一个伪元素(未来的版本可能会支持多伪元素)。
20.使用css如何让浏览器支持比12px小的字体,比如10px
针对谷歌浏览器内核,加 webkit 前缀,用 transform:scale()这个属性进行缩放!
p span{
display:block;
font-size:10px;
-webkit-transform:scale(0.8); //缩放
}
21.为什么要使用less、sass
- 嵌套:允许子选择器嵌套在父选择器内,提高样式的可读性,缓解css命名冲突问题;
- 变量:允许定义和重复使用样式中的值,轻松管理颜色、字体大小等属性;
- 混合(Minxins):可以创建样式块,实现样式的重用;
- 继承:允许选择器继承另一个选择器的样式,以提高代码的复用性;
- 函数:允许自定义函数,以便执行各种操作。
22.less和sass的区别
less | sass | |
---|---|---|
实现方式 | 基于JavaScript,是在客户端 进行处理的 | 基于Ruby,是在服务器端 进行处理的 |
语法 | 大括号 | 缩进 |
定义变量 | @ | $ |
混合(Mixins) | 直接命名、直接通过命名调用 | 通过@mixin定义、通过@include引入 |
继承 | .a { &:extend(.b) } | .a { @extend .b } |
工具库 | Less有UI组件库 Bootstrap, Bootstrap是Web前端开发中一个比较有名的前端UI组件库, Bootstrap中样式文件的部分源码就是采用Less语法编写的。 | Sass有工具库 Compass。简单说,Sass和 Compass的关系有点像 JavaScript和 jQuery的关系, Compass是Sass的工具库。在它的基础上,封装了一系列有用的模块和模板,补充和强化了Sass的功能 |
引用外部css | LESS引用外部文件的方式与CSS中的@import相似 | Sass引用外部文件时必须以“\_” 开头,文件名如果以下划线“_”命名,Sass会认为该文件是一个引用文件,不会将其编译为CSS文件。 |
输出 | LESS没有内置的输出设置选项 | SASS提供四种输出选项——nested(嵌套缩进的CSS代码)、expanded(展开的多行CSS代码)、compact(简洁格式的CSS代码)、compressed(压缩后的CSS代码) |
其他 | 无 | @if、@else、@for、@while、@each、@function、扩展scss |
23.flex布局属性
- justify-content主轴对齐方式
-
flex-start 左对齐(默认) flex-end 右对齐 center 居中对齐 space-between 均匀分布、左右不留 space-around 均匀分布、左右少许间隙 space-evenly 均匀分布、左右相等间隙
-
- align-items侧轴对齐方式
-
flex-start 上对齐 flex-end 下对齐 center 居中对齐 stertech 拉伸铺满(默认) inline 根据内容文字对齐
-
- align-content行轴对齐方式,只有1行时不生效
-
flex-start 上对齐 flex-end
下对齐 center 居中对齐 space-between 均匀分布、上下不留 space-around 均匀分布、上下少许间隙 space-evenly 均匀分布、上下相等间隙
-
- flex属性:
- 第一个参数表示: flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
- 第二个参数表示: flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
- 第三个参数表示: flex-basis给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小
JavaScript
1.js的数据类型有哪些
- 基本类型:null,undfiend,boolean,number,string,symbol,bigin
- 引用类型:Object(包含Object,Array,Function)
2.ES6新增了哪些内容
let块级作用域声明方式、const声明常量、箭头函数、模版字符串、解构赋值、展开运算符、模块、class类、Set、Map、Promise
3.常用的运行算符
- 算术运算符:+ - * / % ++ -- **
- 比较运算符:== === != !== > < >= <=
- 逻辑运算符:&& || !
- 展开运算符
- 三元运算符
4.运算符的优先级(从高到低)
-
小括号
()
-
一元运算符
delete ++ -- !
-
算术运算符 先乘除
*
/ % 后加减
+
- -
移位运算符
<< >>
-
比较运算符 先大小
>
< >= <= 后相等 == !=
-
按位运算符
&
^
|
-
逻辑运算符
&& ||
-
三元运算符
-
赋值
= += -= *= /= %= &= |= ^= <<= >>= >>>=
5.说一下innerHTML 与 innerText的作用与区别?
- 作用:都可以获取或者设置元素的内容
- 区别:innerHTML可以解析内容中的html标签
- innerText不能解析内容中的html标签
6.JavaScript 由以下三部分组成:
- ECMAScript:是JavaScript的核心,描述了语言的基本语法和数据类型。它是一套标准,定义了一种语言的规范,规定了基本语法、数据类型、关键字等的设计规范,也是解析引擎设计的参考标准,但与具体实现无关。
- DOM(文档对象模型):是一套操作页面元素的API,可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作,从而改变页面的内容和结构,顶层是document对象。
- BOM(浏览器对象模型):是一套操作浏览器功能的API,通过BOM可以操作浏览器窗口,比如弹出框、控制浏览器跳转、获取分辨率等,与浏览器交互的部分功能都是通过BOM来实现的,顶层是window对象。
DOM和BOM的区别
- 含义不同。DOM表示把文档当做一个对象来对待,这个对象主要定义了处理网页内容的方法和接口;BOM表示把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的方法和接口。
- 结构不同。DOM中的元素被表示为节点,并按照树状结构进行组织;BOM以浏览器窗口为中心。
- 交互方式不同。DOM通过对象之间的嵌套和引用进行交互;BOM通过window对象与JS进行交互。
- 应用范围不同。DOM用于文档内容的操作和交互;BOM用于浏览器窗口和浏览器的交互。
- 标准化不同。DOM是W3C的标准;BOM缺乏标准,最初是Netscape浏览器标准的一部分。
7.介绍 JS 有哪些内置对象?
- 数据封装类对象:Object、Array、Boolean、Number、String
- 其他对象:Function、Arguments、Math、Date、RegExp、Error
- ES6 新增对象:Symbol、Map、Set、Promises、Proxy、Reflect
8.说几条写 JavaScript 书写的基本规范?
- 代码一定要正确缩进,建议使用"二个或者四个空格"缩进
- 语句结束使用分号;
- 规范定义 JSON 对象,补全双引号
- 用{}和[]声明对象和数组
- 变量和函数在使用前进行声明
- 以大写字母开头命名构造函数,全大写命名常量
- 代码段使用花括号{}包裹
- 还有要书写正确的标识标签
9.什么是标识符?
- 在JS中,可以自定义命名的东西都属性标识符;
- 比如变量名,函数名,参数名都是标识符
10.offsetWidth,clientWidth,scrollWidth的区别?
- offsetWidth:对象整体的实际宽度,包含滚动条等边线,会随对象显示大小的变化而改变。返回值包含 content + padding + border + 包含滚动条;
- clientWidth:对象内容的可视区的宽度,不包含滚动条等边线,会随对象显示大小的变化而改变。返回值只包含 content + padding,不包含滚动条;
- scrollWidth:对象的实际内容的宽度,不包边线宽度,会随对象中内容超过可视区后而变大。返回值包含 content + padding + 溢出内容的尺寸;
11.解释什么是Json
- json是一种轻量级的数据交换格式,一般用于数据传递
- 里边只允许出现双引号
- JSON的语法表示三种类型值,简单值(字符串,数值,布尔值,null), 数组,对象
12.检测数据类型的方法有哪些
- typeof:基本类型检测;
- instanceof:判断a是否是b的实例;
- Object.prototype.toString.call() :使用call把Object对象的toString方法指向value,获取value的原生构造函数名;
- constructor:属性可能会被改写,导致检测结果不准确;
13.为什么typeof检测null等于object
null 其实属于自己的类型 Null,而不属于Object类型。因为JavaScript 数据类型在底层是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。 属于javascript历史遗留问题。
14.为什么typeof检测Array等于object
在JavaScript里 array 的原型是object.prototype,所以array会显示object
15.为什么typeof检测函数不等于object
如果一个对象实现了[[call]]方法就是function,否则为object。
16.null和undfiend的区别
- null。表示一个空的对象指针,通常用于表示一个变量不包含任何对象。在JavaScript中,null被视为一个空值或缺少值的对象指针,它是一个被明确赋予的值,用来表示没有有效的对象引用。转化为数值是0
- undefined。表示一个未初始化(未定义)的值,通常用于表示尚未赋值的变量。在JavaScript中,undefined是一个全局变量,在没有被赋值的情况下,默认的初始值就是undefined。当你尝试使用一个未初始化的变量时,JavaScript引擎会返回undefined。转化为数值是NAN。
17.其他类型转化成数值
- null 转换之后是 0
- undefined 转换之后是 NaN
- Object 转换之后是 NaN
- String
- 如果字符串中都是数值, 那么就可以正常转换
- 如果字符串是一个空串
""
/" "
, 那么转换之后就是0
- 如果字符串中不仅仅是数字, 那么转换之后是
NaN
- Boolean
true
转换之后是1
false
转换之后是0
- Array
- 空数组,当将其转换成number时,得到的结果为0;
- 只有一个数字成员的数组,得到的结果是该数字;
- 多个数字成员的数组,得到的结果为NaN;
18.介绍一下symbol?怎么迭代symbol描述的键?
- symbol是js为了解决命名冲突而引入的一种ES6新的基本数据类型,表示独一无表示独一无二的值。
- 独一无二性:每个 Symbol 值都是唯一的,无法通过简单的值比较相等。
- 不可变性:Symbol 值一旦创建,就不能被修改。
- 隐藏性:使用 Symbol 作为属性键,这些属性对于常规的对象遍历和操作是不可见的。
- 作为属性键:Symbol 可以作为对象的属性键,用于创建对象的私有属性或隐藏属性,以避免命名冲突。
- symbol常用的的方法
- Symbol.for() 全局符号注册表
- Symbol.keyFor() 查询全局注册表
- Object.getOwnPropertySymbols() 获取对象内部symbol命名的key
- Reflect.ownKeys() 获取对象内部所有的key
19.介绍一下bigint?为什么会出现bigint
- bigint是js为了解决数值过大导致的精度失真问题而引入的一种ES6新的基本数据类型,表示表示任意长度的整数。
- bigint不能直接和普通数据类型进行运算,必须保证参与运算的值都是bigint类型
- 创建bigint的方式有两种
- 在一个整数字面量后面添加一个字母
n
,如9007199254740991n
- 使用
BigInt()
函数将普通整数值转换为BigInt类型,如BigInt(9007199254740991)
- 在一个整数字面量后面添加一个字母
20.var、let、const的作用
- var :变量声明方式,声明的变量具有变量提升,一般情况下会提升到全局,但是在函数内部会形成局部变量。支持重复声明,支持修改。
- let:块级作用域声明方式,不具备变量提升。不支持重复声明,支持修改。
- const:常量声明方式,不具备变量提升。不支持重复声明,不支持修改。
21.const声明的引用类型数据可以修改吗
因为const只能浅层检测引用类型数据指针(地址),所以不能直接修改引用类型数据的指针(地址),但是可以修改引用类型数据内部的元素。
22.Math常用的方法
- min、max、abs绝对值、PI、pow幂、sqrt平方根、sell向上取整、floor向下取整、trunc保留整数位、round四舍五入、random随机数
23.数值常用的方法
- toFixed(digits):将数字转换为指定小数位数的字符串。
- toPrecision(precision):将数字转换为指定有效位数的字符串。
- isNaN(value):检查一个值是否为 NaN(非数字)
- isFinite(value):检查一个值是否是有限数。
- parseInt(string, radix):将字符串解析为整数。
- parseFloat(string):将字符串解析为浮点数。
- Number()将其他类型数据转化成数值
24.字符串常用的方法
- 不改变原字符串:trim、toLowerCase、toUpperCase、chartAt、includes、indexOf、lastIndexOf、search、match、startsWith、endsWith、silce、substr、substring、replace、replaceAll、concat、repeat、split。
25.函数常用的方法
- call、apply、bind
25.对象常用的方法
- 不改变原对象:Object.keys、Object.values、Object.entries、Object.isFrozen、obj.hasOwnProperty、key in obj
- 改变原对象:Ovject.create、Object.assign、Object.freeze、Object.defineProperty、Object.defineProperties
26.数组常用的方法
- 改变原数组:push、pop、unshift、shift、store、reverse、splice、fill;
- 不改变原数组:at、includes、indexOf、lastIndexOf、slice、concat、flat(Infinity)、toString、join、some、every、find、findindex、forEach、Map、flatMap、filter、reduce、reduceRight
27.伪数组
- 伪数组的特征
- 具有length属性;
- 按索引方式存储数据;
- 不具有数组的方法.
- 伪数组转数组的方法
- Array.from()
- Array.prototype.slice.call()
- 展开运算符(...)
- 常见的伪数组
- new Set()
- arguments
- document.getElementsByClassName('div')
28.循环语句
- while:while 语句只要指定条件为 true,就会执行循环
- 参数1:必须。定义执行循环的条件
- do/while:先执行一次代码块,再检查条件是否为真,为真就继续循环
- 参数1:必须。定义执行循环的条件
- for:可以指定代码块循环的次数
- 参数1:可选,初始化的变量,也可以指定多个;
- 参数2:可选,条件判断语句;
- 参数3:可选,每次循环后执行的语句;
- for/of:用于遍历数组
- 参数1:必须。数组的元素。
- 参数2:必须。指定遍历的数组。
- for in:用于迭代对象或者数组
- 参数1:必须。对象的键或数组的索引。
- 参数2:必须。指定迭代的的对象或者数组。
- forEach:遍历数组
- 参数1:对数组元素执行的回调函数
- 参数2:this指向
- forEach没有返回值
29.forEach、for中断循环、跳出循环?
- forEach:中断循环try/catch/throw;跳出当前循环return;
- for:中断循环break;跳出当前循环continue;
30.for of为什么不可以遍历对象
对象是不可迭代的。es6中引入了iterator接口,只有提供了iterator接口的数据类型才可以使用“for-of”来循环遍历;而普通对象默认没有提供iterator接口,因此无法用“for-of”来进行遍历。
31.字符串slice和substring的区别
- 都可以从字符串中指定起止位置提取并返回一个新的字符串,且不修改原数组。
- 两者第二个参数都可以省略(默认提取到字符串末尾)。
- slice如果参数是负数,则表示从字符串的尾部开始计数,。
- substring如果任一参数是负数,它会被视为0。
32.数组去重的方法有哪些
- Array.from(new Set())
- arr.filter((item, index) => arr.indexOf(item) === index);
- arr.reduce((total, item) => (total?.includes(item) ? total : [...total, item]) , []);
33.数组拍平的方法有哪些
- arr.flat(Infinity)
- arr.toString().split(',').map((item) => Number(item));
- arr.reduce((total, item) => (Array.isArray(item) ? [...total, ...flat(item)] : [...total, item]),[],);
34.arguments
- 在JavaScript函数内部,arguments对象是一个类数组对象,它存储了函数被调用时传递的所有参数。
- 动态参数列表:arguments对象可以接受任意数量的参数。这意味着你可以在调用函数时传递任意数量的参数,而不需要在函数定义中明确指定形式参数的个数。
- 通过索引访问参数:可以通过arguments对象的索引来访问函数的参数。例如,arguments[0]表示第一个参数,arguments[1]表示第二个参数,以此类推。
- 类数组对象:虽然arguments对象看起来像一个数组,但它并不是一个真正的数组。它没有数组特有的方法,如push()和pop(),但可以通过length属性获取参数的数量。
35.什么是纯函数
- 返回值取决于参数
- 不依赖于外部状态,也不改变外部状态的函数
36.匿名函数(IIFN)
- 匿名函数,即没有名称的函数
- 如果单独只写一个匿名函数,此时是不符合语法要求的 会报错。需要给 匿名函数包裹一个括号,使之成为表达式。
- 被小括号包裹的内容会被js识别为一个函数表达式
37.什么是闭包函数
- 在函数内部声明一个函数,并且使用了外部函数的变量,被称为闭包函数
- 闭包函数的优点:实现私有属性和方法,减少对外界的污染;
- 闭包函数的缺点:外部函数执行完之后,内部函数不会被销毁,造成变量长期贮存,占用内存空间
- 解决闭包:使用call,apply,bind改变内部函数的this指向;内部函数改用箭头函数
- 使用场景:防抖、节流、缓存、封装私有变量、函数柯里化等
38.函数柯里化
概念:把一个接收多个参数的函数变成接收单一参数 并且返回能够接收新参数的函数
39.什么是递归函数
- 一个函数直接或间接地调用自身,称为递归。
- 常用场景:遍历属性结构、生成斐波那契数列、深拷贝
40.什么是回调函数
- 在javascript中,回调函数指的是一个被作为参数传递给另一个函数的函数;
- 回调函数本身不是异步的,但是可以通过异步机制(例如事件循环、Promises、async/await)来实现异步行为,例如定时器、延时器、axios请求、hooks、事件
41.箭头函数和普通函数的区别
- this指向。箭头函数的this在定义时就已经确定,不会在运行时改变,它继承自外围作用域的this值;普通函数的this在运行时确定,可以通过call、apply、bind方法改变。
- 原型属性。箭头函数没有原型属性;普通函数有原型属性
- 构造函数。箭头函数不能作为构造函数使用,不能通过new关键字调用;普通函数可以作为构造函数使用。
- 语法形式。箭头函数使用=>符号定义,而普通函数使用function关键字定义
- 函数体语法。箭头函数的函数体可以省略括号,但有一定的限制。如果箭头函数的函数体只有一行语句,则可以省略大括号和return关键字;如果箭头函数的函数体多于一行语句,则必须使用大括号和return关键字;普通函数的函数体必须使用大括号。
- arguments对象。箭头函数不绑定arguments对象,但可以通过剩余参数代替;普通函数有arguments对象,用于存储所有传递的参数
- Generator函数。箭头函数不能作为Generator函数使用,不能使用yield关键字;普通函数可以作为Generator函数。
42.Generator函数
- Generator函数是一种特殊类型的函数,它可以在执行过程中暂停并在需要时恢复执行。当Generator函数被调用时,它并不会立即执行,而是返回一个遍历器对象(Iterator),这个对象可以逐步遍历Generator函数的内部状态。
- Generator函数使用步骤
- Generator 函数使用
function*
声明,通过yield
关键字产生一个值,并挂起函数的执行。 - 可以通过调用
iterator.next()
方法逐一遍历迭代器对象,也可以为其传参,执行后标记为未完成。 - 当运行到
iterator.return()
语句时,迭代器对象会被标记为“完成”,不再产生新的值。
- Generator 函数使用
- iterator对象的方法:
- next()逐一遍历迭代器对象
- return()返回return的对象同时停止迭代
- throw()中断迭代
- 主要用途包括:解决回调地狱、迭代和生成无限序列、替代回调函数。
function
* generator() {
yield
'one'
;
yield
'two'
;
return
'done'
;
}
const iterator = generator(); //
返回一个遍历器对象
console.log(iterator.next());
//{ value: 'one', done: false }
console.log(iterator.next());
//{ value: 'two', done: false }
console.log(iterator.next());
//{ value: 'done', done: true }
43.防抖和节流
- 防抖(debounce)和节流(throttle)是性能优化中常用的两种技术,用以控制函数执行的频率,以减少计算资源的使用。
- 防抖:指的是在一定时间内,对于频繁触发的事件,重新计算时间,只让其最后一次触发时执行。例如搜索框、页面改变大小。
- 节流:指的是在一定时间内,对于频繁触发的事件,只让其执行一次,例如:触底加载、页鼠标不断点击时。
44.引用类型和值类型的区别
值类型 | 基本数据类型 | |
---|---|---|
存储位置 | 栈(stack) | 指针存放在 栈(stack)、实体存放在 堆(heap) |
数据大小 | 固定 | 不固定 |
占用空间 | 小 | 大 |
存取速度 | 块 | 慢 |
数据类型 | 简单 | 复杂、可嵌套 |
访问方式 | 按值访问 | 按指针访问 |
45.什么是深拷贝和浅拷贝
- 深拷贝。只复制对象内容,不复制对象的指针,拷贝出来的对象是一个全新的对象,修改这个新对象的时候,不会影响源对象。
- 浅拷贝。只复制对象的指针,不复制对象的内容,拷贝出来的新对象指针还是指向原来的对象。修改这个新对象时,原来的对象也会被修改。引用类型的数据,默认都是浅拷贝。
46.深拷贝的方法
- JSON.stringfy(JSON.parse())
- 取不到值为 undefined 的 key;
- 如果对象里有函数,函数无法被拷贝下来;
- 无法拷贝copyObj对象原型链上的属性和方法;
- 展开运算符:只能进行浅层的拷贝
- Object.assign :只能进行浅层的拷贝
- 递归实现深拷贝
- 在递归过程中,需要记录已经遍历过的对象。
- 需要处理不同数据类型(如数组和对象)。
- 使用插件实现
- lodash的cloneDeep函数
- Immutable.js生成不可变数组或对象(Map不可变对象 List不可变数组)
47.什么是同步任务和异步任务
- js是一门单线程语言,单线程意味着所有任务都需要排队,只有前一个任务执行完之后,才能执行下一个任务。这种情况显然是不合理的,比如说我挂起一个30秒的延时器,那么后面的任务就会被阻塞,导致cpu空闲且无法执行其他任务。针对这一情况,javascript把这些阻塞进程的任务统一放在了异步任务队列中等待执行,任务队列通知主线程某个异步任务可以执行了,这个异步任务就会被放在主线程中按顺序执行。
- 同步任务(synchronous)就是主线程中的任务,按照顺序一个个的执行,比如:普通函数、new Promise()、console.log()等等
- 异步任务 (asynchronous) 就是任务队列中的、会阻塞到进程的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。比如:DOM 事件回调、定时器回调、网络请求的回调、promise.then()、promise.catch()的回调。异步任务又分为宏任务和微任务:
- 宏列队 macrotask queue:用来保存待执行的宏任务(回调),比如:DOM 事件回调、定时器回调、网络请求的回调
- 微列队 microtask queue:用来保存待执行的微任务(回调),优先级高于宏任务,比如:promise.then()、promise.catch()、MutationObserver 、queueMiscrotask()
48.宏任务和微任务的区别
- 执行优先级不同。宏任务的优先级相对较低,而微任务的优先级较高。
- 执行时机不同。宏任务会被添加到事件队列中,在每个事件循环中执行一次;微任务会在当前宏任务执行完毕后立即执行,而不会添加到事件队列中,微任务的执行时机是在当前宏任务的末尾,在下一个宏任务之前。
- 用途不同。宏任务通常包括定时器任务(如setTimeout、setInterval)、网络请求、用户交互事件(如点击、滚动等),这些是相对较大的任务,需要等待一定时间或特定的触发条件才会执行;微任务通常包括Promise回调、DOM变动观察器等,这些是相对较小的任务,适合在当前宏任务执行完毕后立即执行。
- 总结:宏任务是事件循环中的较大任务,通常处理用户交互、渲染等任务;微任务是较小的任务,通常用于处理异步操作的结果,由于微任务具有较高的优先级,它们可以在用户交互之前或渲染之前得到及时处理。
49.Promise的作用
- Promise的主要作用是处理异步操作。它允许你以更同步的方式编写异步代码,提高了代码的可读性和可维护性。
- 处理异步任务结果。Promise区分成功(resolve)和失败(reject)的状态,使得错误处理更加明确和直接。
- 避免回调地狱。Promise对象可以链式调用,减少嵌套回调的使用,避免代码过于复杂和难以管理。
- 支持并发操作。例如,使用Promise.all()可以并行执行多个操作,等待所有操作完成后执行某些逻辑。
- 便于代码的模块化和复用。通过将异步操作封装成Promise对象,可以提高代码的复用性和模块化程度。
50.promise的三种状态?怎么改变promise的状态
- 等待中(Pending)。这是Promise的初始状态,表示异步操作尚未开始或结果尚未确定。
- 已完成(Fulfilled)。当异步操作成功完成并返回结果时,Promise的状态变为Fulfilled。
- 已拒绝(Rejected)。当异步操作执行失败时,Promise的状态变为Rejected。
51.Promise.then和Promise.catch
- Promise函数有两个参数resolve和reject,当我们调用resolve()时,Promise函数会从等待状态变为成功状态,当我们调用reject()时,Promise函数会从等待状态变为失败状态。
.then():在promise函数中调用
resolve()后执行的函数,用于处理执行成功的结果。可以通过resolve(参数)获得参数。.catch():在promise函数中调用
reject()后执行的函数,用于处理执行失败的结果。可以通过reject(参数)获得参数。- .then和.catch都属于异步任务中的微任务。
52.promise.all和promise.race的区别
Promise.all
和Promise.race
都是用于处理多个Promise实例的方法,但它们的行为和用途有所不同。Promise.all
。接收一个Promise数组作为参数。只有所有结果都执行成功或失败的时候,才会变为成功状态或者失败状态,否则仍然会处于等待状态。如果所有Promise都执行成功,则返回所有执行成功的结果。如果有一个执行失败,就会返回第一个执行失败的结果。Promise.race
。接收一个Promise数组作为参数,返回的第一个执行成功的Promise结果。romise.all
适用于需要等待所有异步操作完成再进行下一步处理的场景,例如并行下载多个文件并将它们合并为一个文件;Promise.race
适用于需要在多个异步操作中获取最先完成的结果的场景,例如设置超时机制或不知道哪个接口响应更快的情形。
53.proimse是同步还是异步
Promise本身是同步的。Promise是一个用于异步编程的对象,它允许你以同步的方式编写异步代码,但Promise对象本身在创建时立即执行,不会造成主线程的阻塞。然而,Promise的回调函数,如then()和catch(),是异步执行的,它们会在当前脚本的所有同步任务执行完毕后调用。
54.promise.then的交替执行
如果是单个promise实例,即使有多个then,仍然会按照顺序执行。如果是多个promise实例同时调用.then,then会出现交替执行的情况。这个是编译器做的优化,主要是为了避免某一个promise占用的时间太长。
55.怎么解决回调地狱
- promise实现链式调用
- generator函数
- async/await
56.不使用promise能否把请求数据返回出来?
- 回调函数(类似于react中的子父传值)
- 监听事件
const xhr = new XMLHttpRequest();
xhr.addEventListener("load", function(data) {
console.log(data);
});
xhr.open("GET", "http://www.example.com/");
xhr.send();
57.async和await
async
和await
是ECMAScript 2017 (ES8)标准引入的新特性,用于简化异步操作。async:用于修饰一个异步操作的函数
,该函数返回一个Promise对象。如果函数中没有返回值,会默认返回一个Promise对象。如果在函数中 return 一个直接量,async 会把这个直接量通过Promise.resolve()
封装成 Promise 对象;- await:用来等待一个异步方法执行完成。后面await如果是一个 Promise 对象,返回该Promise执行成功或者失败的结果。如果不是 Promise 对象,就直接返回对应的值。注意await 只能出现在 async 函数中,await会阻塞进程。
58.原型和原型链
- 每个对象都有一个prototype属性,表示对象的原型(prototype也是一个对象)。prototype主要用于实现继承、属性的共享、方法的调用。
- prototype作为对象的内部属性,是不能被直接访问的,但是可以通过__proto__来访问。
- 原型链,当访问对象的属性或方法时,首先对象会从自身去找,找不到就会往原型(prototype)中去找,如果原型(prototype)中找不到,就会往原型后面的原型上去找,这样就形成了链式的结构,称为原型链。
- 原型链的最顶层是Object,在往上就是null。
59.this指向问题
- 全局作用域中的函数:非严格模式下其内部this指向window
- 对象内部的函数:其内部this指向对象本身:
- 构造函数:其内部this指向生成的实例:
- 由apply、call、bind改造的函数:其this指向第一个参数:
- 箭头函数:箭头函数没有自己的this,看其外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。(函数定义时的this,而不是调用时this)
60.怎么改变this指向?
- call():在改变this指向的同时会调用函数
- apply():在改变this指向的同时会调用函数,第二个参数接收的是一个数组。
- bind():在改变this指向的同时不会调用原来的函数,而使生成并调用一个新的已经改变过this指向的函数
61.new做了什么
- 在内存创建一个新对象
- 把构造函数中this指向新建的对象
- 会在新对象上添加一个__proto__属性,指向函数的原型对象prototype
- 判断函数返回值,如果值是引用类型就直接返回值;否则返回this(创建的新对象)
62.构造函数
- 什么是构造函数:JS中的任何一个普通函数,当用new关键字来调用时,它就是构造函数。构造函数与函数定义无关,与调用方法有关。构造函数一般首字母大写。
- 构造函数的目的:在JavaScript中,构造函数是用来初始化新创建的对象的函数。构造函数的主要目的是在创建对象时初始化对象的属性。
- 构造函数的意义:使用对象字面量创建一系列同一类型的对象时,这些对象可能具有一些相似的属性和方法,此时会产生很多重复的代码,把这些重复性的特征和属性抽象出来,做成构造函数,可以实现代码复用。
- 构造函数的作用:构造新对象,设置对象的属性和方法。创建对象时完成初始化,当我们在new一个对象并传入参数的时候,会自动调用构造函数并完成参数的初始化。
- 常见的构造函数:Object、Array、String、Boolean、Number、Date等。
- 构造函数的this指向:
- 当以函数的形式调用时,this是window
- 当以方法的形式调用时,谁调用方法this就是谁
- 当以构造函数的形式调用时,this就是新创建的那个对象
- 自定义构造函数:
- 首字母大写
- 通过new创建实例对象
- 创建构造函数时,里面的属性和方法前必须加this,this就表示当前运行时的对象
- 返回值
- 不写return,返回一个this对象
- return一个基本数据类型,返回一个this对象
- return一个复杂数据类型,返回一个复杂数据类型,比如对象、数组
63.构造函数和普通函数的区别
- 普通函数是小驼峰的名命方式,而构造函数是大驼峰的名命方式(行业规范)。
- 我们知道普通函数的this指向是指向全局对象的,而构造函数内部的this指向当前对象的实例。
- 使用的方式不同,普通函数直接调用,构造函数必须使用new 来调用,通过 new.target 来判断调用的方式是不是构造函数。
- 任何函数只要使用new操作符调用就是构造函数,而不使用new操作符调用的函数就是普通函数
63.es6class类
- 类(class)是ES6新的基础性语法糖结构,用于创建对象的模板。可以看成构造函数的另一种写法,这种写法可以让对象原型的写法更加清晰、更像面向对象编程的语法而已。
- 类必须使用
new
调用,否则会报错。普通构造函数使用new创建的是实例化对象,不使用new则执行的是普通函数的调用。 - 类的数据类型就是函数,类本身就指向构造函数。
- 函数受函数作用域限制,而类受块作用域限制。
64.constructor
- constructor 方法是一个特殊的方法,用于创建和初始化一个由
class
创建的对象。通过 new 关键字生成对象实例时,自动会调用该方法。 - 一个类只能拥有一个名为"constructor"构造函数,不能出现多个,如果定义了多个"constructor"构造函数,则将抛出 一个SyntaxError错误。
- 如果没有定义"constructor"构造函数,class 会默认添加一个空的"constructor"构造函数。
65.super
- super代表的是父类的构造函数。super可以用来调用父类的属性和方法,也可以用来调用父类的构造函数。
- super继承。
- ES6 class 可以通过
extends
关键字实现继承,同时子类必须在constructor中调用super,否则新建实例时会报错。这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super
方法,子类就得不到this
对象,只有调用super
之后,才可以使用this
关键字。 - 子类如果没有定义constructor方法,super方法会被默认添加。
- ES6 class 可以通过
- super方法。
super
作为函数调用时,代表父类的构造函数。super
虽然代表了父类的构造函数,但是返回的是子类的实例,即super
内部的this
指的是子类的实例,因此super()
在这里相当于A.prototype.constructor.call(this)
。
- super对象。
- 在普通方法中,指向父类的原型对象。由于
super
指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super
调用的。ES6 规定,在子类普通方法中通过super
调用父类的方法时,方法内部的this
指向当前的子类实例。由于this
指向子类实例,所以如果通过super
对某个属性赋值,这时super
就是this
,赋值的属性会变成子类实例的属性。 - 在静态方法中(statrc修饰的方法),指向父类。如果
super
作为对象,用在静态方法之中,这时super
将指向父类,而不是父类的原型对象。在子类的静态方法中通过super
调用父类的方法时,方法内部的this
指向当前的子类,而不是子类的实例。
- 在普通方法中,指向父类的原型对象。由于
66.构造器constructor为什么要使用super
因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super
方法,子类就得不到this
对象,只有调用super
之后,才可以使用this
关键字。
67.继承
1、原型链式继承:将子构造函数的 prototype 指向父构造函数的 prototype
Child.prototype = new Parent(); 缺点: 1.原型中包含的引用值会在所有实例间共享 这是因为在使用原型实现继承时,原型实际上变成了另一个类型的实例; 2.子类型在实例化时不能给父类型的构造函数传参。2、构造函数式继承:使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上
function Child (){ Parent.call(this) // 借助call 来调用Parent函数 } 缺点:只能继承父类的实例属性和方法,不能访问父类原型上定义的方法3、组合式继承:把原型链式继承和构造函数式继承的优点结合。基本的思路是:使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。
function ChildThree() { Parent.call(this) // 继承属性 } ChildThree.prototype = new Parent() // 继承方法 缺点: Parent执行了两次【Parent.call(this) 和 new Parent()】,造成了多构造一次的性能开销。4、原型式继承:用Object.create()实现,这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)
var child = Object.create(parent) 缺点:因为Object.create方法实现的是浅拷贝,所以和原型链式继承一样多个实例的引用类型属性指向相同的内存,存在篡改的可能5、寄生式继承:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象
function clone(origin){ let clone = Object.create(origin) clone.getFriends = function(){ return this.friends; } return clone } var j1 = clone(parent) 缺点:同上6、寄生组合式继承:
function clone (parent, child) { // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程 child.prototype = Object.create(parent.prototype); } function Child() { Parent.call(this); } clone(Parent, Child); var f1 = new Child()7、ES6 语法 extends
class ColorPoint extends Point { }
68.什么是js严格模式
严格模式(Strict Mode)是一种在代码中启用的特殊模式,用于提供更严格的语法和错误检查,以改善代码质量和增强安全性。使用严格模式可以帮助大家避免一些常见的错误,并禁用一些不推荐使用的特性。严格模式通过在脚本或函数的头部添加use strict; 表达式来声明。
- 变量必须先声明后使用:在严格模式下,变量必须通过 var、let 或 const 关键字进行声明,否则会抛出 ReferenceError。在非严格模式下,未声明的变量会被隐式创建,并被添加到全局对象(比如浏览器环境中的 window 对象)中。
- 禁止删除变量、函数或函数参数:在严格模式下,使用 delete 操作符删除变量、函数或函数参数会抛出 SyntaxError。
- 禁止对只读属性进行赋值:在严格模式下,对只读属性(通过 const 关键字声明的常量)进行赋值会抛出 TypeError。
- 禁止使用八进制字面量:在严格模式下,以 0 开头的数字会被视为八进制字面量,这在非严格模式下是允许的。严格模式下,使用八进制字面量会抛出 SyntaxError。
- 限制 this 值:在严格模式下,函数内部的 this 值不再是全局对象(比如浏览器环境中的 window 对象),而是undefined,除非通过 call()、apply() 或 bind() 明确指定。
- 禁止使用重复的函数参数名:在严格模式下,函数参数名不能重复。在非严格模式下,重复的函数参数名会被忽略。
- 禁止使用 with 语句:在严格模式下,使用 with 语句会抛出 SyntaxError。with 语句在非严格模式下允许将对象的属性添加到作用域链中,但这被认为是不推荐使用的特性,因为它可能导致代码可读性和性能问题。
- 限制 eval 和 arguments 的赋值:在严格模式下,无法对 eval 和 arguments 进行赋值。在非严格模式下,这种赋值是允许的。
69.怎么阻止表单提交默认行为
- e.preventDefault()
- οnsubmit事件中return false
70.js事件流是什么?怎么修改事件传播机制?怎么阻止事件传播?
- 捕获阶段:从外向里依次查找元素
- 目标阶段:从当前事件源本身的操作
- 冒泡阶段:从内到外依次触发相关的行为
- addEventListener事件监听器的第三个参数设置成true捕获false冒泡
- event.stopPropagation() 阻止事件传播
71.dom事件委托原理,有什么优缺点
- 事件委托原理
- 利用事件冒泡机制,将事件监听器设置在其父节点上,通过event.target.nodeName判断是否是子节点来实现控制子节点。
- 优点
- 可以大量节省内存占用,减少事件注册
- 可以实现当新增子对象时,无需再对其进行事件绑定
- 缺点
- 如果把所有事件都用事件代理,可能会出现事件误判
72.栈内存和堆内存
- 在JavaScript中,数据是分为两类存储的:基本类型和对象类型。基本类型值指的是那些保存在栈内存(Stack)中的数据,而对象类型值则被保存在堆内存(Heap)中。
- 栈内存是一种后进先出(LIFO)的数据结构,主要用于存储函数的局部变量、临时数据、书签等。当你创建一个基本类型的变量时,它会被存储在栈内存中,并且占据一块连续的空间。
- 堆内存是用来存储对象的地方,对象可以包含多个值,大小不固定,可以动态地增加或减少。当你创建一个对象类型的变量时,这个对象会被存储在堆内存中。
73.主线程和任务队列
- JavaScript 中的主线程和任务队列是浏览器的 JavaScript 引擎如何工作的基本概念。
- 主线程:主线程是 JavaScript 引擎用来执行执行代码的地方。当 JavaScript 引擎开始执行代码时,程序的主线程就会被创建。
- 任务队列:JavaScript 是单线程的,这意味着它只有一个主线程来执行代码。但是,JavaScript 引擎还有其他的任务队列,如微任务队列和宏任务队列。
74.执行栈和调用栈
执行栈和调用栈通常是指程序在执行过程中的两种不同的数据结构。
- 执行栈(Execution Stack):在JavaScript中,执行栈是用来存储执行上下文(Execution Context)的数据结构。每当一个函数被调用时,就会为这个函数创建一个新的执行上下文并将其推入执行栈。执行栈是后进先出(LIFO)的数据结构。当函数执行完毕,它的执行上下文就会从栈中移除。
- 调用栈(Call Stack):调用栈是一个系统级的数据结构,用于存储一个个正在被执行的函数的地址。当一个函数调用另一个函数时,被调用的函数的地址会被添加到调用栈顶部。当这个函数执行完毕,它的地址会从调用栈顶部移除。在JavaScript中,调用栈是由JavaScript引擎管理的,开发者可以通过错误栈跟踪(stack trace)来查看调用栈的状态。
75.js事件循环机制(底层原理)
事件循环用于循环监听任务队列中是否有任务需要执行。如果有,则将任务添加到调用栈中执行。当调用栈中的代码执行完成后,事件循环线程会从任务队列中取出一个任务添加到调用栈中执行。这个过程是一直重复的,直到任务队列中没有任何的任务为止。
总的来说,JavaScript事件循环机制的工作原理是通过执行代码、收集事件,然后按照特定的顺序(先微任务后宏任务)循环执行队列中的子任务。这种机制使得JavaScript能够在单线程环境中处理异步操作,提高了代码的效率和响应速度。
76.target 和 currentTarget 区别
- 都是事件对象上的属性
- event.target:返回触发事件的元素
- event.currentTarget:返回绑定事件的元素(相当于事件中this)
77.浮点数精度失真
- 计算机内部使用二进制浮点数表示法,而不是十进制。这种二进制表示法在某些情况下无法准确地表示某些十进制小数,从而导致精度丢失。
- 解决方法
- 保留指定位数的小数
- 获取最大小数位,根据最大小数位乘以10的倍数
- 使用decimal.js
78.import和require的区别
- 使用方式。import用于导入整个模块或模块中的特定部分,使用export default或export命令进行导出;require用于导入整个模块,使用module.exports或exports命令进行导出。
- 加载时机。import是在编译时加载,必须放在文件的开头;require是在运行时加载,可以放在代码的任何位置。
- 解构赋值。import支持解构赋值,可以直接导入模块中导出的值、函数或对象;require进行的是赋值操作,导入的是模块导出的对象。
- 所属规范。import是ES6(ECMAScript 2015)引入的关键字,属于ES模块化语法规范;require是CommonJS规范的一部分,主要用于Node.js环境。
- 动态绑定。import提供静态分析,支持宏和类型检验;require提供动态绑定,更适合服务器或浏览器环境。
- 此外,由于历史原因和兼容性问题,在Node.js中,import语法通常需要通过Babel等工具转码为require语句才能使用。尽管import是ES6标准的一部分,并且在现代JavaScript开发中非常常用,但require仍然被广泛支持,特别是在Node.js社区中。开发者可以根据项目需求和目标平台选择使用import或require。
79.postMessage
- 作用:“跨文档消息传递”,又称为“窗口间消息传递”或者“跨域消息传递”。postMessage方法允许来自一个文档的脚本可以传递文本消息到另一个文档里的脚本,而不用管是否跨域,可以用这种消息传递技术来实现安全的通信。具体使用场景如下:
- 页面和其打开的新窗口的数据传递
- 页面与嵌套的 iframe 消息传递
- 多窗口之间消息传递
- 跨域数据传递
- 发送消息:otherWindow.postMessage(message, targetOrigin);
- otherWindow:其他窗口的一个引用。比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象等。
- message:要发送的消息。它将会被结构化克隆算法序列化,所以无需自己序列化,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化。
- targetOrigin:“目标域“。URI(包括:协议、主机地址、端口号)。若指定为”*“,则表示可以传递给任意窗口,指定为”/“,则表示和当前窗口的同源窗口。当为URI时,如果目标窗口的协议、主机地址或端口号这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会发送。
-
接收消息:当调用
postMessage()
方法的时候,目标窗口的Window对象上就会触发一个message
事件。为window添加message事件即可获取postMessage传来的消息。 - 安全问题:
- 如果你不希望从其他网站接收 message,请不要为 message 事件添加任何事件监听器。
- 如果你确实希望从其他网站接收message,请使用使用事件对象中的 origin 和 source 属性验证发件人的身份。
- 当你使用 postMessage 将数据发送到其他窗口时,始终指定精确的域名,而不是 *。
80.面向函数编程和面向对象编程
- 面向函数的编程(面向过程)和面向对象的编程(面向对象)是两种不同的编程范式。
- 面向函数编程(Functional Programming)强调的是执行的过程,即先后顺序,注重的是执行流程的封装。
- 面向对象编程(Object-Oriented Programming)则是基于对象的概念,将数据和方法封装在对象中,然后通过对象之间的交互来完成任务。
81.发布订阅模式
- 发布订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
- 布—订阅模式有以下优点:
- 可以广泛应用于异步编程中,这是一种替代传递回调函数的方案。比如,我们可以订阅ajax请求的error、success等事件,我们无需过多关注对象在异步运行期间的内部状态,只需要订阅感兴趣的事件发生点就可以。
- 可以取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口。发布—订阅模式让两个对象松耦合地联系在一起,虽然不太清楚彼此的细节,但这不影响它们之间相互通信。当有新的订阅者出现时,发布者的代码不需要任何修改;同样发布者需要改变时,也不会影响到之前的订阅者。只要之前约定的事件名没有变化,就可以自由地改变它们。
-
我们按照以下思路实现一个发布—订阅模式:
-
指定谁充当发布者(比如公众号运营者)
-
给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者(关注的列表)
-
发布消息的时候,发布者会遍历缓存列表依次触发里面的回调函数(给每个关注的人发通知消息),我们还可以往回调函数里填入一些信息,订阅者接收到这些信息之后可以进行各自的处理。
-
var officialAccount = {}; // 定义公众号拥有者
officialAccount.clientList = []; // 缓存列表
// 添加订阅者
officialAccount.listen = function (fun) {
this.clientList.push(fun);
};// 发布消息
officialAccount.trigger = function () {
for (let i = 0, fun; (fun = this.clientList[i++]); ) {
fun.apply(this, arguments);
}
};
82. 为什么js是单线程而不是多线程
如果js是多线程的,那么当我们同时添加和删除某个元素时,就会启动两个线程,我们无法确定先执行哪个导致最终的结果不同。js使用单线程是为了保证程序执行的一致性。
83. 把script标签放在body之前和之后有什么区别
-
将
<script>
放在<head>
标签中:这种方式会在页面开始加载时就加载和执行脚本,但在<body>
内容完全加载之前。 -
将
<script>
放在<body>
标签的底部:这种方式会在整个页面都加载完成后再执行脚本,通常用于延迟执行,直到页面加载完成。 -
将
<script>
放在外部文件中,然后通过<script src="path/to/your/script.js"></script>
引入:这种方式可以使得JavaScript代码更容易管理和复用,<script>
标签通常放在<body>
的底部。 -
将
<script>
放在<body>
标签的外部:按照html标准,在body之后出现的任何标签都是错误的,浏览器会自动化容错,视作仍然在body体内。
84.defer和async的区别
defer和async都是用来延迟加载的方式,defer并行加载js文件会按照页面上的script标签的顺序执行,async并行加载js文件,下载完成立即执行,不会按照页面上的顺序。
85.静态方法
- 声明方式:通过在方法前使用static关键字来声明一个静态方法。
- 访问方式:静态方法只能通过类名直接调用,不能通过类的实例来访问。
- 继承特性:静态方法不会被子类继承,因此子类不能调用父类的静态方法,除非子类中也定义了同名的静态方法。
- this指向:在静态方法中,this关键字指向的是类本身,而不是类的实例。
- 重名允许:静态方法可以与普通实例方法重名,这意味着在同一类中可以有同名的静态方法和非静态方法,它们各自通过不同的方式(类名或实例)被调用。
86.垃圾回收机制
-
在JavaScript中,垃圾回收(Garbage Collection)是一种自动内存管理机制,它可以自动地识别不再使用的变量和对象并将它们从内存中清除,以释放内存空间。JavaScript中的垃圾回收机制主要有两种:标记清除(Mark-and-Sweep)和引用计数(Reference Counting)。
-
标记清除(Mark-and-Sweep)的工作原理是:垃圾回收器会定期扫描内存中的对象,从根对象开始遍历内存中的所有对象,对于可达对象,通过标记它们来标识它们是可达对象;对于未被标记的对象,就说明它们是不可达对象,需要被清除。该算法的优点是可以处理循环引用的情况,但在执行时间上可能会比较长,影响程序的性能。
- 引用计数(Reference Counting)的工作原理是:垃圾回收器会记录每个对象被引用的次数,当对象被引用的次数为0时,就将该对象清除。该算法的优点是实现较为简单,但无法处理循环引用的情况,可能会导致内存泄漏。
浏览器
1.浏览器兼容性问题
不同浏览器的内核不尽相同,所以各个浏览器对网页的解析存在一定的差异。浏览器内核主要分为两种,一是渲染引擎,另一个是js 引擎。所以浏览器兼容性问题一般指:css兼容、js兼容。
CSS兼容:
(1). 不同浏览器的标签默认的margin和padding不同
利用通配符选择器设置 *{margin:0;padding:0;}
(2). css3新属性,加浏览器前缀兼容早期浏览器
浏览器前缀
-moz-
/* 火狐浏览器 /-webkit-
/ Safari, 谷歌浏览器等使用Webkit引擎的浏览器 /-o-
/ Opera浏览器(早期) /-ms-
/ IE */哪些css3属性需要加:
- 定义关键帧动画 @keyframes
- css3中的变形(transform)、过渡(transtion)、动画(animation)
- border-radius 圆角
- box-shadow 盒子阴影
- flex 弹性布局
(3). css hack解决浏览器兼容性(csshack本身就是处理浏览器兼容的)
background-color:yellow0; 0 是留给ie8的
+background-color:pink; + ie7定了;
_background-color:orange; _专门留给神奇的ie6
JS兼容
(1). 事件绑定
IE:
dom.attachEvent()
;
标准浏览器:dom.addEventListener()
;(2). event事件对象问题
谷歌火狐IE9以上获取事件对象 event
谷歌和IE获取事件对象 window.enent
document.οnclick=function(event){var e= event || window.event
}; //兼容写法;
(3). event.target事件源对象问题
event
对象有srcElement
属性,但是没有target
属性;// IEevent
对象有target
属性,但是没有srcElement
属性。// 火狐srcObj = event.srcElement || event.target; //兼容写法;
(4). 阻止事件默认行为
e.stopPropagation();//W3C标准
e.cancelBubble=true;//IE....
(5). 阻止事件传播
e.preventDefault();//W3C标准
e.returnValue='false';//IE..
2.什么是同源策略
同源策略是浏览器向服务端发送请求时的一种安全策略,它规定了浏览器只能请求域名、端口号、协议相同的数据源,非同源的请求会被浏览器拦截,造成跨域问题。
3.什么是跨域请求,怎样解决跨域
当浏览器请求域名、端口号、协议不同的数据源时,会被浏览器拦截,此时就会报跨域。
- JSONP: 通过动态创建<script>标签来绕过跨域限制,适用于GET请求。但JSONP安全性较低,不推荐在敏感数据传输中使用。
- 代理服务器:将请求发送给代理服务器,代理服务器发送给目标服务器。
- CORS代理: 服务器会在响应头中添加合适的CORS头,使请求看起来像是来自同一个源。
- 使用第三方库: 比如Axios等支持CORS的库,可以帮助处理跨域请求的细节。
- PostMessage API: 用于不同源之间的通信,适用于iframe或窗口之间的跨域消息传递。
4.axios发送请求无法携带cookie是什么原因,如何解决?
同源策略。axios发送非同源的请求会出现跨域问题,cookie不能跨域,会被浏览器隐藏掉。
5.axios常用的api有哪些?
axios可以发送各种类型的HTTP请求,包括GET、POST、PUT、DELETE等。我们可以使用以下方法来发送请求。
- axios.git(url,config) 一般用来查询。
- axios.post(url,data,config) 一般用来添加数据。
- axios.put(url,data,config) 一般用来修改数据。
- axios.delete(url,config) 一般用来删除数据。
- axios.interceptors.request.use(responseFun,errorFun) 请求拦截器
- axios.interceptors.response.use(responseFun,errorFun) 响应拦截器
6.谈一下你对token验证机制的理解
- Token验证机制是一种身份验证机制,用于保护Web应用程序和API免受未经授权的访问和攻击。
- 在这种机制中,用户登录成功后,服务器会生成一个令牌(Token),并将其返回给用户。该令牌包含一些身份验证信息,如用户ID、访问权限等。用户在后续的请求中将该令牌发送给服务器,以证明他们的身份。服务器可以通过验证该令牌的有效性来确定用户是否有权访问请求的资源。
- 在这种机制中,用户登录成功后,服务器会生成一个令牌(Token),并将其返回给用户。该令牌包含一些身份验证信息,如用户ID、访问权限等。用户在后续的请求中将该令牌发送给服务器,以证明他们的身份。服务器可以通过验证该令牌的有效性来确定用户是否有权访问请求的资源。
7.怎样解决token过期
- 刷新令牌(Refresh Token)在用户登录时,除了发放一个访问令牌(Access Token)以外,再发放一个刷新令牌(Refrsh Token)。访问令牌的有效期比较短,刷新令牌的有效期比较长。当访问令牌过期时,使用刷新令牌向服务器请求新的访问令牌。如果刷新令牌也过期,则跳转回登录界面。 这种方式的优点是可以避免用户频繁登录,但需要妥善保管刷新令牌,因为它的安全性比访问令牌更高。
- 滑动窗口:用户每次使用使用访问令牌时,服务器都会更新访问令牌的过期时间。这种方式的优点是用户只要频繁访问,就不需要登录,但可能会增加服务器负担。
- 重新登录:当访问令牌过期时,跳转回登录界面,让用户重新登录。这是最简单的一种方式,但可能会影响用户体验。
8.localStorage
- 作用。处理长期存储的业务逻辑数据。
- 获取方式:window.localStorage
- 过期时间。除非手动删除,否则永远不会消失。
- 储存:容量5MB。只能存储字符串类型。
- 属性。Key、Value
- 跨域访问。window.postMessage
9.sessionStorage
- 作用。处理当前账号的业务逻辑数据。
- 获取方式:window.sessionStorage
- 过期时间。浏览器关闭时自动删除。
- 储存:容量5MB。只能存储字符串类型。
- 属性。Key、Value
10.cookie
- 作用。cookie是服务器保存在浏览器上的一段文本信息,浏览器进行在本地进行存储并且在请求中携带上cookie。服务器可以根据cookie辨别用户的身份,也可以判断两个请求是否来自于同一个浏览器。相应的服务器也可以修改cookie。
- 过期时间。cookie一般会在浏览器关闭的时候删除,也可以通过expires来设置过期时间。
- 储存:一个cookie不能超过4KB,一个站点最多存放20个cookie。只能存储字符串类型。
- 属性。
- Name:cookie的名字,一个域名下绑定的cookie的name不能相同。如果是相同的name则会被覆盖。
- value:每个cookie拥有的属性,表示cookie的值。由于cookie规定是名称/值是不允许包含分号,逗号,空格的,所以为了不给用户到来麻烦,考虑服务器的兼容性,任何存储cookie的数据都应该被URL编码。
- domain:cookie的域名。cookie绑定的域名,如果没有设置,自动绑定当前执行语句的的域。同一个域名下的二级域名也是不可以交换cookie的。
- path:path是默认属性’/',匹配web路由
- 跨域。跨域请求默认不使用凭证,如果在跨域请求中设置了cookie,axios会把cookie隐藏。
11.session
session 是基于 cookie 的。在用户第一次使用 session 的时候(访问 jsp 页面会获取 session,所以一般访问 index.jsp 就算是第一次使用 session 了),服务器会为用户创建一个 session 域对象。使用 jsessionid 和这个对象关联,这个对象在整个用户会话期间使用。响应体增加 set-cookie:jsessionid=xxx 的项。用户下次以后的请求都会携带 jsessionid 这个参数,我们使用 request.getSession()的时候,就会使用 jsessionid 取出 session 对象。
12.cookie和session的区别
- 存储位置。Session数据保存在服务器端;Cookie数据保存在客户端浏览器。
- 存取方式。Session能保存任意数据类型;Cookie只能保存ASCII字符。
- 有效期。Session中的数据在会话结束或浏览器关闭时失效;Cookie中的数据可以长时间存在,除非设置过期时间。
- 数据安全性。由于Cookie数据保存在客户端,其安全性相对较差,容易受到篡改或被第三方分析利用;Session数据保存在服务器端,安全性相对较高。
- 存储大小。Cookie一般保存的数据大小不会超过4KB;Session理论上没有存储大小限制。
- 使用场景。由于Cookie安全性较低,不适合存储敏感信息,如登录凭证;Session适合存储敏感信息,如用户会话状态。
13.localStorage和sessionStorage和cookie的区别
cookie、localStorage、sessionStorage是本地存储的方式有三种。
- 功能:localStorage存储长期数据;sessionStorage储存账号数据;cookie识别用户身份信息。
- 过期时间:localStorage永久存储、除非手动删除;sessionStorage浏览器关闭时自动删除;cookie浏览器关闭时自动删除,也可以设置过期时间。
- 内存大小:localStorage有5MB、sessionStorage有5MB、cookie4KB且最多存储20个cookie
- 属性:localStorage有Key和Value;sessionStorage有Key和Value;cookie有Name和Value和domain和path。
- 跨域:localStorage可以跨域存储和访问;sessionStorage不能跨域;cookie本身不支持跨域访问但是同一应用服务器内的不同应用或者二级域名间可以共享Cookie
14.webWorker
WebWorker是h5中新增的WebAPI,用于启动一个独立的线程,主线程和分线程可以通过相互发送消息进行通信。当前端页面中有耗时很长的代码需要执行时,可以放在worker中执行,否则会卡塞主线程,影响用户体验。
- 复杂计算。当涉及到大量计算或复杂的数据处理时,可以将这些操作放在 Web Worker 中进行,避免阻塞主线程。例如,图像处理、音频处理、视频处理等。
- 后台下载。当需要下载大量数据时,可以使用 Web Worker 在后台进行数据的下载和处理,以避免阻塞用户界面。例如,从服务器获取大量数据并进行处理之后再展示给用户。
- 数据处理。当需要对大量数据进行处理或者排序时,可以使用 Web Worker 将处理逻辑放在后台线程中进行,提高处理的效率。
- 实时通信。可以使用 Web Worker 来处理实时通信的逻辑。在主线程可以与 Web Worker 进行通信,从而实现实时的数据交换。
使用流程:
- 主线程:
- 创建Web Worker实例:myWorker = new Worker(js脚本文件名,配置对象)
- 接收worker线程处理结果:myWorker.onmessage('数据')
- 启动worker线程并向worker发送数据:myWorker.postMessage('数据')
- worker线程:
- 接收主线程发送的任务:self.onmessage()
- 将任务处理结果发送给主线程:self.postMessage()
15.webSocket
webSocket是一种双工通信技术(俗称长链接),可以实现服务器主动向客户端发送数据。适用于需要实时通信的网站, 比如人工客服服务、弹幕、大型文件下载时的进度条。webSocket有四种状态:open 开启服务,close关闭服务,message接收信息,error发生错误。
webSocket使用流程:
- 建立连接:const socket = new WebSocket("ws://example.com/socketserver");
- 发送信息:socket.send(JSON.stringify(oj));
- 接收消息:socket.onmessage = function (event) {}
- 关闭链接:socket.close()
- 连接建立时触发:socket.
onopen
(event) {} - 发生错误时触发:socket.
onerror
(event) {} - 连接关闭时触发:socket.
onclose
(event) {}
webSocket优势:
- 实时性:与传统的HTTP请求/响应模式不同,WebSocket允许实时双向通信,使得服务器能够主动向客户端推送数据,而不需要客户端发起请求。
- 更少的网络流量:WebSockets使用更少的网络流量,因为在连接建立后,客户端和服务器之间的通信只需要很少的开销。
- 更少的延迟:由于WebSocket允许实时通信,因此它可以大大减少通信的延迟时间。
- 更少的服务器压力:WebSocket连接保持打开状态,因此服务器不需要为每个请求创建一个新的连接。这可以减轻服务器的负载并提高性能。
- 更好的跨域支持:WebSocket具有更好的跨域支持,因为它使用标准的HTTP握手来建立连接,可以使用与HTTP相同的跨域策略。
- 可扩展性:WebSocket可以轻松扩展以支持大量并发连接,因为它使用单个TCP连接来处理多个并发请求。
- 省电:WebSocket可以通过减少网络流量和延迟来减少移动设备的能耗,因此它在移动设备上的使用非常适合。
16.如何优化网页打开速度
- 尽量减少页面资源的请求次数(可以通过base64图片、合并图片、合并js,css文件实现)。
- 对页面代码进行压缩(主要是js代码压缩)
- 合理地使用lazy()懒加载。
- 对比较重的任务使用web workor独立线程
17.HTTP、HTTPS、DNS、TCP协议
- DNS:域名系统协议。计算机无法识别域名,只能识别IP地址。域名系统就是一个将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。域名系统分为四层:本地域名服务器<根域名服务器<顶级域名服务器<权限域名服务器,从本地域名服务器开始依次查找对应的IP地址。
- TCP:传输控制协议。是一种面向连接的、可靠的、基于字节流的传输层通信协议,提供可靠的通信服务。浏览器建立TCP链接之后,才可以向服务器发送http请求,每次进行通信之后就会断开链接。
-
TCP三次握手
1) 客户端发送带有SYN标志为1、起始序seq号为随机数x的数据包到服务端 ,客户端进入SYN-SENT状态
2)服务端发送带有SYN/ACK标志的数据包,其确认字段值ack=x+1,seq=y 包到客户端;服务端进入SYN-RECEIVED状态;客户端接收服务端的ACK包、进入ESTABLISH状态(连接建立完成)
3)客户端发送带有ACK标志的数据包给服务端、其中ack=y+1;服务端进入ESTABLISHED状态(连接建立完成状态) -
TCP四次挥手
1)客户端发送一个FIN,用来关闭客户端到服务器端的数据传送;
2)服务器收到FIN,它发回一个ACK,确认序号为收到的序号+1;和SYN一样,一个FIN将占用一个序号;
3)服务器关闭与客户端的连接、发送一个FIN给客户端;
4)客户端受到FIN,发送一个ACK包给服务端,并将序号设为收到序号+1;客户端关闭与服务端的连接。
-
- HTTP:超文本传输协议。是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。
- http只能进行单向通信。单向通信指的是请求只能由客户端发起,服务端只能对请求做出应答,服务端不能主动向客户端发送数据
- http是无状态。无状态指的是在处理事务时没有记忆的能力,也就是说,我们向服务器发送http请求之后,会给我们发送数据过来,但是不会记录任何数据,后续想要使用这个数据,必须重新发送请求。
- http是一个短链接。短链接指的是浏览器和服务器每进行一次 HTTP 操作,就需要建立一次连接,任务结束就中断连接。
- HTTPS:超文本传输安全协议。简单的说就是在http和tcp中间添加了一层安全协议ssl,就称为HTTPS超文本传输安全协议。因为在发送网络请求后,会有很多的服务器路由器的转发。传输过程中可能会被篡改信息,而如果使用HTTPS协议,在传输过程中被ssl或tsl安全协议保护,密钥在终端和终点站才可以访问,增加了通信的安全性。
18.http和https的区别
- 关系:HTTP协议通常承载于TCP协议之上,在HTTP和TCP之间添加一个安全协议层(SSL或TSL),这个时候,就成了我们常说的HTTPS。
- 端口号不同:HTTP的默认端口号为80,HTTPS的默认端口号为443。HTTP安全性较低,HTTPS安全性较高。
- 安全性不同:网络请求需要中间有很多的服务器路由器的转发。传输过程中可能会被篡改信息,而如果使用HTTPS协议,在传输过程中被ssl或tsl安全协议保护,密钥在终端和终点站才可以访问,所以https更加安全。
19.浏览器输入一个地址,敲下回车键
- 首先浏览器会根据域名,在DNS各级域名服务器(本地域名服务器>根域名服务器>顶级域名服务器>权限域名服务器)中查找对应的IP地址。
- 根据IP地址与服务器建立TCP链接(三次握手)
- 发送http请求。按照HTTP协议的规定,生成请求报文,浏览器将请求报文发送给服务器
- 解析html代码,生成dom树渲染页面。解析css,生成网页内容样式美化网页。解析javascript代码,生成元素动态交互。
- 断开链接(四次挥手)
20.什么是mvc和mvvm
- MVC:model数据-view视图-controller控制器。通过controller控制器协调model和view的交互
- MVVM:model数据-view视图-view视图-model数据,使用数据双向绑定,model和view直接进行交互。
- M(model):对应组件的方法或生命周期函数中实现的业务逻辑和this.state中保存的本地数据
- V(View):对应框架在浏览器中基于虚拟DOM生成的真实DOM以及我们书写的CSS
- V(view)-M(model):对应组件中的JSX,它实质上是Virtual DOM的语法。
21.XSS攻击、CSRF攻击
- Xss(cross-site scripting 跨站脚本攻击) 指的是攻击者往页面中里插入恶意 html标签或者javascript代码。比如:攻击者在论坛中放一个看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
- 防范:首先代码里对用户输入的地方和变量都需要仔细检查长度和对<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。避免直接在cookie 中泄露用户隐私,例如email、密码等等。其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie。
- Cross—Site Request Forgery,跨域请求伪造。CSRF攻击攻击原理及过程如下:1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
- 防范:在服务端敏感接口中添加referer判断。
react
1.react的优缺点
- 优点
- 虚拟DOM:减少对真实DOM的操作,提高性能。
- 组件化:将代码分成一个个小的、可复用的组件,利于管理、维护。
- 使用JSX:在React中可以嵌入HTML和JavaScript。
- 单向数据流:React的单向数据流使得应用的状态更改时,可以更容易地追踪和调试。
- 缺点
- react主要关注UI构建,并不算是一个完整的框架,基本都需要加上reactrouter和flux/redux才能写大型应用。
- 较高的入门曲线,需要理解组件、状态、属性、状态管理等概念。
2.vue和react有什么区别
- 数据流不同。Vue支持双向数据绑定,而React提倡单向数据流,即数据从父组件流向子组件,子组件不能直接修改父组件的数据。
- 性能优化不同。Vue通过其响应式系统和虚拟DOM自动优化性能,而React则需要开发者手动优化性能,例如通过使用
shouldComponentUpdate
或PureComponent
。 - 状态管理不同。Vue通常使用Vuex进行状态管理,而React则更多使用Redux或Context API进行状态管理。
- 设计理念和架构不同。Vue最初是基于MVVM模式设计的,而React则更多地基于函数式编程和组件化思想;Vue提供了指令系统,使得模板更加易于编写和理解,而React则推荐使用JSX,它允许在JavaScript中编写类似HTML的模板语法。
3.单向数据流
单向数据流是一种数据流动模式,数据只能从父组件流向子组件,子组件不能直接修改父组件的数据。这种模式有助于简化状态的管理和维护,降低了组件之间的耦合度,提高代码的可维护性和可预测性。
4.diff算法
diff
算法是一种用于比较虚拟 DOM 树的两个版本之间差异的算法。这使得 React 可以高效地更新真实 DOM,只应用必要的更改,而不是重新渲染整个组件树。diff的三种对比方式:
- Element Diff: 标签对比,对比标签名、标签属性、标签内容。
- Component Diff: 组件对比,比较同一层级的相同组件类型间的差异。
- Tree Diff: DOM树对比,逐层对比。
5.什么是虚拟dom
- 虚拟DOM(Virtual DOM)是一种抽象层,它使用普通的JavaScript对象(JS对象)来描述DOM(文档对象模型)结构。
- 虚拟DOM对象通常包含标签名、属性和标签内容等信息,这些信息与真实的DOM树结构相对应,但虚拟DOM不是真实的DOM节点,因此被称为“虚拟”。
- 虚拟DOM的实现基于一种设计思想,即通过比较新旧虚拟DOM树的差异,从而最小化对真实DOM进行的操作,以提高性能和效率。
5.react渲染dom的流程
- 首先使用JSX创建react元素结构,这个元素结构就是后期生成虚拟dom的模版;
- babel会把这些元素结构编译成react.createElement()语法;
- React.createElement()会将这些元素转化成虚拟dom;
- 最终render函数会将这些虚拟dom渲染成真实dom,插入文档中。
- 如果后续数据发生变化,React就会生成新的虚拟dom,通过diff算法同层对比新旧dom,重新渲染发生变化的dom。
- 调度器(Scheduer):调度任务,为任务排序优先级,让优先级高的任务先进入到 Reconciler
- 协调器(Reconciler):生成 Fiber 对象,收集副作用,找出哪些节点发生了变化,打上不同的 flags,diff 算法也是在这个组件中执行的。类组件的render和函数组件也是在这里被调用的
- 渲染器(Renderer):根据协调器计算出来的虚拟 DOM 同步的渲染节点到视图上。
6.react中key的作用
- key是列表通过map循环时给循环标签添加的属性,用于标记每一个循环元素。
- 在列表数据更新时,react可以通过key可以判断元素是新创建的还是被移动的元素,从而减少不必要的元素渲染。
- 如果使用index作为key值,当我们改变列表元素的顺序时,就会导致该元素后面的所有元素的key发生变化,元素也会重新渲染,
7.什么是受控组件和非受控组件
- 受控组件:指的是受react状态控制的表单控件,通过setState来驱动数据变化,需要绑定onChange和value属性。
- 非受控组件:指的是不受react状态控制的组件,表单数据由dom本身处理。非受控组件可以通过ref访问dom获得表单的值。
8.有状态组件和无状态组件
- 有状态组件:也被称为类组件,通过class类定义,拥有自己的状态数据,生命周期函数,适用于处理各种业务逻辑。
- 无状态组件:也被称为函数组件,没有自己的状态数据,生命周期函数,渲染结果由props决定,一般仅用于展示数据。
9.组件之间的通讯方式有哪些
父子传值\子父传值\Context上下文传值\redux\路由传值
10.路由跳转时如何传递数据
- 通过url的?传值,可以通过useSearchParams.get()或者location.query获取
- 通过url的/传值,可以通过useParams()获取
- 通过query对象传值,可以通过useLocation.query获取
- 通过state传值,可以通过useLocation.state获取
11.什么是高阶组件(HOC)
- 高阶组件是一个函数,它接收一个组件作为参数,并返回一个新的组件,这个新的组件可以对原组件进行扩展或增强,例如添加一些通用的逻辑、状态、行为等。
- 高阶组件的作用:
- 代码复用:通过高阶组件可以将共享逻辑抽离出来,避免重复代码。
- 渲染劫持:高阶组件可以修改传入组件的React元素树。
- 状态抽象和操作:高阶组件可以管理状态,并将其通过props传递给被包装的组件。
- Props操作:可以用来添加、修改或删除传递给被包装组件的props。
- 高阶组件的实现方式:
- 属性代理:通过props将新的属性传递给被包裹的组件。
- 反向继承:通过继承被包裹的组件,可以覆盖或增加它的生命周期方法和渲染函数。接收一个组件作为参数,返回一个新的组件并继承这个组件,在新的组件中可以覆盖或增加这个组件的生命周期方法。
- <PropsChildComponent />以标签形式嵌入组件。外部组件可以调用被继承组件的方法。但不能将被继承的 state 和 钩子覆盖掉。
- super.render()嵌入组件。外部组件的 state 可以将被继承组件的 state 和 钩子函数彻底覆盖掉。同时,外部组件也可以调用被继承组件的方法。
12.什么是渲染劫持
一般通过高阶组件(HOC)来实现,高阶组件可以在组件的render函数中进行操作,从而控制原组件的渲染输出。渲染劫持的应用范围广泛,包括但不限于性能优化、状态管理、错误处理等。
13.react严格模式
- React的严格模式是一种用于识别和报告在开发过程中可能会引起问题的部分的模式。它不会在生产环境中运行,只在开发环境中进行检查。
- 开启严格模式:将<React.StrictMode>组件放在你的应用最外层的某个地方即可。
- 严格模式有哪些限制:
- 检测不安全的生命周期(如
componentWillMount
,componentWillReceiveProps
,componentWillUpdate
)使用。 - 识别不稳定的使用模式,如未使用的DOM节点、未使用的Refs。
- 识别可能导致性能问题的长时间渲染延迟。
- 检测不安全的生命周期(如
14.什么是jsx?jsx的特点
- JSX(JavaScript XML)是React中一种用于构建用户界面的语法扩展,它允许我们在JavaScript代码中编写类似HTML结构的代码。
- JSX的特点:
- 声明性:JSX提供了一种声明性的方式来描述用户界面的结构和外观,类似于编写HTML模板,使代码更易读、理解和维护。
- JavaScript表达式:在JSX中,可以直接嵌入JavaScript表达式,用花括号{}包裹。这样可以在标记内部进行动态计算和引用变量。
- 组件支持:JSX允许我们以标签形式使用自定义组件,将代码组织成可复用的组件,实现模块化开发。
- 属性传递:JSX中的标签可以带有属性,用于向组件传递数据或配置选项。
- 样式类名:为了与JavaScript语法保持一致,JSX中使用className来定义元素的样式类,而非class属性。
- 注释:JSX中可以使用JavaScript风格的注释,将注释内容用花括号包裹起来。
- 防止XSS攻击:JSX在渲染时会自动进行转义,避免了常见的跨站脚本攻击(XSS)安全漏洞。
- JSX注意事项:
- JSX必须有一个根节点,如果没有根节点,可以使用<></>或者<Fragment>替代所有标签必须形成闭合,成对闭合或者自闭合都可以
- JSX中的语法更加贴近JS语法,属性名采用驼峰命名法 class -> className click -> onClick
- JSX支持多行(换行),如果需要换行,需使用() 包裹,防止bug出现
15.组件懒加载
- 什么是懒加载:懒加载通常指的是代码层面的懒加载,即在需要时才加载 JavaScript 模块,而不是在页面初始加载时一起加载所有模块。类似于js中的异步任务。之所以需要懒加载,是因为在首屏同时加载过多的内容,会导致卡顿不流畅响应速度慢、用户等待时间过长等问题,对此可以使用懒加载机制来进行优化。
- 懒加载的优点:
- 减少初始加载时间: 将不必要的组件延迟加载可以减少初始页面加载时间,提高用户体验。
- 优化页面性能: 减少初始加载的资源量可以降低页面的网络请求和内存占用,优化页面性能。
- 提高用户体验: 使用懒加载可以更快地渲染页面内容,并减少用户等待时间,从而提高用户体验。
- 懒加载的使用
- const OtherComponent = lazy(() => import('./OtherComponent'));
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense> lazy
函数接收一个import()
表达式,并返回一个 React Element。Suspense
组件允许你指定在组件渲染之前显示的后备内容(fallback)。./OtherComponent
是将被懒加载的模块的路径。
- const OtherComponent = lazy(() => import('./OtherComponent'));
16.<></>和<Fragment>的作用
- 相同点。空白标签和Fragment标签既有标签容器的作用又不会生成额外的标签元素。
- 精简DOM结构:可以避免添加多余的DOM节点,从而减小页面的结构复杂度,提高性能。
- 优化渲染性能:不会创建额外的DOM节点,它可以减少Virtual DOM的比较和更新操作,从而提高组件的渲染性能。
- 更清晰的代码结构:我们可以更清晰地组织组件结构,将相关的子元素包裹在一起,使代码更易读、维护和理解。
- <></>和Fragment的区别。Fragment标签支持能接受键值或属性,可以遍历循环渲染元素。<></>不能使用标签属性。
17.react16版本新增了那些内容(Fiber渲染机制)
- Fiber 架构:引入了新的渲染机制。传统上,React 使用一种称为堆栈调和递归算法来处理虚拟 DOM 的更新,这种方法在大型应用或者频繁更新的情况下可能会产生性能问题。React Fiber 则是基于一种增量渲染的思想,它将更新任务分解成小的、可中断的单元,使得 React 在更新时可以更灵活地控制和切换任务,提高应用的响应性。
- 增量渲染: React Fiber 将更新任务拆分成多个小任务单元(称为 “fiber”),并使用优先级调度器来处理这些任务,以提高响应性和用户体验。
- 优先级调度: Fiber 引入了优先级概念,使 React 能够根据任务的优先级来决定任务的执行顺序,确保高优先级任务得到及时处理。
- 中断与恢复: React Fiber 允许在渲染过程中中断任务,然后在适当的时机恢复执行,从而避免了阻塞的情况。
- 任务取消: Fiber 具备任务取消的能力(shouldComponentUpdate或者React.PureComponent),可以取消不必要的更新,提高性能。
- 引入了Portals(传送门),允许子组件渲染到父组件的 DOM 层级外的节点。
- 创建元素:在构造其中创建一个元素。this.el = document.createElement('div');
- 添加元素:document.body.appendChild(this.el);
- 移除元素:document.body.removeChild(this.el);
- 开启传送门:在render函数中return通过ReactDOM.createPortal创建的传送门元素。
- 引入了错误边界(Error Boundaries)的概念:允许组件捕获并显示子组件树中的JavaScript错误,而不会使整个应用崩溃。可以通过componentDidCatch函数来实现,它接收两个参数:error和info。error参数表示抛出的错误,info参数包含有关组件引发错误的组件堆栈的信息。
- 生命周期函数更改:
componentWillMount
和componentWillReceiveProps
被标记为不推荐使用,分别替换为UNSAFE_componentWillMount
和UNSAFE_componentWillReceiveProps
。 - 支持自定义 DOM 属性的渲染,例如
data-
属性。 - 服务端渲染(Server-Side Rendering,SSR)得到改善,支持streaming和同步渲染。
- 引入了自动批量更新(Batching),减少了DOM更新次数,提高了性能。
17.react16.8新增了哪些内容(hooks)
- Hooks。Hooks允许函数组件使用类组件的特性,如状态(state)和生命周期方法。有几种主要的Hooks:
- useState。用于在函数组件中添加和管理状态。
- useEffect。用于处理函数组件中的副作用,如数据获取和订阅。
- useContext。用于在函数组件中共享上下文。
- useReducer。用于简化状态管理。
- useMemo。用于缓存计算结果,提高性能。
- useCallback。用于缓存函数,同样用于提高性能。
- useRef。用于获取对子组件的引用。
- useDebugValue。用于在开发者工具中显示自定义的Hook调试信息。
- useTransition。用于在函数组件中实现异步状态更新。
- 自定义Hook。从React 16.8开始,可以创建自定义Hook,这些Hook可以捕获额外的逻辑并在多个组件之间重用。
- 性能改进。Hooks的出现减少了不必要的生命周期方法的使用,使得代码更加简洁,同时提高了虚拟DOM的diff算法效率,从而提高了页面渲染速度。
- 更好的开发者体验。React 16.8版本提供了更好的开发者工具支持,包括对Hooks的调试和监视。
19.react17新增了那些内容(多版本共存和渐进式升级)
- 无新特性:React 17并没有为开发人员添加任何新的功能,它的主要焦点是改进React本身的升级过程。React 17被视为一个“垫脚石”版本,它为逐步升级提供了支持,使得将不同版本的React管理的树嵌入到另一个版本的React管理的树中变得更加安全。
- 渐进式升级(多版本并存与微前端架构):React17支持多版本并存、渐进地完成版本升级。v17 版本可以使得由一个React版本管理的代码树嵌入到另一个React版本管理的代码树中,这是该版本提供的渐进升级的能力。在以往的版本中,React一直遵循“all-or-nothing”的升级策略,开发者可以继续使用旧版本,也可以将整个应用升级到新版本,没有介于两者之间的情况。这种情况往往会导致两个主要问题:1. 想要让业务代码自动化升级过渡,React团队需要无限期支持过时的API;2. 如果react有不兼容版本更新,业务开发者需要在继续使用旧版本和更新React之间做取舍,因为往往这种面临较大的风险。
- 其他细节调整(参考网址)
- 事件委托不再挂到 documen 上,而会将事件处理器附加到渲染 React 树的根容器root DOM 中。
- 更加靠近浏览器原生事件。
- DOM事件复用池被废弃。
- Effect Hook 清理操作改为异步执行。
- render返回undefined报错
- 报错信息透传组件“调用栈”
- 删除部分暴露出来的私有 API
20.react18新增了哪些内容(并发)
- 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。通俗点说:具备处理多个任务的能力,但不是在同一时刻处理,而是交替处理多个任务。举个栗子:在并发的情况下,
react更新
到一半的时候,进来了click
任务,这个时候先去执行click
任务。等click
任务执行完成后,接着继续执行剩余的react更新
。这样就保证了即使在耗时更新的情况下,用户依旧是可以进行交互的(interactive
)。 - 自动批处理:批处理是指,当 React 在一个单独的重渲染事件中批量处理多个状态更新以此实现优化性能。如果没有自动批处理的话,我们仅能够在 React 事件处理程序中批量更新。在 React 18 之前,默认情况下
promise
、setTimeout
、原生应用的事件处理程序以及任何其他事件中的更新都不会被批量处理;但现在,这些更新内容都会被自动批处理 - 过渡更新(非紧急更新):过渡(transition)更新是 React 中一个新的概念,用于区分紧急和非紧急的更新。紧急更新:对应直接的交互,交互之后立即可以看到效果的更新。如输入,点击等。过渡更新 不需要立即看到效果,关注的是最后的结果。我们可以通过以下方法开启过度更新:
useTransition
: 一个用于开启过渡更新的 Hook,用于跟踪待定转场状态。startTransition
: 当 Hook 不能使用时,用于开启过渡的方法。- 被包裹在
startTransition
中的更新会被处理为过渡更新,如果有紧急更新出现,比如点击或者按键,则会中断过渡更新。如果一个过渡更新被用户中断,React 将会抛弃未完成的渲染结果,然后仅渲染最新的内容。
- Suspense:Suspense 允许你声明式地为一部分还没有准备好被展示的组件树指定加载状态。相比于
React.lazy
懒加载拆分代码时指定加载状态,Suspense增加了服务端渲染,并且使用并发渲染特性扩展了其功能。 - 新增了一些客户端和服务端渲染API
- React DOM Client 客户端API
createRoot
:为render
或者unmount
创建根节点的新方法。请用它替代ReactDOM.render
。如果没有它,React 18 中的新功能就无法生效。hydrateRoot
:hydrate 服务端渲染的应用的新方法。使用它来替代ReactDOM.hydrate
与新的 React DOM 服务端 API 一起使用。如果没有它,React 18 中的新功能就无法生效。
- React DOM Server 服务端API
renderToPipeableStream
:用于 Node 环境中的流式渲染。renderToReadableStream
:对新式的非主流运行时环境,比如 Deno 和 Cloudflare workers。
- React DOM Client 客户端API
- 新的严格模式行为:为未来新增功能(允许 React 在保留状态的同时添加和移除 UI。例如,当一个用户标签页切出又切回时,React 应该能够立即将之前的页面内容恢复到它先前的状态)做铺垫。
- 新的hooks
- useId:用于生成在客户端和服务端两侧都独一无二的 id,避免 hydrate 后两侧内容不匹配。它主要用于需要唯一 id 的,具有集成 API 的组件库。
- useTransition:
useTransition
和startTransition
让你能够将一些状态更新标记为过渡更新。 - useDeferredValue:允许推迟渲染树的非紧急更新。可以用来创建延迟值,这个值在渲染过程中是最新的,但在屏幕上更新直到下一次渲染周期开始。useTransition + useDeferredValue = 非紧急渲染
- useSyncExternalStore:允许使用第三方状态管理来支持并发模式,并且能通过对 store 进行强制更新实现数据同步。
- useInsertionEffect:允许 CSS-in-JS 库解决在渲染中注入样式的性能问题。除非你已经建立了一个 CSS-in-JS 库,否则我们不希望你使用它。
21.BrowserRouter和HashRouter的区别:
- 底层原理不同:BrowserRouter利用HTML5的History API(pushState, replaceState和popState)来实现路由管理,这种实现方式不兼容IE9及以下版本12。HashRouter则使用URL的哈希值(即window.location.hash)来管理路由,这种方式兼容性更广,但因为哈希值的变化不会触发页面的重新加载,所以它不适合用于需要服务器端参与的路由。
- URL表现形式不同:BrowserRouter的路径中不包含#符号;HashRouter的路径中包含#符号。
- 刷新后对路由state参数的影响:BrowserRouter在刷新页面时不会丢失路由的state参数,因为state信息被保存在了history对象中。HashRouter在刷新页面时可能会导致路由的state参数丢失,因为哈希值的变化不会触发页面的重新加载,从而无法保存state信息。
- 其他特性:HashRouter提供了额外的配置选项,如basename、getUserConfirmation和hashType,这些选项可以用于定制路由的行为和外观。
1.说一说你对类组件的理解
- 类组件,就是通过使用ES6类的编写形式去编写组件,该类必须继承React.Component类。
- 如果想要访问父组件传递过来的参数,可通过this.props的方式去访问
- 在组件中必须实现render方法,在return中返回React对象。
- 类组件拥有自己的生命周期函数,state状态。
- 类组件一般用于处理具有复杂的交互逻辑的页面或者模块。
2.什么是函数组件
-
函数组件,就是以函数的形式编写的组件。
-
函数组件不能声明自己的私有数据,没有生命周期。
-
函数组件的返回值一般由父组件传递的props决定,主要用于数据展示。
-
在react16.8以后,诞生了hooks概念,自此函数组件可以实现自己的状态数据和生命周期函数,操作起来更加简单方便。
3.state和props的区别
-
State是一种数据结构,在类组件的构造器中声明,state是可读可写的,组件中可以通过setstate()来修改数据。State一般只存放纯数据。
-
Props是一种组件配置,一般是父组件传给子组件的,子组件不能直接修改props。Props也可以存放函数。
4.类组件中的数据声明方式
-
直接声明:当属性不参与页面渲染的时候,只参与逻辑运算,可以将其设置为类属性,直接声明。
-
在构造器的state中声明:当属性需要渲染到页面上的时候,需要在 constructor 构造器中声明组件私有数据。只要有 constructor构造器,就必须要有 super() 调用,也可以不使用constructor,直接声明state数据。
5.修改state数据的方式
-
使用this.setState修改数据,可以触发render函数,进行重新渲染页面。
-
使用this.state…修改属性,这就是普通的class属性的修改,拿到的数据还是最新的数据,只是不会渲染页面了。
6.state是同步的还是异步的
-
在受 React 控制的事件中,setState() 是异步的,无法立刻获得最新数据,比如:onClick、componentDidMount() 等。
-
在不受 React 控制的事件中,setState() 是同步的,可以立刻获得最新数据,比如:定时器、延时器等。
7.this.setState如何获取修改后的最新值
-
在 setState 的第二个参数中获取最新值setState({}, () => {}),React 会在数据更新后,立即执行回调函数。
-
在 setState 的第一个参数中以函数形式获取最新值this.setState((state)=>{}) ,此时回调函数中的参数 state 也是最新数据。
8.类组件中常用的生命周期函数
- componentWillMount() 组件即将挂载 (不建议使用)
- componentDidMount() 组件挂载阶段执行的生命周期函数
- 无参数
- 无返回值
- componentWillReceiveProps() 组件收到新的 props 时调用
static
getDerivedStateFromProps
() 根据props更新stateprops
:组件即将用来渲染的下一个 props。state
:组件即将渲染的下一个 state。- 返回值:返回一个对象来更新 state,或返回
null
不更新任何内容。
- shouldComponentUpdate
(nextProps,nextState,nextContext)
控制组件是否更新nextProps
:组件即将用来渲染的下一个 props。将nextProps
与 this.props 进行比较以确定发生了什么变化。nextState
:组件即将渲染的下一个 state。将nextState
与 this.state 进行比较以确定发生了什么变化。nextContext
:组件将要渲染的下一个 context。将nextContext
与 this.context 进行比较以确定发生了什么变化。仅当你指定了 static contextType(更新的)或 static contextTypes(旧版)时才可用。- 返回值:默认为true,允许组件更新。false禁止组件更新
- componentWillUpdate() 组件即将更新前执行的生命周期函数。
- getSnapshotBeforeUpdate
(prevProps,prevState) 组件更新前执行的生命周期函数
prevProps
:更新之前的 Props。prevProps
将会与 this.props 进行比较来确定发生了什么改变。prevState
:更新之前的 State。prevState
将会与 this.state 进行比较来确定发生了什么改变。- 返回值:任何类型的快照值,或者是
null
。你返回的值将作为第三个参数传递给 componentDidUpdate。
- componentDidUpdate(
prevProps,prevState,
snapshot) 组件更新阶段执行的生命周期函数prevProps
更新之前的 props。prevProps
将会与 this.props 进行比较来确定发生了什么改变。prevState
更新之前的 state。prevState
将会与 this.state 进行比较来确定发生了什么改变。- snapshot 如果你实现了 getSnapshotBeforeUpdate 方法,那么
snapshot
将包含从该方法返回的值。否则它将是undefined
。 - 无返回值。
- componentWillUnmount() 组件卸载阶段执行的生命周期函数
- 无参数
- 无返回值
static getDerivedStateFromError(error)
子组件在渲染过程中抛出错误时调用componentDidCatch(error, info)
渲染过程中抛出错误时调用
9.forceupdata()
- 默认情况下,当组件的state或props改变时,组件将重新渲染。如果你的render()方法依赖于一些其他的数据,你可以告诉React组件需要通过调用forceUpdate()重新渲染。
- 调用forceUpdate()会导致组件跳过shouldComponentUpdate(),直接调用render()。这将触发组件的正常生命周期方法,包括每个子组件的shouldComponentUpdate()方法。
- forceUpdate就是重新render。有些变量不在state上,当时你又想达到这个变量更新的时候,刷新render;或者state里的某个变量层次太深,更新的时候没有自动触发render。这些时候都可以手动调用forceUpdate自动触发render
10.为什么类组件的构造器中要调用super函数
在JavaScript 子类的构造函数中 super 指的是父类(即超类)的构造函数。子类中显式定义了constructor的方法中必须在其最顶层调用super,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。 如果不调用super方法,子类就得不到this对象。所以必须先调用super才可以使用this。
11.react类组件渲染优化的方式
- shouldComponentUpdate:shouldComponentUpdate 周期里面默认返回
true
,通过条件控制返回false
来阻止组件重新render
,如果条件过于复杂不推荐使用这中方式; - PureComponent:值得注意的是PureComponent监听props是否发生变化是浅比较,所以最好传递 简单结构的数据
12.类组件对于引用类型数据如何进行渲染优化
- 转化成字符串进行对比;
- 对比引用类型数据里面的属性
1.说一说你对hooks组件的理解
- hooks:在React中,Hooks其实就是一个函数,这个函数的命名以use开头、函数return一个结果;React Hooks其实就是封装了一些通用的、公共的方法,就是一些通用的工具。
- hooks组件:使用大驼峰语法命名,内部使用了hooks API的组件,被称为hooks组件。
- hooks组件可以通过useState、useReducer来缓存自己的状态数据。
- 可以通过useCallBack和useMemo来缓存自己的方法。
- 可以通过useEffect来模拟生命周期函数。
- 可以通过React.Memo和useMemo来进行渲染优化。
2.hooks组件对比class组件的优势
-
简化组件:hooks组件不需要使用
this
,使得代码更简洁,更容易理解。 -
状态逻辑复用:使用
useState
、useEffect
、useContext
等hooks可以轻松地在多个函数组件之间共享状态逻辑和副作用。 -
无需类:hooks组件不需要定义一个类,代码更加简洁。
-
使用最新的JavaScript特性:可以使用新的JavaScript特性,如hooks中的State Hooks使用了最新的可选链和空值合并操作符。
-
无需绑定this:不需要手动绑定事件处理器的
this
,React会自动为你处理。 -
使用hooks,你可以在组件之间更容易地共享有状态的逻辑,而无需中间层如高阶组件或装饰器。
3.hooks为什么只能在组件最上层使用
- hooks的实现就是基于fiber的。每个组件都会生成一个 FiberNode(节点),组件内使用的 hook 会以链表的形式挂在 FiberNode 的 memoizedState 上面。各个 FiberNode 汇聚起来会变成一颗 Fiber 树,React 每次会以固定的顺序遍历这棵树,这样就把整个页面的 hook 都串联起来了。
- react按照固定的顺序来执行hooks的,在循环、判断、嵌套中使用,就会打乱hooks的 调用的顺序,就会导致 react 无法区分出对应的 hook
4.hooks组件中常用的hooks
useState
缓存状态变量- 语法:const [state, setState] = useState(initialState);
参数
initialState
:你希望 state 初始化的值。它可以是任何类型的值,但对于函数有特殊的行为。在初始渲染后,此参数将被忽略。如果传递函数作为initialState
,则它将被视为 初始化函数。当初始化组件时,React 将调用你的初始化函数,并将其返回值存储为初始状态。
- 返回值
- state:当前的 state。在首次渲染时,它将与你传递的
initialState
相匹配。 - setState(nextState):set 函数,它可以让你将 state 更新为不同的值并触发重新渲染。
nextState
:你想要 state 更新为的值。它可以是任何类型的值,但对于函数有特殊的行为。如果你将函数作为nextState
传递,它将被视为 更新函数。它必须是纯函数,只接受待定的 state 作为其唯一参数,并应返回下一个状态。React 将把你的更新函数放入队列中并重新渲染组件。在下一次渲染期间,React 将通过把队列中所有更新函数应用于先前的状态来计算下一个状态。
- state:当前的 state。在首次渲染时,它将与你传递的
- useReducer 缓存需要逻辑处理的变量
- 语法:const [state, dispatch] = useReducer(reducer, initialArg, init?)
- 参数:
reducer
。用于更新 state 的纯函数。参数为 state 和 action,返回值是更新后的 state。state 与 action 可以是任意合法值。initialArg
。用于初始化 state 的任意值。初始值的计算逻辑取决于接下来的init
参数。- 可选参数
init
。用于计算初始值的函数。如果存在,使用init(initialArg)
的执行结果作为初始值,否则使用initialArg
。
- 返回值:
- 当前的 state。初次渲染时,它是
init(initialArg)
或initialArg
(如果没有init
函数)。 - dispatch 函数。用于更新 state 并触发组件的重新渲染。
- 当前的 state。初次渲染时,它是
- useCallBack 缓存函数
- 语法:const cachedFn = useCallback(fn, dependencies)
- 参数:
-
fn
:想要缓存的函数。此函数可以接受任何参数并且返回任何值。React 将会在初次渲染而非调用时返回该函数。当进行下一次渲染时,如果dependencies
相比于上一次渲染时没有改变,那么 React 将会返回相同的函数。否则,React 将返回在最新一次渲染中传入的函数,并且将其缓存以便之后使用。React 不会调用此函数,而是返回此函数。你可以自己决定何时调用以及是否调用。 -
dependencies
:依赖项。响应式值包括 props、state,和所有在你组件内部直接声明的变量和函数。如果你的代码检查工具 配置了 React,那么它将校验每一个正确指定为依赖的响应式值。依赖列表必须具有确切数量的项,并且必须像[dep1, dep2, dep3]
这样编写。React 使用 Object.is 比较每一个依赖和它的之前的值。
-
-
返回值:
-
在初次渲染时,
useCallback
返回你已经传入的fn
函数。在之后的渲染中, 如果依赖没有改变,useCallback
返回上一次渲染中缓存的fn
函数;否则返回这一次渲染传入的fn
。
-
-
useMemo 缓存计算的结果
-
语法:const cachedValue = useMemo(calculateValue, dependencies)
-
参数
-
calculateValue
:要缓存计算值的函数。它应该是一个没有任何参数的纯函数,并且可以返回任意类型。React 将会在首次渲染时调用该函数;在之后的渲染中,如果dependencies
没有发生变化,React 将直接返回相同值。否则,将会再次调用calculateValue
并返回最新结果,然后缓存该结果以便下次重复使用。 -
dependencies
:依赖项。响应式变量包括 props、state 和所有你直接在组件中定义的变量和函数。如果你在代码检查工具中 配置了 React,它将会确保每一个响应式数据都被正确地定义为依赖项。依赖项数组的长度必须是固定的并且必须写成[dep1, dep2, dep3]
这种形式。React 使用 Object.is 将每个依赖项与其之前的值进行比较。
-
-
返回值:
-
在初次渲染时,
useMemo
返回calculateValue函数的返回值
。在接下来的渲染中,如果依赖项没有发生改变,它将返回上次缓存的值;否则将再次调用calculateValue
,并返回最新结果。
-
-
-
useRef 引用一个不需要渲染的值或者访问标签元素
-
语法:const ref = useRef(initialValue)
-
参数
initialValue
:ref 对象的current
属性的初始值。可以是任意类型的值。这个参数在首次渲染后被忽略。
- 返回值
current
:初始值为传递的initialValue
。之后可以将其设置为其他值。如果将 ref 对象作为一个 JSX 节点的ref
属性传递给 React,React 将为它设置current
属性。在后续的渲染中,useRef
将返回同一个对象。
-
-
useImperativeHandle 向父组件暴露一个属性
-
语法:useImperativeHandle(ref, createHandle, dependencies?)
-
参数:
-
ref
:绑定的ref。 -
createHandle
:该函数无需参数,它返回你想要暴露的 ref 的句柄。该句柄可以包含任何类型。通常,你会返回一个包含你想暴露的方法的对象。 -
可选的
dependencies
:依赖项。反应式的值包含 props、状态和其他所有直接在你组件体内声明的变量和函数。倘若你的代码检查器已 为 React 配置好,它会验证每一个反应式的值是否被正确指定为依赖项。该列表的长度必须是一个常数项,并且必须按照[dep1, dep2, dep3]
的形式罗列各依赖项。React 会使用 Object.is 来比较每一个依赖项与其对应的之前值。如果一次重新渲染导致某些依赖项发生了改变,或你没有提供这个参数列表,你的函数createHandle
将会被重新执行,而新生成的句柄则会被分配给 ref。
-
-
-
useContext 获取组件上下文
-
语法:const value = useContext(SomeContext)
-
参数
SomeContext
:先前用 createContext 创建的 context。context 本身不包含信息,它只代表你可以提供或从组件中读取的信息类型。
- 返回值
useContext
为调用组件返回 context 的值。它被确定为传递给树中调用组件上方最近的SomeContext.Provider
的value
。如果没有这样的 provider,那么返回值将会是为创建该 context 传递给 createContext 的defaultValue
。返回的值始终是最新的。如果 context 发生变化,React 会自动重新渲染读取 context 的组件。
-
- useTransiton 在不阻塞 UI 的情况下更新状态
- 语法:const [isPending, startTransition] = useTransition()
- 参数:无
- 返回值:
- startTransition 函数,你可以使用此方法将状态更新标记为 transition。
isPending
,告诉你是否存在待处理的 transition。
-
useDeferredValue 延迟更新 UI 的某些部分
-
语法:const deferredValue = useDeferredValue(value)
- 参数:
value
:你想延迟的值,可以是任何类型。
- 返回值:
- deferredValue:在组件的初始渲染期间,返回的延迟值将与你提供的值相同。但是在组件更新时,React 将会先尝试使用旧值进行重新渲染(因此它将返回旧值),然后再在后台使用新值进行另一个重新渲染(这时它将返回更新后的值)。
-
5.useState的set函数合并更新
- set函数接收一个变量时:如果连续多次调用,会直接替换状态的值,而不是基于之前的状态进行操作,最终会被最后一次调用覆盖。
- set函数接收一个函数的时候:接受待定的 state 作为参数,并应返回下一个状态。如果连续多次调用,React 会把队列中所有更新函数应用于先前的状态来计算下一个状态。
6.useState和useReducer的区别
- 应用场景不同:useState适用于管理简单的、独立的状态,而useReducer适用于管理复杂的、有多个相关状态的逻辑。
- 使用方式:useState使用简单,直接返回一个状态值和一个更新状态值的函数。使用useState时,需要为每个状态定义一个独立的useState调用。useReducer使用略微复杂,需要定义一个reducer函数和初始状态。reducer函数根据不同的动作类型更新状态。使用useReducer时,可以在一个地方管理多个相关的状态。
- 状态更新:useState的更新函数会直接替换状态的值,而不是基于之前的状态进行操作。这意味着如果状态的更新依赖于先前的状态,需要手动处理。useReducer的更新函数接收一个动作对象,可以根据动作对象中的信息来更新状态。更新函数可以返回新的状态值,也可以返回不变的状态,用于跳过不必要的重新渲染。
- 性能优化:如果状态的更新逻辑非常简单,且没有复杂的依赖关系,使用useState通常更为方便。而如果状态较为复杂,有多个相关的状态需要更新时,使用useReducer可以更好地组织和管理代码。
- 总结:useState适用于管理简单的独立状态,而useReducer适用于管理复杂的多个相关状态的逻辑。使用useState更简单,而useReducer更灵活,并提供了一种可扩展的状态管理方式。选择使用哪个Hook取决于具体的场景需求和个人偏好。
7.useCallBack和useMemo的区别
- useMemo 缓存的结果是回调函数中return回来的值,主要用于缓存计算结果的值,应用场景如需要计算的状态
- useCallback 缓存的结果是函数,主要用于缓存函数,应用场景如需要缓存的函数,因为函数式组件每次任何一个state发生变化,会触发整个组件更新,一些函数是没有必要更新的,此时就应该缓存起来,提高性能,减少对资源的浪费;另外还需要注意的是,useCallback应该和React.memo配套使用,缺了一个都可能导致性能不升反而下降。
8.React.memo
- 作用:m
emo
允许你的组件在 props 没有改变的情况下跳过重新渲染。 - 语法:const MemoizedComponent = memo(SomeComponent, arePropsEqual?)
- 参数:
-
Component
:要进行记忆化的组件。memo
不会修改该组件,而是返回一个新的、记忆化的组件。它接受任何有效的 React 组件,包括函数组件和 forwardRef 组件。 -
可选参数
arePropsEqual
:一个函数,接受两个参数:组件的前一个 props 和新的 props。如果旧的和新的 props 相等,即组件使用新的 props 渲染的输出和表现与旧的 props 完全相同,则它应该返回true
。否则返回false
。通常情况下,你不需要指定此函数。默认情况下,React 将使用 Object.is 比较每个 prop。
-
- 返回值:
memo
返回一个新的 React 组件。它的行为与提供给memo
的组件相同,只是当它的父组件重新渲染时 React 不会总是重新渲染它,除非它的 props 发生了变化。
9.React.Memo怎么对引用数据类型进行优化
- 函数:使用useCallback或者useMemo缓存函数;
- 对象:JSON.stringfy()转化成字符串对比或者打点对比
- 数组:JSON.stringfy()转化成字符串对比
10.自定义hooks
自定义 Hooks 是 React 中一种重用逻辑的方式。它们允许我们将组件逻辑提取到可重用的函数中,以便在多个组件中共享。自定义 Hooks 通常以"use"开头,小驼峰语法;应用场景:管理Table分页器状态。
11.react Router hooks
React-Router是一款用于构建单页面应用(SPA)中处理路由的JavaScript库。在现代的Web应用中,SPA已经成为了一种常见的应用架构模式,它允许在不刷新整个页面的情况下进行交互式的用户体验。
- 常用hooks:
- useSearchParams 获取/修改当前路由的params参数的函数
- 语法:let [searchParams, setSearchParams] = useSearchParams();
- 参数:无
- 返回值:
- searchParams:用于查询参数的对象
- get('id') // 获取参数
- getAll('id') // 获取多个参数,返回一个数组
- has('id') // 判断参数是否存在
- delete('id') // 删除参数
- append('desc',"1111111") // 追加参数
- set('id', 'def') //设置参数
- keys() // 获取所有的参数属性名
- values() // 获取所有的参数属性值
- sort() // 按照升值排序,返回适合在URL中使用的查询字符串
- forEach(function(value, key) { console.log(key,value); });遍历所有参数,对每个参数都执行一次回调函数。
- setSearchParams:更新查询参数的函数
- searchParams:用于查询参数的对象
- useParams 获取当前路由的动态参数
- 语法:const params=useParams()
- 参数:无
- 返回值:当前路由的动态参数
- useLocation 获取当前路由状态
- 语法:const location=useLocation()
- 参数:无
- 返回值:当前路由状态
- useNavigate 重定向/跳转
- 语法:const navigate=useNavigate()
- 参数:无
- 返回值:navigate(url,config) 重定向函数,可以接收路径和配置作为参数。
- useSearchParams 获取/修改当前路由的params参数的函数
- 常用标签:
- <Link>页面跳转标签
- <Router> 容器组件,页面组件的容器
- <Route> 注册页面级组件
- <Switch> 容器组件,渲染与当前 URL 匹配的第一个
<Route>
或<Redirect>
的包裹组件。
- <Switch> 容器组件,渲染与当前 URL 匹配的第一个
- <Redirect>重定向
- React-Route包含两种模式BrowserRouter和HashRouter:
- 底层原理不同:BrowserRouter使用的是H5的History API,不兼容IE9以及以下版本。HashRouter使用的是URL的哈希值
- 表现形式不一样:BrowserRouter的路径中没有#。HashRouter的路径中包含#
- 刷新后对路由state参数的影响:BroserRouter 没有任何影响,因为state保存在history对象中。HashRouter刷新后会导致路由state参数的丢失
- HashRouter可以用于解决一些路径错误相关的问题
13.react Redux hooks
- useSelector:用于从Redux store中读取状态
- 语法:const result: any = useSelector(selector: Function, equalityFn?: Function)
- 参数:
- selector :接收所有的state状态作为参数。返回的结果可以是任何值,而不仅仅是一个对象。selector 的返回值将被作为
useSelector()
hook 的返回值被使用。 - equalityFn:比较函数,接收旧值和新值作为参数,如果比较函数返回true,则
useSelector
认为状态没有发生变化,它将返回缓存的结果。如果返回false,则会重新计算并返回新的状态。
- selector :接收所有的state状态作为参数。返回的结果可以是任何值,而不仅仅是一个对象。selector 的返回值将被作为
- 返回值:selector函数的返回值,一般为某个redux store状态
- useDispatch:用于获取
dispatch
函数。- 语法:const dispatch = useDispatch()
- 参数:无
- 返回值:
dispatch:可以接收一个修改redux srote的方法,并执行修改。
- useStore:直接获取到了 Redux store 的引用
- 语法:const store = useStore();
- 参数:无
- 返回值:
getState() // 不同于
useSelector(),getState()获得当前时刻的 redux state
,之后state 更新
并不会
导致这个方法被再次调用
,也不会
导致重新渲染。
dispatch(action) // 修改redux store状态的函数,等同于
useDispatch()subscribe(listener)
replaceReducer(nextReducer)
14.react useworkor hooks
useWorker是一个库,它使用React Hooks
在简单的配置中使用Web Worker API。它支持在不阻塞UI的情况下执行耗时任务,支持使用Promise
而不是事件监听器。
- const [sortWorker, { status: sortStatus, kill: killSortWorker }] =useWorker(sortNumbers);
- sortWorker:调用worker函数
- sortStatus:worker的执行状态,常量
- killSortWorker:终止worker的函数
1.介绍redux
- Redux是一款热门的前端开发库, 它是javascript的可预测状态容器, 可用于管理react的全局状态。使用Redux开发的应用易于测试,可以在不同环境中运行,并显示相同行为。
- redux有三大要素
- Store 核心对象,负责串联React组件和Redux之间的数据通讯。
- action 定义信号,主要提供发射信号的方法。信号中必须有type属性作为修改数据的钥匙,type一般用常量全大写定义。
- reducer 存放全局状态,用于接收store传递的信号。通过接收type判断更新状态数据。
- redux有三大原则
-
唯一数据源(整个项目的 state 状态数据被储存在一个状态树(对象)中,单一状态树更容易跟踪数据的变化, 检查问题和调试)
-
State 状态是只读的, 想要更改数据必须经过action的派发事件, 通过接收action参数修改。
-
reducer必须是纯函数(一个输入必须对应着唯一的输出, 返回值取决于参数)
-
- 常用方法
-
dispath() 是Redux提供的类组件中修改数据的方法
-
connect() 是Redux提供的类组件中映射数据的方法
-
useSelector:用于hooks组件从Redux store中读取状态。
-
useDispatch:用于hooks组件获取
dispatch
函数
-
2.redux如何处理异步action
Redux 处理异步 action 通常使用中间件,如 redux-thunk
或 redux-saga
// 配置文件
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
// 创建处理异步的 action creator:
function fetchData() {
return function(dispatch) {
axios.get('/api/data')
.then(response => {
dispatch({ type: 'FETCH_SUCCESS', payload: response.data });
})
.catch(error => {
dispatch({ type: 'FETCH_ERROR', payload: error });
});
};
}
// 使用 action creator
store.dispatch(fetchData());
3.redux异步action的作用
Redux中的异步action主要用于处理异步操作,如API请求、数据持久化等。
在Redux中,普通的action创建函数会直接返回一个type属性和可选的其他数据属性的对象。然而,对于异步操作,比如网络请求,我们需要在action创建函数中去触发异步操作,并在异步操作完成后发送一个同样的type的新action。
这样的设计模式使得我们的state可以保持单向的状态变化,这对于理解应用程序的状态和调试非常有帮助。
4.RTK(Redux Tool Kit)
Redux Toolkit(RTK)是redux官方为了简化redux复杂性而封装的工具集。
- 核心API
- configureStore:包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer,添加你提供的任何 Redux 中间件,redux-thunk默认包含,并启用 Redux DevTools Extension。
- createSlice:接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions。
- createAsyncThunk: 接受一个动作类型字符串和一个返回承诺的函数,并生成一个pending/fulfilled/rejected基于该承诺分派动作类型的 thunk;
- createSlice剖析
- (1) createSlice主要包含如下几个参数:
- ◼ name:用户标记slice的名词: 在之后的redux-devtool中会显示对应的名词;
- ◼ initialState:初始化值,第一次初始化时的值;
- ◼ reducers:相当于之前的reducer函数对象类型,并且可以添加很多的函数; 函数类似于redux原来reducer中的一个case语句; 函数的参数:
- ✓ 参数一:state
- ✓ 参数二:调用这个action时,传递的action参数;
- (2) 返回值
- ◼ createSlice返回值是一个对象,包含所有的actions;
- (1) createSlice主要包含如下几个参数:
- configureStore剖析
- 用于创建store对象,常见参数如下:
- reducer,将slice中的reducer可以组成一个对象传入此处;
- middleware:可以使用参数,传入其他的中间件(自行了解);
- devTools:是否配置devTools工具,默认为true;
- 用于创建store对象,常见参数如下:
脚手架相关
1.yarn和npm的区别
- 安装速度:yarn的安装速度比npm快,因为它在安装包时会先从本地缓存中寻找,如果本地没有找到,才会去下载。
- 安装版本:yarn和npm在安装版本上有所不同。yarn安装的包版本可以在
yarn.lock
文件中明确指定,这保证了在不同的开发环境中使用相同的版本;而npm则只会安装最新的版本。 - 包的管理:yarn使用
yarn.lock
文件管理包的版本,这保证了在不同的开发环境中使用相同的版本;而npm则只会安装最新的版本。 - 依赖管理:yarn在管理包依赖关系时比npm更严格,可以管理多个包之间的依赖关系,而npm则不能。
- 性能:yarn在性能上优于npm。yarn使用并行安装,可以加快安装速度;而npm则是串行安装,速度较慢。
- 命令:虽然yarn和npm的命令大体上是相似的,但它们的一些命令略有不同。例如,安装依赖项的命令在npm中是
npm install
,而在yarn中是yarn
。
2.yarn常用指令
- yarn install 安装package.json文件中的所有依赖
- yarn add 添加依赖
- yarn remove 删除依赖
- yarn upgrade 升级依赖不记录在package.json中
- yarn start 运行定义的程序脚本命令
- yarn buid 打包
3.npm常用指令
- npm install 安装package.json文件中的所有依赖
- npm install 包名 添加依赖
- npm uninstall 删除依赖
- npm update 升级依赖不记录在package.json中
- npm run dev运行定义的程序脚本命令
- npm run build 打包
4.Git命令
- `git clone 地址` //克隆仓库
- `git branch 分支名` //创建分支
- `git checkout 分支名` //切换分支
- `git checkout -b 分支名` //创建并切换分支
- `git fetch 远程分支名称` 更新远端分支
- `git merge origin/远程分支名称` 合并到本地分支
- `git pull origin 远程分支名称` 更新远端分支且合并取到本地
- `git add .` 将文件提交到暂存区
- `git commit -m "提交内容" ` 提交更改
- `git push origin 本地分支名` 推送到远程
5.nvm
- nvm install :安装指定版本的Node.js,例如nvm install 18.16.1。
- nvm uninstall :卸载指定版本的Node.js,例如nvm uninstall 18.16.1。
- nvm list:列出所有已经安装的Node.js版本。
- nvm current:显示当前正在使用的Node.js版本。
- nvm use :切换到指定版本的Node.js,例如nvm use 18.16.1。
6.Webpack的核心概念
- Entry:入口,Webpack执行构建的第一步将从Entry开始,可抽象成输入。告诉Webpack要使用哪个模块作为构建项目的起点,默认为./src/index.js
- output:出口,告诉Webpack在哪里输出它打包好的代码以及如何命名,默认为./dist
- Module:模块,在Webpack里一切皆模块,一个模块对应着一个文件。Webpack会从配置的Entry开始递归找出所有依赖的模块。
- Chunk:代码块,一个Chunk由多个模块组合而成,用于代码合并与分割。
- Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
- Plugin:扩展插件,在Webpack构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。
7.webpack打包流程
-
安装与初始化:首先需要安装Webpack及其命令行接口(CLI),通常通过npm或yarn等包管理工具进行安装。接着,在项目根目录下创建一个名为
webpack.config.js
的配置文件,用于配置Webpack的各项参数。 -
配置:在
webpack.config.js
文件中,需要配置Webpack的入口(entry)、输出(output)、加载器(loader)和插件等。入口文件是Webpack开始打包的起点,输出路径和文件名定义了打包后的文件应该放在哪里以及如何命名。加载器(loader)用于处理非JavaScript文件,如CSS、图片等,而插件则用于扩展Webpack的功能。 -
模块编译:Webpack从配置的入口文件开始,递归地调用匹配文件的loader对文件进行处理,并分析模块间的依赖关系。这一步是Webpack打包流程的核心,涉及到将源代码转换为浏览器可以识别的格式。
-
模块生成:在模块编译阶段结束后,Webpack会得到模块之间的相互依赖关系,并生成最终的代码块。
-
输出文件:根据模块间的依赖关系及配置文件,Webpack将处理后的模块输出到指定的output目录下。这一步是Webpack打包流程的最终阶段,输出的文件可以直接由浏览器加载执行。
-
其他配置:Webpack还支持通过插件和加载器进行更高级的配置,如使用
babel-loader
进行JavaScript代码的转换,使用css-loader
和style-loader
处理CSS文件等。此外,还可以通过配置watch
选项来监视文件变化并自动重新打包。 -
启动与监控:为了方便开发过程中的自动打包和监控,可以安装
webpack-dev-server
并配置相应的脚本命令来启动一个实时打包的HTTP服务器。这样,每次修改代码后,服务器会自动重新打包并刷新浏览器,实现快速的开发循环。
8.webpack常见的loader
- css-loader读取合并CSS文件
- style-loader把CSS内容注入到JavaScript里
- sass-loader解析sass文件(安装sass-loader,node-sass)
- postcss-loader自动添加浏览器兼容前缀(postcss.config配置)
- url-loader将文件转换为base64 URI。
9.gulp和webpack的异同
- 核心功能定位:
- Webpack:主要是一个模块打包器,它的核心在于解决现代前端开发中的模块化问题,能够分析项目的依赖关系并生成静态资源。Webpack不仅能处理JS模块,还通过Loader处理其他类型的资源(如CSS、图片等),并通过Plugin机制进行更复杂的构建步骤,比如代码分割、热更新、tree shaking等。
- Gulp:是一种基于流的构建系统,专注于流程的自动化,允许开发者创建自定义的任务流(tasks),对文件进行一系列的操作,如读取、修改、写入等。它擅长于文件的移动、复制、合并以及预处理(如Sass转CSS)等操作。
- 处理方式:
- Webpack:基于入口起点(entry point),通过配置文件声明模块间的依赖关系,自动递归地打包所有引用的模块,输出单一或者分块的bundle。
- Gulp:关注于任务流的概念,通过管道(pipeline)将不同的任务串联起来,逐个处理文件。
- 模块化支持:
- Webpack:内建了对CommonJS、AMD、ES6模块等多种模块格式的支持,并能无缝整合到其构建过程中。
- Gulp:本身不直接支持模块化,但可以通过集成Browserify或Rollup等模块打包工具间接实现。
- 构建复杂度:
- Gulp:对于简单的构建流程,Gulp可能更容易理解和配置,尤其当需要进行大量定制时。
10.tree shaking
作用:删除无用代码。在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。
Webpack 中,Tree-shaking 的实现是先「标记」出模块导出值中哪些没有被用过,二是使用 Terser 删掉这些没被用到的导出语句。标记过程大致可划分为三个步骤:
- Make 阶段,收集模块导出变量并记录到模块依赖关系图 ModuleGraph 变量中
- Seal 阶段,遍历 ModuleGraph 标记模块导出变量有没有被使用
- 生成产物时,若变量没有被其它模块使用则删除对应的导出语句