22.04.06 根据b站博主总结的问题扩展而来
面试一般问题分为3部分:1.八股文 2.项目问题 3.手写代码
【前端】互联网大厂前端面试问什么?掌握这些最最最精髓问题,大厂实习不是梦!
自我介绍
1. HTML
1.2 选择器
- 选择器相关知识;
-
基础选择器
- 标签选择器:标签名
- 类选择器:使用
class
属性来调用 class 类;.class-name1{} - id选择器:只能允许一个标签调用;#id名{}
- 通配符选择器:选取页面中所有标签;*{}
-
复合选择器
- 后代选择器:元素1 元素2 {}
- 子选择器:亲儿子;元素1 >元素2{}
- 并集选择器:用于集体声明;元素1,元素2{}
- 伪类选择器:用于向某些选择器添加特殊的效果
- 链接伪类选择器:a:link/visited/hover/active
- focus伪类选择器:获取焦点的表单元素;input: focus {background-color: yellow;}
- 其他标准伪类选择器::first-child/:last-child/:nth-child(an+b)/:not§/::after 用来创建一个伪元素/::before
-
1.3 head标签
- html文件head标签常见的标签,如:meta script title style link;
- title标签:定义网页的标题。
- meta标签:通常用于指定网页的描述,关键词,文件的最后修改时间,作者及其他元数据。元数据可以被使用浏览器(如何显示内容或重新加载页面),搜索引擎(关键词),或其他 Web 服务调用。
- charset:定义文档的字符编码
- content:定义域http-equiv或name属性相关的元信息
- http-equiv:把content属性关联到HTTP头部
- name:把content属性关联到一个名称
- scheme:html5不支持,定义用于翻译content属性值的格式
- link标签:用于引入外部样式文件(CSS 文件)
- style标签:用于定义元素的CSS样式。
- script标签:用于定义页面的JavaScript代码,或者引入外部JavaScript文件。
1.4 script标签属性
- script标签内的属性,defer async异步属性常问;来源
- src:定义引用外部脚本的URI,这可以用来代替直接在文档中嵌入脚本。指定了 src 属性的script元素标签内不应该再有嵌入的脚本。
- type:定义script元素包含或src引用的脚本语言。属性的值为MIME类型
- async:指示浏览器是否在允许的情况下异步执行该脚本。该属性对于没有src属性的脚本不起作用。
- defer:被设定用来通知浏览器“该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行”。如果无src,则不起作用。
- 不管这段脚本放在html的何处(即使head中),都会等待dom解析完成后再去加载,如果将script标签放在整个html文件的最后,那就不需要defer了。
- async和defer的区别:defer要等到整个页面正常渲染结束,才会执行;async是在渲染html时发现 脚本已经异步下载完,就去执行,执行完了,再继续往下渲染html。
- crossorigin:添加crossorigin属性来获取跨站文件的错误信息
- integrity:提供hash值,来验证览器获得的资源(例如从 CDN 获得的)是否被篡改。
1.5 canvas
-
canvas相关(会不会用,了解它的哪些属性,能够使用到什么程度)
-
是 HTML5 新增的,一个可以使用脚本(通常为 JavaScript) 在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。
-
属性:width/heigh
1.6 img标签
- title属性:鼠标移动到元素上的文本提示
- alt属性:text;规定图像的替代文本
1.7 input标签
- type属性:值button/checkbox/image等(没有select),默认text
1.8 a标签
- herf属性:用于指定链接的目标URL
- name属性:html不支持;规定锚的名称
- target属性:
_blank
新窗口打开 - rel属性:规定当前文档与目标URL之间的关系
1.9 iframe标签
注:
- 在相同域名下,外层网页脚本可以获取iframe网页内的对象,内嵌的iframe可以获取外层的对象
- 通过iframe,网页可以嵌入其他网页内容,并可以动态更改(?表述错了?只有在同域的情况下?)
1.10 标题标签
<hgroup>
-
标签被用来对标题元素进行分组。
-
当标题有多个层级(副标题)时,
<hgroup>
元素被用来对一系列<h1>-<h6>
元素进行分组。
<header>
-
标签定义文档或者文档的一部分区域的页眉。
-
<header>
元素应该作为介绍内容或者导航链接栏的容器。
2. CSS
2.1 display、visibility区别
- display:none visibility:hidden区别,是否触发重绘重排;
-
元素的显示与隐藏
- display 属性:none/block;隐藏元素后,不再占有原来的位置。
- visibility属性:hidden/visible;隐藏元素后,继续占有原来的位置。
- overflow 属性:visible/hidden/scroll/auto;指定了如果内容溢出一个元素的框(超过其指定高度及宽度)时,会发生什么。
-
两者区别:区别
-
重绘重排:重排和重绘
- display: none;/* 1重排 2不会被子元素继承,但是父元素都不在了,子元素自然也就不会显示了 3无法触发绑定的事件 4transition无效 */
- visibility: hidden;/* 1重绘 2会被子元素继承,可以通过设置子元素visibility: visible 使子元素显示出来 3无法触发绑定的事件 4transition无效 */
- opacity: 0;/* 1重绘 2也会被子元素继承,但是不能通过设置子元素opacity: 1使其重新显示 3可以触发绑定的事件 4transition有效 */
-
2.2 CSS3
变量
声明变量:--foo: #7F583F
读取变量:var()
对比:less的变量用@;sass用$
2.3 定位
定位(属性和用法,如粘性定位的使用场景);
-
定位:将盒子定在某一个位置,所以定位也是在摆放盒子,按照定位的方式移动盒子。定位=定位模式(position=fixed)+边偏移(top/bottom/left/right)。
-
定位模式:
- 静态定位static:默认定位方式,无定位。
- 相对定位relative:相对于它原来的位置来移动,不脱标
- 绝对定位absolute:相对于它祖先元素来移动,脱标。如果没有祖先元素或者祖先元素没有定位,则以浏览器为准定位( Document 文档)。子绝父相
- 固定定位fixed:以浏览器的可视窗口为参照点移动元素,不随滚动条滚动,脱标。固定在版心右侧位置(让固定定位的盒子 left: 50%, 再margin—left:板心宽度的一半距离)
- 粘性定位sticky:可以被认为是相对定位和固定定位的混合。以浏览器的可视窗口为参照点移动元素(固定定位特点);粘性定位占有原先的位置(相对定位特点);必须添加 top, left, right, bottom 其中一个才有效跟页面滚动搭配使用。使用场景:侧边栏
-
定位叠放次序z-index:可控制盒子的前后次序,有定位的盒子才有此属性
clear作用:清除浮动;
- 为什么需要清楚浮动:父级盒子很多情况下,不方便给高度,子盒子浮动又不占有位置,就会影响下面的盒子,对后面元素排版产生影响。
- clear:选择器: {clear: both;};弊端—margin失效;清除浮动策略—闭合浮动
- 清除浮动方法:
- 额外标签法(隔墙法):在最后一个浮动元素末尾添加一个空块级元素,给其赋以属性 clear: both;
- 父级添加 overflow:将其属性值设置为 hidden、auto或scroll;缺点—无法显示溢出部分
- :after 伪元素法:也是额外标签法的一种
- 双伪元素法
2.4 盒子模型
- 盒子模型(两种盒模型和区别,以及使用场景)参考
- 1.W3C标准盒模型:box-sizing:content-box(默认);当我们对一个元素设置他的width和height的时候,标准盒模型只是对content(蓝色部分)设置了宽高,这个元素真正的宽高就还要加上他的padding(内边距)border(边框)margin(外边距)
- 2.IE盒模型(怪异盒模型):box-sizing: border-box;当我们对一个元素设置他的width和height的时候,怪异盒模型把整个盒子看成是一个整体。给整个盒子一个宽高。如果还给盒子设置了额外的边距和边框。那么中间的content蓝色部分就要受到挤压,变小。
- 注意:css的盒模型由content(内容)、padding(内边距)、border(边框)、margin(外边距)组成。但盒子的大小由content+padding+border这几部分决定,而不加上margin。
- 注意:在编写页面代码时应尽量使用标准的W3C模型(需在页面中声明DOCTYPE类型),这样可以避免多个浏览器对同一页面的不兼容。因为若不声明DOCTYPE类型,IE浏览器会将盒子模型解释为IE盒子模型,FireFox等会将其解释为W3C盒子模型;若在页面中声明了DOCTYPE类型,所有的浏览器都会把盒模型解释为W3C盒模型。
- 使用场景:框架想要具备栅格系统,肯定要用border-sizing
- padding属性简写:四个值(上右下左)、三个值(上 右左 下)、两个值(上下 右左)、一个值(上右下左)
2.5 CSS常见布局方式
传统盒模型布局方式
文档流布局
浮动布局
定位布局
flex布局
flex布局(常问:用法,有哪些属性,可能当场给出图片让写布局);
- MurphyChen’s Notes移动端开发之 flex 布局;菜鸟教程
- 原理总结:通过给父盒子添加flex属性,来控制子盒子的位置和排列方式。
- flex 布局父项常见属性:
- flex-direction:设置主轴方向,row/row-reverse/column/column-reverse
- flex-wrap:设置子元素是否换行。默认不换行,若父盒子一行上装不开,则会缩小子元素的宽度,从而仍然一行显示。nowrap(默认)/wrap/wrap-reverse
- flex-flow:是
flex-direction
和flex-wrap
属性的复合属性,flex-flow: row nowrap; - justify-content:设置主轴子元素排列方式,flex-start(默认)/flex-end/center/space-around/space-between
- align-items :设置侧轴上的子元素排列方式(单行)。flex-start/flex-end/center/baseline/strech默认
- align-content:设置侧轴上的子元素的排列方式(多行),只能用于子项出现换行的情况(多行)。flex-start/flex-end/center/space-around/space-between/strech(默认)
- flex 布局子项常见属性
- order: 定义项目的排列顺序。数值越小,排列越靠前,默认为 0。
- flex-grow:子容器剩余空间的拉伸比例(
flex-grow
定义子容器的瓜分剩余空间的比例,默认为0
,即如果存在剩余空间,也不会去瓜分。深入理解 flex-grow、flex-shrink、flex-basis) - flex-shrink:子容器超出空间的压缩比例(定义了溢出空间的压缩规则,默认为1)
- flex-basis:子容器在不伸缩情况下的原始尺寸,默认为auto,即项目本来的大小。
- flex :是flex-grow、flex-shrink、flex-basis的简写,默认0、1、auto。定义子项目分配剩余空间,用 flex 表示占多少份数,值可以是整数/百分数。快捷值:auto (1 1 auto) 和 none (0 0 auto)。
flex: flex-grow flex-shrink flex-basis|auto|initial|inherit;
- align-self :控制子项自己在侧轴上的排列方式,允许单个项目有与其他项目不一样的对齐方式,可覆盖 align-items 属性。auto/flex-start/flex-end/center/baseline/strech
grid网格布局
常用的CSS布局
水平垂直居中
-
如何实现垂直居中(定位+平移,flex,一般需要说出2种及以上方法);css实现水平/垂直居中效果 - 逍遥游 - 博客园 (cnblogs.com)
-
1.table表格法:显示设置父元素为:table,子元素为:cell-table,vertical-align: middle;文字text-align: center;
-
2.空元素法:在父元素里放一个空标签span,设置他的vertical-align: middle; 宽度为0
-
3.-50%定位法:子元素绝对定位,距离顶部 50%,左边50%,然后使用css3 transform: translate(-50%; -50%)
-
4.flex布局法:父盒子display: flex; justify-content: center; align-items: center;
-
5.绝对定位法:父元素使用定位(相对/绝对都行),子元素设置position: absolute; top: 0; left: 0; bottom: 0; right: 0; margin: auto;
-
外边距可以让块级盒子 水平居中,但是必须满足两个条件:盒子必须指定宽度(width);盒子左右的外边距都设置为 auto。 使行内元素或行内块元素水平居中
text-align: center;
-
-
图片和文字垂直居中
圣杯布局
-
三栏式布局,两边定宽,中间自适应
<html> <head> <style> //三个元素的宽高设置好,都设置为float:left,中间的盒子设置宽度100% //左面的盒子margin-left设置为-100%(负的中间盒子的宽度),使左盒子提升到上一层 //右边的盒子margin-left设置为负的自身的宽度 //将父元素的padding-left设置为左边盒子的宽度,padding-right设置为右盒子的宽度,便可使中间子元素不被遮盖 //最后设置相对位置,使左盒子和右盒子往两边移动 .container{ padding: 0 200px; } .middle{ width: 100%; background: paleturquoise; height: 200px; float: left; } .left{ background: palevioletred; width: 200px; height: 200px; float: left; margin-left: -100%; } .right{ width: 200px; height: 200px; background: purple; float: left; margin-left: -200px; } </style> </head> <body> <div class="container"> //中间栏要放在文档流前面以优先渲染 <div class="middle">测试</div> <div class="left">left</div> <div class="right">right</div> </div> </body> </html>
双飞翼布局
圣杯布局和双飞翼布局解决问题的方案在前一半是相同的,也就是三栏全部float浮动,但左右两栏加上负margin让其跟中间栏div并排,以形成三栏布局。为了中间div内容不被遮挡,双飞翼布局直接在中间盒子内部创建子子盒子用于放置内容,在该子盒子里用margin-left和margin-right为左右两栏div留出位置。
<html>
<head>
<style>
.middle-container{
width: 100%;
background: paleturquoise;
height: 200px;
float: left;
}
.middle{
margin-left: 200px;
margin-right: 200px;
}
.left{
background: palevioletred;
width: 200px;
height: 200px;
float: left;
margin-left: -100%;
}
.right{
width: 200px;
height: 200px;
background: purple;
float: left;
margin-left: -200px;
}
</style>
</head>
<body>
<div class="container">
<div class="middle-container">
<div class="middle"></div>
</div>
<div class="left">left</div>
<div class="right">right</div>
</div>
</body>
</html>
2.6 BFC
是什么:BFC(Block Formatting Context),即块级格式化上下文,是页面中的一块渲染区域,并且有自己的渲染规则
规则:
- 内部盒子在垂直方向上一个接一个放置
- 同一个BFC的两个盒子margin会重叠
- 每个元素的左外边距与包含块的左边界接触,即使浮动了也是这样
- BFC的区域不会与float元素的区域重叠
- 计算BFC高度时,浮动子元素也参与计算
- BFC是页面上的一个独立容器,容器里的子元素与容器外的互不干扰
- BFC里的除了新创造的BFC的内部元素,都属于同一个BFC
触发条件
- 根元素,即html元素
- 脱离标准流的定位,即position为absolute/fixed
- 浮动元素,即float为left/right
- display为inline-block、table-cell、table-caption、table、inline-table、flex、inline-flex、grid、inline-grid
- overflow不为visible的元素,即overflow为auto/scoll/hidden
应用场景
- 防止margin重叠(塌陷)
- 清除内部浮动
- 自适应多栏布局
2.7 继承属性
可继承:字体系列属性;文本系列属性;元素可见性;表格布局全局选择器属性;列表布局属性
-
字体系列属性
-
font-family:字体系列
-
font-weight:字体的粗细
-
font-size:字体的大小
-
font-style:字体的风格
-
-
文本系列属性
-
text-indent:文本缩进
-
text-align:文本水平对齐
-
line-height:行高
-
word-spacing:单词之间的间距
-
letter-spacing:中文或者字母之间的间距
-
text-transform:控制文本大小写(就是uppercase、lowercase、capitalize这三个)
-
color:文本颜色
-
-
元素可见性
- visibility:控制元素显示隐藏
-
列表布局属性
- list-style:列表风格,包括list-style-type、list-style-image等
-
光标属性
- cursor:光标显示为何种形态
2.8 em/px/rem/vh/vw区别
传统开发中,常使用px、%、em这几个单位,css3新增了rem、vh、vw、vm等计量单位,利用这些新的单位开发出比较良好的响应式页面,适应多种不同分辨率的终端,包括移动设备等
-
相对长度单位:em、ex、ch、rem、vw、vh、vmin、vmax、%
-
绝对长度单位:cm、mm、in、px、pt、pc
px:表示像素,即显示器上的一个个小点,每个像素大小等同(在移动端存在设备像素比,实际大小不确定,但px的大小和元素的其他属性无关)
em:相对于当前对象内文本的字体尺寸,如当前行内文本的字体未被人为设置,则相对于浏览器的默认字体尺寸(1em=16px)
rem:相对HTML根元素font-size的值
vh、vw、vm:vw是根据窗口的宽度,分成100等份,100vw表示满宽;vh为窗口的高度;vm相对于视口的宽度或高度中较小的那个(窗口,在桌面端指浏览器的可视窗口,在移动端指布局视口;%相对于父元素)
2.9 回流、重绘
- 是什么:
在HTML中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及回流与重绘
- 回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置
- 重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制
在页面初始渲染阶段,回流不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局发生改变
-
当对
DOM
的修改引发了DOM
几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来 -
当对
DOM
的修改导致了样式的变化(color
或background-color
),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了重绘
- 如何触发
回流:这一阶段主要计算节点的位置和几何信息,当页面布局和几何信息发生变化时,就需要回流
- 添加或删除可见的DOM元素
- 元素位置、尺寸发生变化
- 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所代替
- 页面一开始渲染时
- 浏览器的窗口尺寸变化
- 获取一些特定值时(如offsetTop、scollTop等),需要通过即时计算的属性
重绘:触发回流一定会触发重绘
- 颜色的修改
- 文本方向的修改
- 阴影的修改
浏览器优化机制:由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列;当获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的offsetTop等方法都会返回最新的数据,因此浏览器不得不清空队列,触发回流重绘来返回正确的值
- 如何减少
- 如果想设定元素的样式,通过改变元素的
class
类名 (尽可能在 DOM 树的最里层) - 避免设置多项内联样式
- 应用元素的动画,使用
position
属性的fixed
值或absolute
值(如前文示例所提) - 避免使用
table
布局,table
中每个元素的大小以及内容的改动,都会导致整个table
的重新计算 - 对于那些复杂的动画,对其设置
position: fixed/absolute
,尽可能地使元素脱离文档流,从而减少对其他元素的影响 - 使用css3硬件加速,可以让
transform
、opacity
、filters
这些动画不会引起回流重绘 - 避免使用 CSS 的
JavaScript
表达式
2.10 动画
transition(过渡):
- 元素从一种样式逐渐改变为另一种的效果(与animation效果类似,但使用不一样);必须指定要添加效果的css属性、效果的持续时间
- transition(简写属性);transition-property(过渡的css属性名称);transition-duration(过渡效果花费的时间,默认0);transition-timing-function(过渡效果的时间曲线,默认“ease”);transition-delay(过渡效果何时开始,默认0);
- 通常用在鼠标移动到过渡元素上查看效果
transform(转换):
- 可以对元素进行移动、缩放、转动、拉长或拉伸;一般配合transition过渡使用,且不支持inline元素,使用前把它变成block
- translate(位移);scale(缩放);rotate(旋转);skew(倾斜)
animation(动画):
-
css动画只需要定义一些关键的帧,而其余的帧浏览器会根据计时函数插值计算出来
-
animation: change1s 部分就是动画的第一部分,用于描述动画的各个规则;@keyframes change{} 部分就是动画的第二部分,用于指定动画开始、结束以及中间点样式的关键帧;
div { animation: change 3s; } @keyframes change { 0% { color: #f00; } 100% { color: #000; } }
-
属性:animation(简写);animation-duration;animation-timing-function;animation-delay;animation-iteration-count;animation-fill-mode;animation-play-state;animation-name
2.11 伪类与伪元素
伪类的操作对象是文档树中已有的元素,而伪元素则创建了一个文档数外的元素。因此,伪类与伪元素的区别在于:有没有创建一个文档树之外的元素。
CSS3规范中的要求使用双冒号(::)表示伪元素,以此来区分伪元素和伪类,比如::before和::after等伪元素使用双冒号(:😃,:hover和:active等伪类使用单冒号(😃
链接
-
链接的状态:
-
a:link - 正常,未访问过的链接
-
a:visited - 用户已访问过的链接
-
a:hover - 当用户鼠标放在链接上时
-
a:active - 链接被点击的那一刻
-
-
顺序规则:
-
a:hover 必须跟在 a:link 和 a:visited后面
-
a:active 必须跟在 a:hover后面
-
3. JavaScript
3.1 作用域与闭包
- 闭包(描述什么是闭包,闭包的优点,缺陷,如何解决缺陷);学习Javascript闭包 阮一峰的网络日志 (闭包的缺点与解决办法_zihanzy.com的博客
闭包(closure):指有权访问另一个函数作用域中变量的函数。(–JavaScript 高级程序设计)
闭包作用:延伸变量的作用范围;让这些变量的值始终保持在内存中
缺陷:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
解决办法:1.能不用闭包就不用2.及时释放
var f = fn1()
f()
f = null //让内部函数成为垃圾对象,从而回收闭包
- 作用域的本质是什么?闭包与作用域的关系是什么?参考
作用域是一套规则,规定了在运行时代码的某些特定部分中变量、函数和对象的可访问性。作用域链是作用域这套规则的实现。
JavaScript 属于解释型语言,JavaScript 的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:
解释阶段:
- 词法分析
- 语法分析
- 作用域规则确定
执行阶段:
- 创建执行上下文
- 执行函数代码
- 垃圾回收
JavaScript 解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是 this 的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。
函数在调用激活时,会开始创建对应的执行上下文,在执行上下文生成的过程中,变量对象、作用域链、this的值会被分别确定。
在 fn 函数中,取自由变量 x 的值时,要到哪个作用域中取?——要到创建 fn 函数的那个作用域中取,无论 fn 函数将在哪里调用。
“无论函数是在哪里调用,也无论函数是如何调用的,其确定的词法作用域永远都是在函数被声明的时候确定下来的”
闭包使得作用域链上的变量对象不被垃圾回收机制回收,可以记住并访问所在作用域,因为嵌套函数(闭包)的引用被保留了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8iWE1QPk-1665332674765)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ab263a936a5441293b442369ebf93f5~tplv-k3u1fbpfcp-watermark.image?)]
3.2 继承
继承(掌握每一种继承的手写代码);js继承的6种方式 - ranyonsue - 博客园 (cnblogs.com) 这样回答继承可能面试官更满意
- 原型链继承
- 借用构造函数继承
- 组合继承(组合上面两种)
- 原型式继承
- 寄生式继承
- 寄生组合式继承
- es6-extends继承
- ES5的继承机制是先创造一个独立的子类的实例对象,然后再将父类的方法添加到这个对象上,即“实例在前,继承在后”;ES6的继承机制是将父类的属性和方法,加到一个空的对象上,再将该对象作为子类的实例(调用super()来新建一个父类的实例对象),即“继承在前,实例在后”。ES6的继承必须先调用super()方法,因为这一步会生成一个继承父类的this对象,没有这一步就无法继承父类。
- 子类无法继承父类的私有属性(#);子类会继承父类的静态属性和静态方法(static)
Object.getPrototypeOf()
:可以用来从子类获取父类- super:
- 作为函数调用时,代表父类的构造函数,返回的是子类的实例,即super内部的this指的是子类的实例。相当于
A.prototype.constructor.call(this)
- 作为对象时,在普通方法中指向父类的原型对象,在静态方法中指向父类
- 作为函数调用时,代表父类的构造函数,返回的是子类的实例,即super内部的this指的是子类的实例。相当于
3.3 原型链
什么是原型:
- 在JavaScript中是使用构造函数来新建一个对象的,每一个构造函数都有一个prototype属性,它的属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的prototype属性对应的值,在ES5中这个指针被称为对象的原型。现在浏览器中都实现了__proto属性来访问这个属性,但最好不要用这个属性,因为不是规范中规定的,最好用
Object.getPrototypeOf()
方法
什么是原型链,如何访问原型;
- 定义:当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,
_proto_
隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的_proto_
中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。 - 获取实例对象obj的原型对象,有三种方法:
obj.__proto__
obj. constructor.prototype
Object.getPrototypeOf(obj)
new
使用new的构造函数调用会生成.prototype和.constructor引用;推荐用字面量和Object.creat()来创建对象,Object.creat()会创建一个用有空[[Prototype]]链接的对象,这个对象无法进行委托,由于没有原型链,所以instanceof操作符无法进行判断,总是会返回false;这些空[[Prototype]]对象通常被称作“字典”,它们完全不会收到原型链的干扰,非常适合用来存储数据。
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1.创建(或者说构造)一个全新的对象。
2.这个新对象会被执行[[原型]]连接。i
3.这个新对象会绑定到函数调用的this。
4.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
3.4 操作符:
数字
-
二进制:整数转二进制(除2取余,逆序排列),小数转二进制(乘2取整,顺序排列)
-
原码、补码和反码:
- 原码:数值前面增加了一位符号位,正数时符号位为0,负数时符号位为1(为了表示正与负)
- 反码:正数的反码与其原码相同,反码是对其原码除符号位外皆取反(为了解决正负相加等于0的问题)
- 补码:正数的补码与其原码相同,负数的补码是在其反码的末位加1去掉最高进位(为了解决+0与-0是同一个数却有两种表达的问题)
- 结论:二进制数在内存中最终是以补码的形式存储的;32位、12位等的不同就是存储的值范围
-
JavaScript中数字存储
- JavaScript不是类型语言,没有不同类型的数字,不分整数和浮点数,所有数字都是使用浮点类型来存储(IEEE 754标准定义的64位浮点格式:0~51位即52位尾数M、52~62位即11位阶码E、第63位即1位符号位S)
- 尾数:将二进制数规范为科学计数法,尾数即数字的小数部分(由于除了0所有数字规格化后首位只能是1,所以IEEE 754直接省略了这个默认的1,所以有效尾数实际上有53位)(由于0.2的二进制是无限循环小数,故尾数取最大52即可,剩下的就截断了,所以会出现0.1 + 0.2 !== 0.3)
- 阶码:阶码 = 阶码真值 + 偏移量1023;阶码真值即为科学计数法中指数真实值的2进制表达
-
js中数字范围
- S代表正负、E决定大小、M决定精度:5e-324~-5e-324
- 一个整数是否是安全整数可以使用JS的内置方法
Number.isSafeInteger()
验证
位运算
令你迷惑的位运算 根据国际IEEE 754 标准,JavaScript
在存储数字时是始终以双精度浮点数来存储的,这种格式用 64 位二进制存储数值,64 位也就是 64 比特(bit),相当于 8 个字节,其中 0 到 51 存储数字(片段),52 到 62 存储指数,63 位存储符号
而在 JS 位运算中,并不会用 64 位来计算,它会先在后台把值转换为 32 位数值,再进行位运算操作,位运算计算完成后再将 32 位转为 64 位存储,整个过程就像在处理 32 位数值一样,所以我们了解位运算时,只需要关注这 32 位二进制整数就可以,因为 64 位存储格式是不可见的,但是也正是因为后台这个默认转换操作,给 JS 这门语言产生了一个副作用,即特殊值 NaN 和 Infinity 在位运算中都会直接被当作 0 来处理
-
按位非(~)
- 按位取反。反码是符号位不变其余位置取反,而按位非是取反码后符号位也变;规律—按位非的最终结果是对原数值取反并减1(
~x = (-x) - 1
) - 使用
按位非~
判断是否等于-1:因为~-1 == 0 == false
,所以!~-1 == 1 == true
,可以用来校验-1 - 使用
按位非~
取整:在位运算中,会把64位在后台转换为32位整数,忽略掉小数。故~~x
便可对x取整
- 按位取反。反码是符号位不变其余位置取反,而按位非是取反码后符号位也变;规律—按位非的最终结果是对原数值取反并减1(
-
按位与(&)
- 对两个操作数的二进制每一位进行对比,两个操作数都为1时结果为1,否则都为0
- 使用
按位与&
判断奇偶:偶数 & 1 //0
;奇数 & 1 //1
- 使用
按位与&
判断数字是否为2的整数幂:n & (n - 1) //等于0时,n就是2的整数幂
-
按位或(|)
- 对两个操作数的二进制每一位进行对比,两个操作数只要有一个为1结果为1,两边都是0结果才为0
- 使用
按位或|
取整:1.111 | 0 //1
- 使用
按位或|
代替Math.round():正数加0.5,负数减0.5即可;1.1 + 0.5 | 0 //1; -1.6 - 0.5 | 0 //-2
-
按位异或(^)
- 与按位或的区别是,按位异或只在一位是1时返回1,两位都是1或两位都是0时都返回0;而已当作把两个数加起来,然后进位去掉
- 使用
按位异或 ^
判断整数部分是否相等:1 ^ 1 //0
两位都是1或两位都是0都返回0,两个相同的数字二进制都是一致的,所以都是0 - 使用
按位异或 ^
来完成值交换:a ^= b
- 使用
按位异或 ^
切换0和1:toggle ^= 1
- 使用
按位异或 ^
判断两数符号是否相同:(a ^ b) >= 0
-
左移(<<)
- 将数值的二进制码按照指定的位数向左移动,符号位不变
- 使用左移取整
-
右移(>>)
- 和左移相反,将数值的二进制码按照指定的位数向右移动,符号位不变
-
无符号右移(>>>)
- 会将数值的所有32位字符都右移
布尔操作符
-
逻辑非:先将数值转换为布尔值,再取反(
!!
相当于Boolean()
) -
逻辑与:
-
如果第一个操作数求值为true,则直接返回第二个操作符;如果第一个操作符求值为false,则返回第一个操作符,且短路
-
如果有一个操作数是null/NaN/undefined,则返回null/NaN/undefined
-
-
逻辑或:
-
如果第一个操作数求值为true,则返回第一个操作数,且短路;如果第一个操作数求值为false,则返回第二个操作数;
-
如果两个操作数都是null/NaN/undefined,则返回null/NaN/undefined
-
比较操作符
非相等运算符
字符串的比较:首先比较首字符的 Unicode 码点,如果相等,再比较第二个字符的 Unicode 码点,以此类推。
非字符串的比较:
- 原始类型值:如果两个运算子都是原始类型的值,则是先转成数值再比较。任何值(包括
NaN
本身)与NaN
使用非相等运算符进行比较,返回的都是false
。 - 对象:会转为原始类型的值,再进行比较。先调用
valueOf
方法;如果返回的还是对象,再接着调用toString
方法
相等运算符
严格相等:
- 两个值的类型不同,直接返回
false
; - 同一类型的基本类型的值比较时,值相同就返回
true
; - 引用类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址
undefined
和null
与自身严格相等
相等:
- 原始类型的值会转换成数值再进行比较
- 对象与原始类型值比较时,先调用对象的
valueOf()
方法,如果得到原始类型的值,就互相比较;如果得到的还是对象,则再调用toString()
方法,得到字符串形式,再进行比较 - undefined和null只有与自身比较,或者互相比较时,才会返回true;与其他类型的值比较时,结果都为false
比较操作符与Object.is()的区别:
-
==
和===
运算符将数字值“+0”和“-0”视为相等,而Object.is()方法则将它们视为不等于。 -
如果两个值都是数字并且都是
NaN
,==
和===
运算符也不会将其视为相等;而Object.is()方法则视为相等。
3.5 数据类型
问题:基本数据类型(手写判断数据类型的方法);判断JS数据类型的四种方法
数据类型:
基本类型:String、Number、Boolean、Symbol、Undefined、Null ;引用类型:Object(还包括 Function 、Array、RegExp、Date 等);新出的还有BigInt。共八个数据类型。
- Symbol和BigInt:
- Symbol:ES5的对象属性都是字符串,容易造成属性名的冲突,ES6引入Symbol,表示独一无二的值。
- BigInt:因为JavaScript所有数字都保存为64位浮点数,所以①数值的精度只能到53个二进制位,大于这个范围的整数无法精确表示;②大于或等于2的2014次方的数值,都会表示位Infinity。故ES2020引入了BigInt,用来表示整数,没有位数的限制。
- null和undefined的区别:
- 基本概念:
null
与undefined
都可以表示“没有”,含义非常相似。null
是一个表示“空”的对象,转为数值时为0;undefined
是一个表示"此处无定义"的原始值,转为数值时为NaN - ①变量声明了但还没有定义的时候就会返回undefined,而不是null
- ②null一般是给对象做未赋值的,而undefined一般是给基本数据类型做未赋值的(因为在用typeof进行判断时,null类型会返回’object’)
- ③undefined不是个关键字,但null是关键字。(可以声明一个变量叫undefined)
- 基本概念:
数据类型的判断方法:
- typeof :是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 7 种:number、boolean、symbol、string、object、undefined、function 等。
typeof NaN //'number'
- 缺点:
typeof null //'Obejct'
;对于引用类型,除了function之外只返回了其原型链最顶端的Obejct
类型(原型链最顶端为null)
- instanceof :是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 instanceof 检测的是原型,只要A的原型链上有B.prototype,则都返回true
- 缺点:instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。
- constructor:当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。如
''.constructor === String
- toString() :是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。
- toString返回对象的字符串表示,基本类型的值需要借助call/apply调用,引用类型可以直接调用
- 由于实例对象可能会自定义
toString
方法,覆盖掉Object.prototype.toString
方法,所以为了得到类型字符串,最好直接使用Object.prototype.toString
方法。通过函数的call
方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型。
数值
浮点数:
- 0.1+0.2为什么不等于0.3:JavaScript内部所有数字都是以64位浮点数形式存储,且JavaScript使用浮点数运算,浮点数不是精确的值,精确度丢失。
- 怎么对比两个小数的大小是否相等:若|a-b|<0.00001,则说明这两个数几乎相等
parseInt:
- parseInt的参数个数(主要考点就是第二个参数是进制);iKendall的博客-CSDN博客;[解析 1, 2, 3].map(parseInt)【JS面试题】
- parseInt(string,radix) 函数可解析一个字符串,并返回一个整数。当参数 radix 的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。
- radix:可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。
数据类型的转换:
-
对象转基本数据类型:
- tostring:处理非字符串到字符串的强制类型转换。基本数据类型转换遵循通用规则;普通对象返回内部属性[[class]]的值(如
[object object]
);数组的默认tostring方法重新定义了,所有元素字符串化后用“,”连接起来。 - valueOf:返回对象的原始值。基础类型的数据,直接返回该类型;非基础类型的值基本转换为对应的原始值。Array/Function/Object/返回数组/函数/对象本身,Boolean/Number/String返回布尔值/数字值/字符串值,Date返回毫秒形式的时间戳
- toPrimitive:若对象中存在这个属性,当对象要转换为对应的原始值时,会优先调用此函数。Symbol.toPrimitive 方法中hint若为string,则优先调用toString方法;若hint为number或default时,优先调用valueof方法。
- 注:tostring和valueof同时存在时,要看对象期望转换成什么样的数据,再由此调用哪个方法;用Object.creat()方式创建对象时,原型链上就没有这些方法,但字面量创建有。
- tostring:处理非字符串到字符串的强制类型转换。基本数据类型转换遵循通用规则;普通对象返回内部属性[[class]]的值(如
深浅拷贝
基础类型:要实现深拷贝,只用简单的赋值方法即可
引用类型:若用简单的赋值语句,就只复制了数据的地址,若源对象发生改变,那么复制的数据也会发生改变,此时就需要深浅拷贝来解决这个问题。
定义:
- 浅拷贝:复制一层,当对象中的属性是引用类型的值时,只复制其内存地址。
- 深拷贝:复制多层,开辟一个新的栈,两个对象属性完全相同,但是对应两个不同的地址。
浅拷贝的实现:
Object.assign(target, ...sources)
Object.assign()
方法将所有可枚举(Object.propertyIsEnumerable()
返回 true)的自有(Object.hasOwnProperty()
返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象。- 例如
var newObj = Object.assign({}, fxObj);
Array.prototype.slice()
,Array.prototype.concat()
- 如果Array的元素有引用类型的值,那么slice和concat实现的还是浅拷贝
- 拓展运算符
...
深拷贝的实现:
-
_.cloneDeep
-
使用JavaScript库lodash
-
const _ = require('lodash'); const obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; const obj2 = _.cloneDeep(obj1); console.log(obj1.b.f === obj2.b.f); // false
-
-
jQuery.extend()
-
const $ = require('jquery'); const obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; const obj2 = $.extend(true, {}, obj1); console.log(obj1.b.f === obj2.b.f); // false
-
-
JSON.stringify()
-
这种方式存在弊端,会忽略undefined、symbol和函数
-
const obj = { name: 'A', name1: undefined, name3: function() {}, name4: Symbol('A') } const obj2 = JSON.parse(JSON.stringify(obj)); console.log(obj2); // {name: "A"}
-
-
循环递归
-
function copyObj(obj){ var cloneObj; //当输入数据为简单数据类型时直接复制 if(obj&&typeof obj!=='object'){cloneObj=obj;} //当输入数据为对象或数组时 else if(obj&&typeof obj==='object'){ //检测输入数据是数组还是对象 cloneObj=Array.isArray(obj)?[]:{}; for(let key in obj){ if(obj.hasOwnProperty(key)){ if(obj[key]&&typeof obj[key]==='object') { //若当前元素类型为对象时,递归调用 cloneObj[key] = copyObj(obj[key]); } //若当前元素类型为基本数据类型 else{cloneObj[key]=obj[key];} } } } return cloneObj; }
-
3.6 数组
6.1(不)改变数组的方法
- 数组的一些方法,哪些能改变,哪些不能改变数组;发呆的薇薇°的博客-CSDN博客
- 改变数组的方法
- push() :向数组的 末尾添加 一个或多个元素,并返回新的长度
- pop(): 删除数组 的 最后一个 元素并返回删除的元素
- shift() : 删除 并返回数组的第一个 元素
- unshift() :向数组的 开头 添加一个或多个 元素,并返回新的长度
- splice() :用于向数组中 插入、删除或替换 数组里的元素
- reverse() :用于反转数组的元素排序
- sort() :对数组的元素进行 排序,排序顺序可以是字母或数字,升序或降序;默认按 字母升序 排列
- forEach() :调用数组的每个元素,并将元素传递给回调函数;对空数组不会执行回调。语法
array.forEach(function(item,index,arr),thisValue)
- 不改变原数组的方法
- filter() :创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。它不会对空数组进行检测。语法:
array.filter(function(item,index,arr),thisValue)
- concat() :连接两个或多个数组,并返回结果;
该方法没有改变现有的数组,但是会返回连接两个或多个数组链接的副本。语法:array1.concat(array2,array3,...,arrayX)
- slice() : 选取数组的一部分,并返回一个新数组
该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回。语法:array.slice(start,end)
- join() :通过指定的 分隔符 将数组中的元素进行分割并转换成一个字符串
- map() :通过指定函数处理数组的每个元素,并返回处理后的数组;不会对空数组进行检测。语法:
array.map(function(item,index,arr),thisValue)
- every():检测数组元素的 每个元素 是否都符合 条件(通过函数提供),不会对空数组进行检测。如果数组中检测到有一个元素不满足,则整个表达式返回false,且剩余的元素不会再进行检测。如果所有元素都满足条件,则返回 true。
- some():检测数组元素中是否有元素符合 指定条件(函数提供),会依次执行数组的每个元素, some()不会对空数组进行检测。如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。如果没有满足条件的元素,则返回false。
- indexOf():搜索数组中的元素,并返回它所在的位置。从头到尾地检索数组,看它是否含有对应的元素,开始检索的位置在数组start处或数组的开头(没有指定 start 参数时)。如果找到一个 item,则返回 item 的第一次出现的位置。开始位置的 索引为0,如果在数组中没找到 指定元素,则返回 -1。
- filter() :创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。它不会对空数组进行检测。语法:
- 改变数组的方法
6.2数组去重
数组去重; MomentYY - 博客园
-
利用Set()+Array.from():
-
const result = Array.from(new Set(arr))
。Array.from() 方法:对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。 -
let arr1 = [1, 2, 2, 3, 3, 3, 4, 1, 2]; let res1 = [...new Set(arr1)]; //集合转为数组:[...st],这里st是一个集合 console.log(res1); // [ 1, 2, 3, 4 ]
-
-
利用两层循环+数组的splice方法:
- 通过两层循环对数组元素进行逐一比较,然后通过splice方法来删除重复的元素。此方法对NaN是无法进行去重的,因为进行比较时
NaN !== NaN
。
- 通过两层循环对数组元素进行逐一比较,然后通过splice方法来删除重复的元素。此方法对NaN是无法进行去重的,因为进行比较时
-
利用数组的indexOf方法:
- 新建一个空数组,遍历需要去重的数组,将数组元素存入新数组中,存放前判断数组中是否已经含有当前元素,没有则存入。此方法也无法对NaN去重。
-
利用数组的includes方法:
- 此方法逻辑与indexOf方法去重异曲同工,只是用includes方法来判断是否包含重复元素。
- includes()方法:用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false。
-
利用数组的filter()+indexOf():
- filter方法会对满足条件的元素存放到一个新数组中,结合indexOf方法进行判断。
-
利用Map():
- Map对象是JavaScript提供的一种数据结构,结构为键值对形式,将数组元素作为map的键存入,然后结合has()和set()方法判断键是否重复。
-
利用对象:
- 其实现思想和Map()是差不多的,主要是利用了对象的属性名不可重复这一特性。
6.3判断数组
- 判断数组的方法(主要是考Array.prototype.toString);一蓑烟雨~~的博客-CSDN博客;DOM曼珠沙华的博客-CSDN博客
- 通过instanceof判断:instanceof运算符用于检验构造函数的prototype属性是否出现在对象的原型链中的任何位置,返回一个布尔值
- 通过constructor判断:实例的构造函数属性constructor指向构造函数,那么通过constructor属性也可以判断是否为一个数组
- 通过Object.prototype.toString.call()判断:可以获取到对象的不同类型,不仅仅可以检验是否为数组,比如是否是一个函数,是否是数字等等
- 通过Array.isArray()判断:用于确定传递的值是否是一个数组,返回一个布尔值
6.4数组循环
- 数组循环的方法(map foreach for in for of);Naynehcs - 博客园 ;https://www.jb51.net/article/71814.htm;[30秒JS 数组篇] 5. JS中的for…in,for…of和forEach的区别 - 知乎 (zhihu.com)
- for循环:
- for遍历数组索引,如果数组中存在空元素,也会执行
- 可以使用return、break、continue跳出或中断循环
- forEach:
- 可以接收三个参数=> item:数组当前项的值,index:数组当前项的索引,arr:数组对象本身。
- forEach无法跳出循环(return、break、continue不起作用)
- for … of:
- for … of 与for一样,如果遇到空元素同样会执行。
- 和for一样可以使用return、break、continue终止循环
- filter:
- 可以接收三个参数=> item:数组当前项的值,index:数组当前项的索引,arr:数组对象本身。
- filter配合return返回符合条件的新数组元素不可自定义,不会改变原数组,数组长度和原数组不同
- map:
- 可以接收三个参数=> item:数组当前项的值,index:数组当前项的索引,arr:数组对象本身
- 有返回值,可以return出来
- map返回新的数组元素可自定义,不会改变原数组,数组与原来长度一致
- some:
- 可以接收三个参数=> item:数组当前项的值,index:数组当前项的索引,arr:数组对象本身
- 配合return使用,返回布尔值,某一元素满足条件返回true,循环中断,只有所有元素都不满足条件才返回false
- every:
- 可以接收三个参数=> item:数组当前项的值,index:数组当前项的索引,arr:数组对象本身
- 配合return使用,返回布尔值,与some相反,某一元素不满足条件返回false,循环中断,只有所有元素满足条件才返回true
- find:
- 可以接收三个参数=> item:数组当前项的值,index:数组当前项的索引,arr:数组对象本身
- 配合return使用,返回第一个满足条件的元素值,如果不存在返回undefined
- findIndex:
- 可以接收三个参数=> item:数组当前项的值,index:数组当前项的索引,arr:数组对象本身
- 配合return使用,用于找出第一个符合条件的数组成员位置(索引),如果没有找到返回-1
- reduce:
- 方法接收两个参数,第一个回调函数(callback),第二个是初始值
- for循环:
3.7 语法
严格模式
给未声明的变量赋值,会抛出ReferenceError
3.8 事件
表单事件
input事件
- 当
<input> 、<select>、<textarea>
的值发生变化时触发,复选框或单选框发生变化时也会触发
onblur:发生在对象失去焦点时
onmouseup:当用户在元素上释放鼠标按钮时
onchange:会在域的内容改变时发生。也可用于单选框与复选框改变后触发的事件。
3.9 标准库:
Boolean对象
- 作为构造函数时,主要用于生成布尔值的包装对象实例。
new Boolean(false) //object
- 单独使用时,将任意值转换为布尔值
Boolean(false) //false
函数
JavaScript的全局函数:
- decodeURI():解码某个编码的URI
- decodeURIComponent():解码一个编码的URI组件
- encodeURI():把字符串编码为URI
- encodeURIComponent():把字符串编码为URI组件
- escape():对字符串进行编码
- eval():计算 JavaScript 字符串,并把它作为脚本代码来执行
- isFinite():检查某个值是否为有穷大的数
- isNaN():检查某个值是否是数字
- Number():将对象的值转换成数字
- parseFloat():解析一个字符串并返回一个浮点数
- parseInt():解析一个字符串并返回一个整数
- String():把对象的值转换为字符串
- unescape():对由 escape() 编码的字符串进行解码
3.10 垃圾回收机制
3.12 map weakmap set weakset区别
map weakmap set weakset区别。_IT入门; touryung - 博客园
Set
- 类似于数组,但成员的值都是唯一的,没有重复的值
- Set实例的属性和实例:
constructor size add() delete() has() clear()
- Set实例的遍历方法:
keys() values() entries() forEach()
;Set只有键值没有键名,所以keys和values行为完全一致;默认遍历器生成函数是values,故可以直接for…of循环遍历Set;扩展运算符(…)内部使用for…of循环,故可以用于Set结构
WeakSet
- 与Set类似,但是与Set有两个区别:WeakSet的成员只能是对象;WeakSet中的对象都是弱引用(垃圾回收机制不考虑WeakSet对该对象的引用,ES6规定不可遍历)
- 方法:
add() delete() has()
- 好处:储存DOM节点,不用担心这些节点从文档移除时,会引发内存泄漏(垃圾回收机制会自动释放内存,不用手动清理)
Map
- 类似于对象,键值对的集合(hash结构),但键的范围不限于字符串,各种类型都可当键
- 实例的属性和操作方法:
size set(key,value) get(key) has(key) delete(key) clear()
- 遍历方法:
keys() values() entries() forEach()
;map的遍历顺序就是插入顺序 - 与其他数据结构的转换:
- Map转数组(
...
); - 数组转Map(
new Map(arr)
); - Map转对象(若map键都是字符串,则无损转对象,若非字符串,则键名转成字符串再作为对象的键名);
- 对象转Map(
new Map(Object.entries(obj))
) - Map转JSON(键名都是字符串时可转为对象JSON,键名有非字符串可转为数组JSON)
- JSON转Map()
- Map转数组(
WeakMap
- 与Map类似,但是与Map有两个区别:WeakMap只接受对象作为键名(null除外);WeakMap的键名所指的对象,不计入垃圾回收机制
- 语法:
get() set() has() delete()
3.13 JSON
- 对JSON的理解。对JSON的理解
3.14 DOM、BOM
定义
- DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。
- BOM(Browser Object Model)即浏览器对象模型,提供了独立于内容而与浏览器窗口进行交互的对象;由于BOM主要用于管理窗口与窗口之间的通讯,因此其核心对象是window;
虚拟Dom
虚拟DOM的操作都是在内存中进行,不会有操作DOM的情况?
3.15 异步编程
异步编程的实现方式
-
回调函数:
-
缺乏顺序性:大脑对干事情的计划方式是线性的、阻塞的、单线程的语义,但是回调表达异步流程的方式是非线性的、非顺序的,这使得正确推导这样的代码难度很大。难于理解的代码是坏代码,会导致坏bug; 我们需要一种更同步、更顺序、更阻塞的的方式来表达异步,就像我们的大脑一样。
-
缺乏可信任性:回调会受到控制反转的影响,因为回调暗中把控制权交给第三方来调用你代码中的continuation。这种控制转移导致一系列麻烦的信任问题,比如回调被调用的次数是否会超出预期(我们用回调函数来封装程序中的continuation,然后把回调交给第三方(甚至可能是外部代码),接着期待其能够调用回调,实现正确的功能。); 可以发明一些特定逻辑来解决这些信任问题,但是其难度高于应有的水平,可能会产生更笨重、更难维护的代码,并且.缺少足够的保护,其中的损害要直到你受到bug的影响才会
被发现。
-
-
Promise
- 我们不希望把自己程序的continuation传给第三方,而是希望第三方给我们提供了解其任务何时结束的能力; Promise,就是一个容器,里面保存着某个未来才会结束的事件的结果。从语法上来说promise是一个对象,可以获取异步操作的消息; promise成功解决了回调函数中嵌套调用和错误跟踪、回调函数控制权等问题。
- 用法:
const promise = new Promise(function(resolve, reject) {});
- 实例方法:then()、catch()、finally()
- 构造函数方法:all()、race()、allSettled()、resolved()、reject()、reject()、try()
- 局限:
- 顺序错误处理:链中的错误很容易被吞掉,从而难以关联错误处理函数来检查错误
- 单一值
- 单决议
- 惯性
- 无法取消的Promise:若因某些原因没有处理,也没用办法从外部停止它的进程
-
generator
-
来源:在多个回调依赖的场景中,尽管Promise通过链式调用取代了回调嵌套,但过多的链式调用可读性仍然不佳,控制流程也不方便。
-
原理:一种顺序、看似同步的异步流控制表达风格。此方式,可以在函数的执行过程中,将函数的执行权转移出去,在函数外部还可以将执行权转移回来。当遇到异步函数执行的时候,将函数执行权转移出去,当异步函数执行完毕时再将执行权给转移回来。因此generator内部对于异步操作的方式,可以以同步的顺序来书写。使用这种方式需要考虑的问题是何时将函数的控制权转移回来,因此需要有一个自动执行generator的机制,比如说co模式来实现generator的自动执行
-
Generator函数:执行
Generator
函数会返回一个遍历器对象,可以依次遍历Generator
函数内部的每一个状态。形式上,Generator
函数是一个普通函数,但是有两个特征:-
function
关键字与函数名之间有一个星号 -
函数体内部使用
yield
表达式,定义不同的内部状态 -
function* foo(x) { var y = 2 * (yield (x + 1)); var z = yield (y / 3); return (x + y + z); } var a = foo(5); a.next() // Object{value:6, done:false} a.next() // Object{value:NaN, done:false} a.next() // Object{value:NaN, done:true} var b = foo(5); b.next() // { value:6, done:false } b.next(12) // { value:8, done:false } b.next(13) // { value:42, done:true } // done用来判断是否存在下个状态,value对应状态值 // yield表达式本身没有返回值,或者说总是返回undefined // 通过调用next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
-
-
注:
Generator
并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator
接口…)
-
-
async函数
- async函数是generator和promise实现的一个自动执行的语法糖,它内部自带执行器,当函数内部执行到一个await语句的时候,如果语句返回一个promise对象,那么函数将会等待promise对象的状态变为resolve后再继续向下执行。因此可以将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。
- 与generator的区别
async/await
自带执行器,不需要手动调用next()就能自动执行下一步async
函数返回值是Promise对象,而Generator返回的是生成器对象await
能够返回Promise的resolve/reject的值
- async的异常处理:有异常的时候想终止执行,最简洁的方式还是不要捕捉异常。如果一定要处理一下异常再终止,可以在catch里面再`return Promise.reject(err),异常就会继续往外抛了
并发与并行的区别
- 并发(Concurrent):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机(CPU)上运行。
- 并行(Parallel):当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
- 区别:并发指的是多个事情在同一时间段内同时发生了,并行指的是多个事情在同一时间点上同时发生了;并发的多个任务之间互相抢占资源,并行的多个任务之间不互相抢占资源;只有在多个CPU的情况中,才会发生并行,否则看似同时发生的事情,其实都是并发执行的。
- 进程与线程:进程像一列火车,线程像车厢。进程和线程独立运行,并可能同时运行,但多个线程能共享单个进程的内存。由于JavaScript的单线程特性,其代码具有完整运行特性,并不会有多线程编程的复杂性;虽然是单线程,但是异步事件循环机制还是存在不确定性,即竞态条件
3.16 this
为什么要用this:this提供了一种更优雅的方式来隐式“传递”一个对象引用,因此可以将API设计得更加简洁并且易于复用
this是什么:参考’作用域与闭包’这节,JavaScript 解释阶段便会确定作用域规则,但是执行上下文是函数执行之前创建的。函数在调用激活时,会开始创建对应的执行上下文,在执行上下文生成的过程中,变量对象、作用域链、this的值会被分别确定。this就是执行上下文的一个属性,会在函数执行的过程中用到。(每个函数的this是在调用时被绑定的,完全取决于函数的调用位置(也就是函数的调用方法))
绑定规则:
如果要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面这四条规则来判断this的绑定对象。
- 由new调用?绑定到新创建的对象。
- 由call或者apply(或者硬绑定bind)调用?绑定到指定的对象。
- 由上下文对象调用?绑定到那个上下文对象。
- 默认:在严格模式下绑定到undefined,否则绑定到全局对象。
注意:
- 一定要注意,有些调用可能在无意中使用默认绑定规则。如果想“更安全”地忽略this绑定,你可以使用一个DMZ对象。比如,Object.create(null),以保护全局对象。
- ES6中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)。这其实和ES6之前代码中的self = this机制一样。
3.17 bind、call、apply区别
作用:三者作用都是改变执行时的上下文,即改变this的指向;call、apply都是function对象的原型方法
call:第一个参数是this的指向,后面传入的是一个参数列表(与apply仅有的区别)
apply:第一个参数也是this的指向,第二个参数是this绑定函数接受的参数(以数组形式传入);改变this指向后原函数会立即执行,且此方式只是临时改变this指向一次;若没有参数或参数为undefined/null,则this默认指向window
bind:第一个参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入);改变this指向后不会立即执行,而是返回一个永久改变this指向的函数
应用:
-
将伪数组转化为数组(含length属性的对象、dom节点、arguments)
let arr = Array.prototype.slice.call(div);
-
数组拼接:
[].push.apply(arr1,arr2) //给arr1添加arr2
-
判断变量类型
console.log(Object.prototype.toString.call(arr1)); // [object Array] console.log(Object.prototype.toString.call(str1)); // [object String] console.log(Object.prototype.toString.call(obj1)); // [object Object] console.log(Object.prototype.toString.call(null)); // [object Null]
-
利用call和apply做继承
- 如构造函数继承、组合继承、寄生组合式继承
3.18 Web常见攻击
XSS
跨站脚本攻击(Cross Site Scripting)
-
允许攻击者将恶意代码植入到提供给其他用户使用的页面中
-
存储型:攻击者将恶意代码提交至目标网站的数据库中,再打开此网站时,就会执行此恶意代码,窃取用户数据或执行指定操作。常见于用户私信、论坛发帖等可以保存用户数据的网站功能
-
反射型:攻击者构造出特殊的包含恶意代码的URL,再打开此URL时,网站服务器将恶意代码从URL中取出,拼接在HTML中返回给浏览器,浏览器会解析此恶意代码。常见于网站搜索、跳转等(攻击者往往结合多种手段诱导用户点击此URL)
-
DOM型:攻击者构造出特殊的包含恶意代码的URL,用户浏览器接收到响应后解析执行,前端JS取出URL中的恶意代码并执行
-
-
预防:
- 防止攻击者提交恶意代码:在用户输入的过程中,过滤掉用户输入的恶意代码,然后提交给后端(但如果绕过前端,就不行了);后端写入数据库前,进行过滤(但此内容在不同地方会有不同显示,不稳)
- 防止浏览器执行恶意代码:
- 在使用 .innerHTML、.outerHTML、document.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent、.setAttribute() 等;
- 如果用 Vue/React 技术栈,并且不使用 v-html/dangerouslySetInnerHTML 功能,就在前端 render 阶段避免 innerHTML、outerHTML 的 XSS 隐患;
- DOM 中的内联事件监听器,如 location、onclick、onerror、onload、onmouseover 等, 标签的 href 属性,JavaScript 的 eval()、setTimeout()、setInterval() 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免
- HttpOnly属性:很多XSS攻击都是来盗用Cookie的,服务器可以通过响应头将cookie设置为HttpOnly,JavaScript是无法读取设置了HttpOnly的cookie数据。故一些重要的数据建议设置HttpOnly标志
CSRF
跨站请求伪造(Cross-site request forgery)
- 攻击者诱导受害者进入第三方网站,在第三方网站中向被攻击网站发送跨站请求;利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的
- 跨站请求可以通过图片URL、超链接、CORS、Form提交等,部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪
- 预防:第三方网站发起的CSRF通常无法阻止发生,只能通过增强自己网站针对CSRF的防护能力来提升安全性
- 阻止不明外域的访问:同源检测、Samesite Cookie、(检查HTTP Referer字段)
- 提交时要求附加本域才能获取的信息:CSRF Token(Token防止表单重复anti csrf攻击)、双重Cookie验证
SQL注入攻击
HTTP劫持
原理:在运营商的路由器节点上,设置协议检测,一旦发现是HTTP请求,且是HTML类型请求,则拦截处理
- 类似DNS劫持,返回302让用户浏览器跳转到另外的网址(钓鱼网站)
- 在服务器返回的HTML数据中插入js或dom节点(通过js脚本安插广告);或将页面嵌入iframe,尽可能减少植入广告对原有网站ye’mian
应对方法:
- 对用户来说,直接向运营商投诉
- 在html 上加上
<meta http-equiv="Cache-Control" content="no-siteapp">
<meta http-equiv="Cache-Control" content="no-transform " />
百度官方给的禁止转码声明。 - 最有用的方式,使用HTTPS ,不让数据那么明显的裸奔。 https 加了SSL协议,会对数据进行加密。
- 在开发的网页中加入代码过滤,大概思路就是用JavaScript代码检查所有的外链是否属于白名单。
3.19 正则
从字符出发
- 单个字符:普通字符直接用,特殊字符需要转译
\*
(\n \f \r \s \t \v [\b]
) - 多个字符:集合的定义方式是使用中括号
[]
,元字符-
表示区间范围[0-9]
(\d \D \w \W \s \S
)
循环与重复
- 0 | 1:
?
代表匹配0个/1个字符 - >= 0:
*
表示匹配0个/无数个字符 - >= 1:
+
表示匹配1个/无数个字符 - 特定次数:
{}
用来重复匹配设置精确的区间范围({x} {min, max} {min, } {0, max}
)
位置边界
- 单词边界:
\b
,匹配能构成单词的字符和不能构成单词的字符中间的位置(如/\bcat\b/
,匹配cat这个单词) - 字符串边界:
^
匹配字符串的开头,$
匹配字符串的末尾(模式匹配:m
多行模式、i
忽略大小写、g
全局模式)
子表达式
- 分组:
()
3.20 语言本质
解释性语言
编译型语言就是一次性把所有的代码转换成机器语言,然后写成可执行文件。
解释性语言则是在程序运行的前一刻还只有源代码没有可执行文件,程序每执行到源代码的某一条指令,就会有一个称为解释程序的外壳程序将源代码转换成二进制代码以供执行,也就是说,要不断地解释、执行、解释、执行、解释、执行。。。像极了人生。因此,解释型语言是离不开解释程序的。像早期的BASIC就是一门经典的解释性语言,要执行BASIC程序,就要进入BASIC环境,然后才能加载程序源文件和运行。
JavaScript 语言本身,虽然是一种解释型语言,但是在现代浏览器中,JavaScript 都是编译后运行。程序会被高度优化,运行效率接近二进制程序。
特点:
-
编译型语言:执行速度快、效率高、依靠编译器、跨平台性较差。
-
解释型语言:执行速度慢、效率低、依靠解释器、跨平台性能好。
4. 浏览器
4.1
4.2 浏览器缓存
- 浏览器缓存全过程,强缓存协商缓存字段有哪些,优先级,http状态码;foolBirdd的博客-CSDN博客_;CSDN资讯的博客-CSDN博客
- 缓存流程:
- 第一次请求需要的资源,服务器返回资源的同时在 response hearder 响应头中添加了缓存策略,告诉浏览器缓存规则(比如以何种方式缓存,缓存信息…),此时就进行缓存了
- 第二次如果是请求相同资源,那么就会检查缓存里面是否有相应资源,有的话直接取用,具体方式请看后续
- 强缓存:检查强缓存,不发送 http 请求直接从缓存里读取资源。一般强缓存都会设置有效时间,过期就失效
- Cache-Control:
- public:响应可以被客户端和代理服务器缓存
- private(默认取值):响应只有客户端可以缓存
- no-cache:直接进入协商缓存阶段
- no-store:不进行任何缓存
- max-age = xxx(xxx 代表数字):缓存内容在 xxx 时间后失效,单位为秒
- must-revalidate:如果缓存失效,必须校验正确后才能使用,某些特殊情况客户端是可以使用过期缓存的,比如校验请求发送失败的时候
- Expires:一个 GMT 格式的时间字符串(时间戳)。服务器返回该请求结果缓存的到期时间,再次发送请求时,如果未超过则直接使用缓存。缺点是判断是否过期用的是本地时间,本地时间可以自己修改
- Pragma:取值为 no-cache,等同 Cache-Control 取值 no-cache
- 注:优先级cache-control > expires,pragma是http1.0内容,Cache-contral是http1.1内容,pragma优先级高
- Cache-Control:
- 协商缓存:需要携带缓存标识(tag)发送 http 请求,由服务器判断是否使用缓存。服务端会进行判断,若资源已发生变化,则返回新资源,否则告诉浏览器启用缓存即可
- 触发条件(两个):强缓存过期;Cache-Control 的值包含 no-cache
- Last-Modified 返回资源文件在服务器最后的修改时间。浏览器再次请求时,If-Modified-Since 携带上次 Last-Modified 的值,到服务端与最后被修改的时间做对比,若时间小于服务端最后修改时间,则代表资源已变更,重新返回资源,状态码为 200,反之状态码为 304,代表缓存可用
- ETag 返回当前文件的一个唯一标识符(服务器生成),再次请求时 If-None-Match 会携带 Etag 的值,与服务端的 Etag 值比较,若不一致,则返回新资源,状态码为 200;反之则继续使用缓存,状态码为 304
- 注:优先级ETag > Last-Modified
- 缓存流程:
4.3 浏览器存储
- 浏览器储存(localstorage sessionStorage cookie区别及使用,不仅要知道概念,还要知道代码怎么写,自己要动手实践);小贤笔记的博客-CSDN博客
- localStorage: localStorage 的生命周期是永久的,关闭页面或浏览器之后 localStorage 中的数据也不会消失。localStorage 除非主动删除数据,否则数据永远不会消失
- sessionStorage: sessionStorage 的生命周期是仅在当前会话下有效。sessionStorage 引入了一个“浏览器窗口”的概念,sessionStorage 是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是 sessionStorage 在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage 也是不一样的
遇到js,css文件阻塞问题(基于此问async defer区别) - cookie: cookie生命期为只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。 存放数据大小为4K左右, 有个数限制(各浏览器不同),一般不能超过20个。缺点是不能储存大数据且不易读取
- localStorage 和 sessionStorage 都具有相同的操作方法,例如 setItem()、getItem() 和 removeItem() 等
4.4 文件阻塞
- js,css文件阻塞的问题,浏览器是怎么处理js,css这些引入文件的,基于此问async和defer异步下载文件的区别小凳子腿的博客-CSDN博客;贾顺名 - 博客园 ;async和defer的作用和区别 ;
- 浏览器渲染网页阻塞顺序
- 1:构建DOM树时,如遇到JS元素时,会阻塞DOM树和CSS规则树的构建,优先执行JS文件
- 2:构建DOM树时,如遇到CSS元素时,会开启异步请求线程,该线程会先下载CSS文件,再构建CSS规则树,该线程会阻塞JavaScript引擎线程,但不会阻塞DOM树的构建
- 3:CSS解析和JS解析互斥,也就是说JS解析时会阻塞CSS解析而CSS解析时也会阻塞JS解析
- 4:JS解析时,如果JS还操作了CSS,而这个CSS还没有下载或构建解析,则会延迟执行JS,直到完成CSS下载构建解析,再会继续执行JS
- 当遇到JS时:
- 渲染引擎会停止执行,控制权交给JS引擎,当执行JS代码时
如果遇到获取DOM,那么如果该DOM还没有解析,则会获取为null,如果JS代码还操作了CSS,而这个CSS如果还没有下载和构建,那么浏览器首先会阻塞JS引擎执行,然后会开启一个异步请求线程,在该线程上,去下载构建CSS规则树,CSS规则树构建完成后,再继续执行JS代码,当JS执行完以后,控制权再次交给渲染引擎去执行。
- 渲染引擎会停止执行,控制权交给JS引擎,当执行JS代码时
- 当遇到CSS元素时:
- 也会开启异步线程,去下载构建CSS规则树,但同时也会继续构建后面的DOM树,也就是说DOM解析和CSS解析可以同时进行,但如果后面遇到JS元素,则会阻塞JS引擎线程执行,后面DOM树解析不受影响。
- async和defer的区别:
- defer:用于开启新的线程下载脚本文件,并使脚本在文档解析完成后执行。
- async:用于异步下载脚本文件,下载完毕立即解释执行代码。
- 浏览器渲染网页阻塞顺序
4.5 浏览器输入url的过程
- 浏览器输入url后的全过程;小凳子腿的博客-CSDN博客
- 1:根据网址进行DNS解析,将相应的域名解析为IP地址
- 2:客户端根据IP地址去寻找对应的服务器并进行TCP三次握手,建立TCP连接
- 3:客户端发起HTTP请求,请求对应资源
- 4:服务器响应并返回相应数据(如:HTML文件)
- 5:浏览器将获取的HTML文档由HTML解析器解析成DOM树
- 6:同时由CSS解析器将CSS样式解析成CSS Rule Tree(CSS规则树)
- 7:将生成的DOM树和CSS规则树合并生成Rendering Tree(渲染树)
- 8:根据渲染树,在屏幕上对元素进行布局
- 9:根据渲染树,将各个元素绘制到屏幕上
- 10:客户端与服务器进行TCP的四次挥手
4.6 跨域问题
(跨域问题,常问 cors、jsonp,主要还是回答cors,设置了Access-Control-Allow-Origin头字段*后,无法携带cookie,如何解决:跨域请求CORS和jsonp、什么是跨域?跨域解决方法)
1.跨域:
浏览器的同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互
2.非同源的限制:
当一个请求url的协议、域名、端口号三者之间任意一个与当前页面url不同,即为非同源。
- 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
- 无法接触非同源网页的 DOM
- 无法向非同源地址发送 AJAX 请求
3.跨域解决方案:
-
JSONP :
-
原理:src和herf属性可以直接通过GET请求跨域访问其他站点;通过添加一个
<script>
元素,向服务器请求json数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来; -
前端
//原生方法: <script src="http://test.com/data.php?callback=dosomething"></script> // 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字 // 处理服务器返回回调函数的数据 <script type="text/javascript"> function dosomething(res){ // 处理获得的数据 console.log(res.data) } </script> //Vue.js this.$http.jsonp('http://www.domain2.com:8080/login', { params: {}, jsonp: 'handleCallback' }).then((res) => { console.log(res); })
-
后端:后端需要拿到指定的回调函数的名字,将json数据放入这个回调函数中,返回给前端,这样前端就可以执行这个回调函数,拿到数据。
-
-
CORS:
-
原理:全称为Cross-Origin Resource Sharing(跨域资源共享),浏览器会自动进行CORS通信,实现CORS通信的关键是后端。发送请求时,分为简单请求和非简单请求。
- 简单请求:发送简单请求,会在请求头中加入
origin:
,指定该请求的域;服务器收到请求后,判断是否接受该跨域请求,如果接受,就在响应头中添加Access-Control-Allow-Origin:
头信息,值一般为origin,若想要同意所有域的请求,值也可以设置为*
;浏览器接收到服务器返回的数据,看是否带有这个头信息,有则返回给请求方,没有则拦截此数据且报错。 - 非简单请求:(多了个预检请求)浏览器检测到有一个非简单的跨域请求时,会拦截该请求,并发送一个预检请求到请求的接收方,通过该请求来知道服务器是否允许跨域请求;若服务器通过了该预检请求,以后每次正常的CORS请求,就跟简单请求一样(请求头有origin信息,响应头有Access-Control-Allow-Origin信息)
- 简单请求:发送简单请求,会在请求头中加入
-
带cookie跨域:前端请求时在
request
对象中配置"withCredentials": true
,指定在涉及到跨域请求时,是否携带 cookie 信息;服务端在response
的header
中配置"Access-Control-Allow-Origin", "http://xxx:${port}"
以及"Access-Control-Allow-Credentials", "true"
-
4.其他
-
window。onerror方法默认情况下无法获取跨域脚本的报错详情
-
不受跨域限制:
其它:
1.promise
- promise常用的一些方法,promise.all 、promise.race手写;
- 常用方法
- Promise.all
- Promise.any
- Promise.race
- Promise.allSettled
- 常用方法
5. 网络
5.1 HTTP
状态码
作用:http状态码是表示网页服务器超文本传输协议相应状态的3位数字码。作用是服务器告诉客户端当前请求响应的状态,通过状态码就可以判断分析服务器的运行状态。
分类:
- 1 信息,服务端收到请求,需要请求者继续执行操作
- 代表服务器收到请求,需要请求者继续执行操作。这类是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束
- 100(Continue):用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分(或者请求已经完成,忽略这个响应)。服务器必须在请求完成后向客户端发送一个最终响应。
- 101(Switching Protocols):服务器根据客户端的请求切换协议,主要用于websocket或http2升级
- 2 成功,操作被成功接受并处理
- 代表请求已成功被服务器接收、理解、并接受
- 200(OK):请求已成功,请求所希望的响应头或数据体随此响应返回
- 201(Created):已创建。成功请求并创建了新的资源
- 202(Accepted):已接受。服务器已经接受请求,但未处理完成
- 203(Non-Authoritative Information):非授权信息。请求成功,但返回的meta信息不在原始的服务器,而是一个副本
- 204(No Content):无内容。服务器成功处理,但没有返回任何内容
- 205(Reset Content):重置内容。服务器成功处理,用户终端(浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
- 206(Partial Content):部分内容。服务器成功处理了部分GET请求。
- 3 重定向,需要进一步的操作以完成请求
- 需要进一步的操作以完成请求。通常,这些状态码用来重定向
- 300(Multiple Choices):多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
- 301(Moved Permanently):永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
- 302(Found):临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
- 303(See Other):查看其它地址。与301类似。使用GET和POST请求查看
- 304(Not Modified):未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
- 305(Use Proxy):使用代理。所请求的资源必须通过代理访问
- 307(Temporary Redirect):临时重定向。与302类似。使用GET请求重定向
- 4 客户端错误,请求包含语法错误或无法完成请求
- 400(Bad Request):客户端请求的语法错误,服务器无法理解
- 401(Unauthorized):请求要求用户的身份认证
- 402(Payment Required):保留,将来使用
- 403(Forbidden):服务器理解请求客户端的请求,但是拒绝执行此请求
- 404(Not Found):服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
- 405(Method Not Allowed):客户端请求中的方法被禁止
- 406(Not Acceptable):服务器无法根据客户端请求的内容特性完成请求
- 407(Proxy Authentication Required):请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
- 408(Request Time-out):服务器等待客户端发送的请求时间过长,超时
- 5 服务器错误,服务器在处理请求的过程中发生了错误
- 500(Internal Server Error):服务器内部错误,无法完成请求
- 501(Not Implemented):服务器不支持请求的功能,无法完成请求
- 502(Bad Gateway):作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
- 503(Service Unavailable):由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
- 504(Gateway Time-out):充当网关或代理的服务器,未及时从远端服务器获取请求
- 505(HTTP Version not supported):服务器不支持请求的HTTP协议的版本,无法完成处理
HTTP请求
常用请求:
get(获取资源)、post(传输实体主体)、put(传输文件)、head(获取报文首部)、delete(删除文件)
-
get和post:
-
本质区别:“约定和规范”上的区别。在规范中,定义get请求是用来获取资源的,即进行查询操作;post是用来传输实体对象的,因此会用来进行添加、修改和删除等操作。按照约定,get和post的参数传递也是不同的,get请求是将参数拼接到URL上进行参数传递,而post是将参数写到请求正文中传递的
-
非本质区别:
- 缓存:get默认缓存,post不
- 参数长度限制:get通过URL,URL是有长度限制的,通常为2k;post没有限制
- 回退和刷新:get可以直接进行回退和刷新;post如果直接回退和刷新,将会把数据再次提交
- 历史记录:get请求的参数会保存在历史记录中,而post请求的参数不会
- 书签:get请求的地址可被收藏为书签,而post不能
-
请求报文的请求头和响应头
参考:
- 通用标头:
- Cache-Control
- 可缓存性
: 它们分别是
no-cache、
no-store、
private和
public - 缓存有效性时间
: 它们分别是
max-age、
s-maxage、
max-stale、
min-fresh - 重新验证并重新加载
: 它们分别是
must-revalidate和
proxy-revalidate - 其他
: 它们分别是
only-if-cached和
no-transform
- 可缓存性
- Connection:持久性连接
和
非持久性连接 - Date
- Pragma
- Trailer
- Transfer-Encoding
- Upgrade
- Via
- Warning
- Cache-Control
-
http请求报文
-
首部行:
-
Accept
Accept-Charset:utf-8,能够接受的字符集
Accept-Encoding
Accept-Language:xxx,表示用户想得到该对象的语言版本
Authorization:Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==,用于超文本传输协议的认证的认证信息
Expect
From
Host:www.someschool.edu指明了对象所在的主机
If-Match
If-Modified-Since
If-None-Match
If-Range
If-Unmodified-Since
Max-Forwards
Proxy-Authorization
RangeReferer
TE
-
User-agent:xxx,指明用户代理,即向服务器发送请求的浏览器的类型
-
Cookie: $Version=1; Skin=new;,服务器通过 Set- Cookie 发送的一个 超文本传输协议Cookie
-
Referer:当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。
-
-
-
http响应报文
- 首部行:
- Accept-Ranges
- Age
- ETag: “737060cd8c284d8af7ad3082f209582d”; 就是一个对象(比如URL)的标志值,就一个对象而言,比如一个html文件,如果被修改了,其Etag也会别修改,所以,ETag的作用跟Last-Modified的作用差不多,主要供WEB服务器判断一个对象是否改变了。比如前一次请求某个html文件时,获得了其 ETag,当这次又请求这个文件时,浏览器就会把先前获得ETag值发送给WEB服务器,然后WEB服务器会把这个ETag跟该文件的当前ETag进行对比,然后就知道这个文件有没有改变了。
- Location
- Proxy-Authenticate
- Retry-After
- Server:xxx,指示该报文是由什么服务器产生的,类似请求报文中的User-agent
- Vary
- www-Authenticate
- Date:xxx,指示服务器产生并发送该响应报文的日期和时间
- ServerLast-Modifind:xxx,指示对象创建或最后修改的日期和时间
- Access-Control-Allow-Origin
- Access-Control-Allow-Origin:* *号代表所有网站可以跨域资源共享,如果当前字段为*那么Access-Control-Allow-Credentials就不能为true
- Access-Control-Allow-Origin: www.baidu.com 指定哪些网站可以跨域资源共享
- Access-Control-Allow-Methods:GET,POST,PUT,DELETE 允许哪些方法来访问
- Access-Control-Allow-Credentials: true 是否允许发送cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。如果access-control-allow-origin为*,当前字段就不能为true
- X-Frame-Options:用于控制网站内容在其他 Web 网站的 Frame 标签内的显示问题(用来告诉浏览器一个页面是否可以嵌入中)。其主要目的是为了防止点击劫持(clickjacking)攻击。
- 首部行:
-
实体标头
-
Allow
-
Content-Encoding
-
Content-Language
-
Content-Length
-
Content-Location
-
Content-MD5
-
Content-Range
-
Content-Type
-
application/json: JSON数据格式
-
multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
-
application/x-www-form-urlencoded : 中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
-
-
Expires:Sun, 1 Jan 2000 01:00:00 GMT。这个响应头也是跟缓存有关的,告诉客户端在这个时间前,可以直接访问缓存副本,很显然这个值会存在问题,因为客户端和服务器的时间不一定会都是相同的,如果时间不同就会导致问题。所以这个响应头是没有Cache-Control:max-age=*这个响应头准确的,因为max-age=date中的date是个相对时间,不仅更好理解,也更准确。
-
Last-Modified
-
Cookie
目的:进行用户与服务器的交互。HTTP服务器是无状态的,但是一个Web站点通常希望能识别用户,把内容与用户身份连接起来,为此使用Cookie,允许站点对用户进行跟踪
4个组件:
- HTTP响应报文中的一个Cookie首部行
- HTTP请求报文中的一个Cookie首部行
- 用户端系统中保留一个cookie文件,并由用户的浏览器进行管理
- 位于Web站点的一个后端数据库
属性(参考):
- 注:
- domain如未设置,默认为
/
httpOnly
属性:设置了HttpOnly
属性的 Cookie 不能被 JavaScript 获取到,这是非常安全的,建议后端设置的 Cookie 都带上HttpOnly
属性。res.setHeader('Set-Cookie', ['name=keliq; httpOnly=true;', 'age=10'])
Expires
:与Max-age都是设置Cookie的有效期,如果没有设置,则默认是Session,即会话期间有效。Expires
用于指定具体的过期时间。若两个同时存在,则Max-age优先级更高SameSite
:用于限制第三方Cookie的发送场景,可以取值(Strict、Lax、None)
- domain如未设置,默认为
http1/1.1/2
- http1.1、http2区别;球意迹的博客-CSDN博客_http1.1
- Http1.0:请求一次,连接自动断开
- Http1.1:一次连接,可以发起多次请求
HTTP 1.1
还允许客户端不用等待上一次请求结果返回,就可以发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能够区分出每次请求的响应内容,这样也显著地减少了整个下载过程所需要的时间
- http2.0:
- 多路复用:HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级
HTTP/2
复用TCP
连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了”队头堵塞”
- 数据压缩:HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快
- 服务器推送:意思是说,当我们对支持HTTP2.0的web server请求数据的时候,服务器会顺便把一些客户端需要的资源一起推送到客户端,免得客户端再次创建连接发送请求到服务器端获取。这种方式非常合适加载静态资源
- 多路复用:HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级
5.2 TCP和UDP区别
- tcp udp区别,什么是队头阻塞;技术博客;什么是队头阻塞以及如何解决 ;TCP和UDP的区别
- 区别
- TCP是面向连接的,UDP是面向无连
- TCP是可靠的,UDP是不可靠的
- TCP是面向字节流的,UDP是面向报文的
- TCP只有一对一的传输方式,而UDP不仅可以一对一,还可以一对多,多对多
- UDP的头部开销小,TCP的头部开销大
- TCP会产生粘包问题,UDP会产生丢包问题
- 队头阻塞
- TCP协议下每个数据包都有唯一的序号,协议能够保证这些包的可靠、按顺序传输。但如果多个包中某个序号比较靠前的在传输过程中发生了丢包,其它序号靠后的包即使已顺利到达,也需要在接收方的缓冲区中等待,直到队头包重传成功才能将报文组装并返回给应用层。
- TCP中的队头阻塞的产生是由TCP自身的实现机制决定的,无法避免。想要在应用程序当中避免TCP队头阻塞带来的影响,只有舍弃TCP协议。
- 区别
5.3 流量控制
流量控制实际上是控制发送方的数据流量;流量控制功能并不是数据链路层独有的;各层的流量控制对象是在相应层的实体之间进行的
5.4 TCP的三次握手
TCP报文段结构:
- 序号字段、确认号字段,被TCP发送方和接收方用来实现可靠数据传输服务
- 接收窗口字段:用于流量控制,指示接收方愿意接受的字节数量
- 首部长度字段:指示了以32比特的字为单位的TCP首部长度;由于TCP选项字段的原因,TCP首部的长度是可变的
- 选项字段:用于发送方与接收方协商最大报文段长度时,或在高速网络环境下用作窗口调节因子时使用
- 标志字段:
- ACK比特:用于指示字段中的值是有效的,即该报文段包括一个对已被成功接收报文段的确认
- RST、SYN、FIN比特:用于连接建立和拆除
- CWR、ECE比特:在明确拥塞通告中使用
- PSH比特:指示接收方应立即将数据交给上层
- URG比特:指示报文段里存在着被发送端的上层实体置为“紧急”的数据
三次握手:
- 第一步:客户端TCP首先向服务端TCP发送一个特殊的报文,该报文不包含应用层数据。首部一个标志位(SYN)置为1,且会随机选择一个初始序号。
- 第二步:IP数据报达到服务器主机,服务器会从该数据报中提取TCP SYN报文段,为该TCP连接分配TCP缓存和变量,并向该客户TCP发送允许连接的报文段,此报文段包含三个信息:SYN比特置为1;确认号字段被置为client_isn+1;服务器选择自己的初始序号,放入序号字段中。
- 第三步:收到SYNACK报文段后,客户也要给该连接分配缓存和变量。客户端向服务器发送另一个报文段,对服务器的允许连接的报文段进行了确认:将server_isn+1放置到TCP报文段首部的确认字段;连接已经确认了,将SYN比特置为0。此阶段可以在报文段负载中携带客户到服务器的数据
四次挥手:
- 第一步:客户TCP向服务器进程发送一个TCP报文段,其首部中的一个标志位FIN置为1
- 第二步:服务器收到该报文段后,向发送发会送一个确认报文段
- 第三步:服务器发送自己的终止报文段,FIN置为1 (发送终止报文段之前,服务器可能还要数据要发送给客户端,等服务器确认自己已经没有数据返回给客户端之后,就可以发送终止报文段了)
- 第四步:客户对服务器的终止报文段进行确认。此时,两台主机上用于该连接的所有资源都被释放了
其他问题:
- 序列号为什么是随机的?以及序列号是怎么生成的?
- 一方面为了安全性(随机ISN能避免非同一网络的攻击),另一方面可以让通信双方能够根据序号将「不属于」本连接的报文段丢弃
- 最后一个确认报文段:
- 停止等待协议,是所谓安全的保证,也就意味着一端发送信息,另一端其实是必须要给予接受的回应。也就意味着tcp 一次信息的发送的确认是2次握手。
5.5 DNS
DNS解析域名的过程
-
客户机先去查找本机的/etc/hosts 文件,看文件中是否存在该域名和IP地址的映射记录。如果有就调用,没有就进行下一步。
-
客户机请求本地域名服务器(LDNS)来解析这个域名,主机要求本地域名服务器直接返回最终结果。在返回结果之前,客户机将完全处于等待状态,不再二次请求。统一由本地域名服务器向各级域名服务器转发请求。
-
本地域名服务器收到客户机的请求后,先查询自己的缓存信息,如果有这个域名的映射记录则返回结果,没有则进行下一步。
-
本地域名服务器请求根域名服务器解析这个域名,根域名告诉本地域名服务器去找对应的一级域名服务器。
-
本地域名服务器请求一级域名服务器解析这个域名,一级域名服务器告诉它去找对应的二级域名服务器。
-
本地域名服务器请求二级域名服务器解析这个域名,二级域名服务器告诉它去找对应的子域名服务器。
-
本地域名服务器请求子域名服务器解析这个域名,子域名服务器返回对应的IP地址。
-
本地域名服务器将IP地址记录到缓存中,并返回给客户机。客户机根据收到的IP地址访问该网站。
5.6
5.7 HTTPS
使用HTTPS就绝对安全了吗?、HTTPS 一定是安全的吗?
HTTP特点:
- 内容加密:结合对称加密和非对称加密,中间这无法直接查看明文内容
- 验证身份:通过证书认证客户端访问的是自己的服务器
- 保证数据完整性:防止传输的内容被中间人冒充或篡改
CA证书:
-
CA是证书的签发机构,它是公钥基础设施(Public Key Infrastructure,PKI)的核心。CA是负责签发证书、认证证书、管理已颁发证书的机关。有这样一个权威机构来签发证书,就确保了证书的可信性(合法性)。
浏览器会对服务器返回SSL证书进行验证:
- 验证域名、有效期等信息是否正确;
- 判断证书来源是否合法:每份签发证书都可以根据验证链查找到对应的根证书,操作系统、浏览器会在本地存储权威机构的根证书,利用本地根证书可以对对应机构签发证书完成来源验证;
- 判断证书是否被篡改:需要与CA服务器进行校验;
- 判断证书是否已吊销,可用于第3步中,以减少与CA服务器的交互,提高验证效率。
上述条件完全满足时,才说明该证书合法。
HTTPS一定安全吗?
- https通信如果检测出不合法的证书时,会进行风险提示,用户仍然可以授权信任证书继续操作,此时如果出现中间人攻击,https就不是安全的了。
如何劫持 HTTPS 请求。 比如你需要抓 HTTPS 的包,怎么做?
- 可以自己签发证书,在客户端安装自己的根证书,这个证书就被浏览器信任,然后作为中间人劫持https请求。
6. Vue
6.1 双向绑定原理
问的比较少,基本没有问语法的,大厂基本都是react,vue最多问一下双向绑定原理;
观察者模式:
发布订阅模式:
数据响应式原理:通过数据的改变去驱动 DOM 视图的变化。
双向数据绑定:双向绑定除了数据驱动 DOM 之外, DOM 的变化反过来影响数据,是一个双向的关系。
- 什么是双向绑定:
- 数据层(Model):应用的数据及业务逻辑;
- 视图层(View):应用的展示效果,各类UI组件
- 业务逻辑层(ViewModel):框架封装的核心,它负责将数据与视图关联起来
- 上述分层的构架方案为MVVM,此控制层的核心功能便是“数据双向绑定”
- 单向绑定就是当用js代码更新model时,view会自动更新;双向绑定就是,在单向绑定的基础上,用户更新了view,model数据也自动更新
- 响应式原理(数据驱动视图):
- 由两个主要部分组成:
- 监听器(Observer):劫持并监听所有属性(在此完成getter和setter操作);vue2采用Object.defineProperty()(是一个一个劫持),vue3采用Proxy()(不用每一个属性都劫持,是对整个对象劫持)
- 解析器(Complier):进行视图处理的
- 实现步骤:
- 实现一个监听器Observer:对数据对象进行遍历,包括子属性对象的属性,利用Object.defineProperty()对属性都加上setter和getter;这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
- 实现一个解析器Compile:解析Vue模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图
- 实现一个订阅者Watcher:
- 实现一个订阅器Dep:
- 由两个主要部分组成:
- 视图驱动数据:无外乎是表单元素的处理。v-model可以把响应数据绑定在文本框中,监听文本框内容的改变
- v-model是如何实现的,语法糖实际是什么?
- 作用在表单元素上:动态绑定了input的value指向了message变量,并且在触发input事件的时候去动态把message设置为目标值
- 作用在组件上:在自定义组件中,v-model默认会利用名为value的prop和名为input的事件。本质上是父子组件通信的语法糖,通过prop和$.emit实现。
- v-model是如何实现的,语法糖实际是什么?
6.2 webpack打包
-
webpack打包,plugin babel loader区别及用法,一般是简历上写了才会问。 loader与plugin 区别
-
loader:
- 由于webpack 本身只能打包commonjs规范的js文件,所以,针对css,图片等格式的文件没法打包,就需要引入第三方的模块进行打包。loader虽然是扩展了 webpack ,但是它只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译。
- css-loader和style-loader模块是为了打包css的
- babel-loader和babel-core模块是为了把ES6的代码转成ES5
- url-loader和file-loader是把图片进行打包的。
-
plugin:
-
plugin完成的是loader不能完成的功能。也是为了扩展webpack的功能,但是 plugin 是作用于webpack本身上的。而且plugin不仅只局限在打包,资源的加载上,它的功能要更加丰富。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。
-
html-webpack-plugin:不但完成了html文件的拷贝,打包,还给html中自动增加了引入打包后的js文件的代码
-
clean-webpack-plugin 插件:可以在每次打包发布时自动清理掉 dist 目录中的旧文件
-
-
6.3 MVVM、MVC、MVP的区别
6.4 怎么学习vue
- 还有可能问到你平时是怎么学习vue的。
6.5 插槽slot
-
slot是什么?有什么作用?原理是什么?
-
插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的内容的占位符。
-
作用:可分为匿名插槽、具名插槽、作用域插槽
-
原理:当子组件实例化时,获取到父组件传入的slot标签的内容。当组件执行渲染函数的时候,遇到slot标签,使用$slot中的内容进行替换,此时可以为插槽传递数据。
-
6.6 不同的历史记录模式
路由相关,history和hash的区别(常问),仔细了解原理和用法;history路由和hash的区别 ;单页应用到底该用hash模式还是history模式;哈希路由(hash模式)和历史路由(history模式)的区别 ;单页面应用history路由实现原理 ;深入了解前端路由 hash 与 history 差异
hash模式
import VueRouter from 'vue-router'
const router = new VueRouter({
mode: 'hash',
routes: [
//...
],
})
在 url 地址末尾加入 #/
,之后对应的是 hash 值。vue-router默认hash模式
原理:vue-router通过hashChange() 事件监听hash值的变化, 根据路由表对应的hash值,在“路由视图容器”中渲染不同的组件
优点:
- 只需要前端配置路由表,不需要后端的参与,hash值改变不会向后端发送请求,完全属于前端路由;
- 兼容性好,浏览器都能支持;
缺点:hash值前面需要加#,不符合url规范且不美观;不符合SEO
history模式(HTML5模式)
import VueRouter from 'vue-router'
const router = new VueRouter({
mode: 'history',
routes: [
//...
],
})
原理:运用了浏览器的历史记录栈,之前有back,forward,go方法,之后在HTML5中新增了pushState()和replaceState()方法,它们提供了对历史记录进行修改的功能,不过在进行修改时,虽然改变了当前的URL,但是浏览器不会马上向后端发送请求。
优点:
- 符合url地址规范,美观
- 可以在url里放参数,还可以将数据存放在一个特定的对象中,而hash传参基于url有体积的限制。
缺点:
- 在用户手动输入地址或刷新页面时会发起url请求,后端需配置index.html页面(若不配置,匹配不到静态资源时会出现404错误)
- 兼容性差,pushState()等方法吧需要特定浏览器的支持
注:要根据权限生成不同的路由时就使用history模式
6.7 组件信息通讯
1.父传子——props属性传递:在父组件调用子组件,想让子组件中具备不同的信息
-
直接写在组件标签上的 就叫做传给当前子组件的属性
<my-vote title="张三" :supnum="10"></my-vote>
2.子改父——发布订阅模式:子组件的某些操作,要修改父组件中的一些信息
- 基于vue自带的发布订阅实现;this. o n ( x x x , f u n c t i o n ( ) . . . ) 自定义事件名,并且向事件池中加入方法( @ x x x = " f u n c t i o n ( ) . . . " ); t h i s . on(xxx,function(){...}) 自定义事件名,并且向事件池中加入方法(@xxx="function(){...}"); this. on(xxx,function()...)自定义事件名,并且向事件池中加入方法(@xxx="function()...");this.emit(xxx,params…) 通知指定自定义事件池中的方法执行
- 操作方法:
- 1.在父组件中定义一个方法,这个方法可以修改父组件的状态信息
- 2.我们需要把这个方法放入到事件池中(每一个vm实例有一个自己的事件池)(因为我们要在子组件中操作这个方法,所以这个方法需要放入到子组件的事件池中)
- 3.只有这样我们在子组件中,可以根据某些操作,控制事件池中的方法执行
3.EventBus——利用EventBus(事件总线)进行整体管控:完全的发布订阅模式
-
原理:不论父子还是兄弟,还是不相关的组件,只要保证每个组件中都可以获取到这个事件池即可
-
操作与子改父类似;需要创建总的事件池(vm实例)
//在每个组件中,加入方法到事件池。let EventBus = new Vue; created() { EventBus.$on('xxx', this.handleMain); }
4.祖先与后代——provide和inject:执行上下文方案,让所有需要通信的组件共同具备一个组件元素,这样就可以基于provide和inject实现祖先和后代之间的通信
- 原理:把需要通信的"公共信息"放在祖先元素上管控,以及需要修改这些“公共信息”的方法也在祖先元素上,后代元素想要使用,直接基于inject注册使用即可
- 操作步骤:祖先组件基于provide注册需要供后代组件使用的数据和方法;后代基于inject声明需要使用的数据并调取使用
5.ref/parent/children
-
ref:若ref在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例,基于此可以快速获取和操作子组件中的数据
<vote-main ref='AAA'></vote-main> mounted(){ console.log(this.$refs.AAA); }
-
children:获取当前父组件所有子组件的实例;this.$children是一个数组集合,需要记住组件顺序才可以
-
parent:在子组件中使用,获取其所在父组件的实例;this.$parent;
6.vuex:vue中实现公共状态管理的插件——前提是SPA单页面
- 步骤:创造一个vuex实例
let store = new Vuex.Store({配置项})
;把创造的实例注入到根组件中new Vue({el:'#app, store:store,})
,注入完成后,每个组件都多了个$store属性 - 配置项:
- state(存放组件间需要共享的公共状态);
- mutations(存储用来更改state中数据的同步方法,
this.$store.commit('muatations中的方法名',传过去的参数)
); - getters(可以对state中的成员加工后传递给外界,理解为vuex的计算属性);
- actions(直接在mutation方法中进行异步操作会引起数据失效,故提供了actions专门进行异步操作,通过dispatch调用,在actions里的函数,一般都会再去触发mutations里的方法);
- modules(使用单一状态树,会导致store对象臃肿不堪,vuex允许将store分割到模块,每个模块有自己的state、mutation等)
6.8 v-if和v-show、v-if和v-for
v-if和v-show有什么区别?使用场景是什么?
- 相同点:作用效果都能控制元素在页面是否显示,用法也相同
- 区别:
- 控制手段不同:v-if显示隐藏是将dom元素整个添加或删除;v-show隐藏则是为该元素添加css–display:none,dom元素依旧还在
- 编译过程不同:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换
- 编译条件不同:v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听和子组件适当地被销毁和重建。只有渲染条件为假时,不做操作,直到为真才渲染
- 生命周期:v-if由false变为true时,触发组件地beforeCreate、create、beforeMount、mounted钩子,由true变为false时触发组件地beforeDestory、destoryed方法;v-show由false变为true的时候不会触发组件的生命周期
- 性能消耗:v-if有更高的切换消耗;v-show有更高的初始消耗
- 使用场景:都能控制dom元素在页面的显示,v-if开销更大,如果需要频繁切换,使用v-show比较好;如果运行时条件很少改变,则使用v-if好
v-if和v-for不建议一起使用?
- 作用:
- v-if:用于条件性地渲染一块内容,在指令地表达式返回true时才会渲染
- v-for:基于一个数组来渲染一个列表
v-for="item in items"
,建议设key值,且每个key独一无二,便于diff算法进行优化
- 优先级:v-for优先级比v-if高
- 注意事项:
- 不要把v-if和v-show同时用在同一个元素上,会带来性能地浪费(每次渲染都会先循环再进行条件判断)
- 想要避免此情况,可以在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环
- 如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项
6.9 VueRouter
导航守卫
-
全局守卫(定义在全局的router对象上的)
- 全局前置守卫beforeEach():to、from、next
- 全局解析守卫beforeResolve():所有组件内守卫和异步路由组件被解析之后触发
- 全局后置钩子afterEach():发生在路由跳转完成后。不接受next函数,也不会改变导航本身
-
路由守卫
- beforeEnter:只有在进入路由时触发,在beforeEach之后紧随执行,不会在
params
、query
或hash
改变时触发。
- beforeEnter:只有在进入路由时触发,在beforeEach之后紧随执行,不会在
-
组件守卫:
-
BeforeRouteEnter:路由进入组件之前调用,该钩子在全局守卫
beforeEach
和路由守卫beforeEnter
之后,全局beforeResolve
和全局afterEach
之前调用。参数包括to
,from
,next
-
beforeRouteUpdate:
-
beforeRouteLeave:
-
6.10 指令、事件
自定义指令
Vue.directive('mymodel', {
//只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
bind(el, binding, vnode, oldVnode) {
},
//被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中),需要父节点dom时使用这个钩子
inserted(el, binding, vnode, oldVnode) {
},
//所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
update(el, binding, vnode, oldVnode) {
},
//指令所在组件的 VNode 及其子 VNode 全部更新后调用。
componentUpdated(el, binding, vnode, oldVnode) {
},
//只调用一次,指令与元素解绑时调用。
unbind(el, binding, vnode, oldVnode) {
},
})
事件
6.11 生命周期
注:
- 哪些阶段可以监听到数据的变化:(created、beforeMount、mounted ?)
7. 项目
根据大家自己写的项目问,没有固定问题。
可能问:
1.前后端怎么沟通接口
- 你在项目中承担了什么工作,前后端怎么沟通接口?
2.如何发生请求
-
前端如何发送请求,涉及ajax(axios)知识;
- jQuery提供的
$.ajax()
封装好了 Ajax 请求
$.ajax({ // 设置请求类型 type: 'get', // 设置请求地址 url: 'http://www.example.com', // 设置请求参数 data: { name: 'zhangsan', age: '20' }, // 设置请求参数类型 contentType: 'application/x-www-form-urlencoded', // 设置请求发送前的要做的事情 beforeSend: function () { // 阻断请求的发送 return false }, // 请求成功后要做的事情 success: function (response) {}, // 请求出现错误要做的事情 error: function (xhr) {} });
- Axios 是专注于网络数据请求的库。相比于原生的 XMLHttpRequest 对象,axios 简单易用。相比于 jQuery,axios 更加轻量化,只专注于网络数据请求。
- jQuery提供的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-unN5skac-1665332674771)(C:\Users\sissiz\AppData\Roaming\Typora\typora-user-images\image-20220411192215668.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6JqClGZM-1665332674772)(C:\Users\sissiz\AppData\Roaming\Typora\typora-user-images\image-20220411192429721.png)]
8. 手写代码
8.1 防抖节流
防抖:
-
概念 :n秒后再执行该事件,若在n秒内被重复触发,则重新计时(将多次执行变为最后一次执行)
-
代码:
<body> <button id="debounce">点我防抖!</button> <script> window.onload = function() { // 1、获取这个按钮,并绑定事件 var myDebounce = document.getElementById("debounce"); myDebounce.addEventListener("click", debounce(sayDebounce)); } // 2、防抖功能函数,接受传参 function debounce(fn) { // 4、创建一个标记用来存放定时器的返回值 let timeout = null; return function() { // 5、每次当用户点击/输入的时候,把前一个定时器清除 clearTimeout(timeout); // 6、然后创建一个新的 setTimeout,这样就能保证点击按钮后的 interval 间隔内 // 如果用户还点击了的话,就不会执行 fn 函数 timeout = setTimeout(() => { fn.call(this, arguments); }, 1000); }; } // 3、需要进行防抖的事件处理 function sayDebounce() { // ... 有些需要防抖的工作,在这里执行 console.log("防抖成功!"); } </script> </body>
-
应用场景:搜索框输入请求,用户在不断输入值时,用防抖来节约请求资源;调整窗口大小时,resize次数过于频繁;手机号、邮箱验证输入检测
节流:
-
概念:n秒内只运行一次,若在n秒内重复触发,只有一次生效(将多次执行变为在规定时间内执行一次)
-
代码:
<body> <button id="throttle">点我节流!</button> <script> window.onload = function() { // 1、获取按钮,绑定点击事件 var myThrottle = document.getElementById("throttle"); myThrottle.addEventListener("click", throttle(sayThrottle)); } // 2、节流函数体 function throttle(fn) { // 4、通过闭包保存一个标记 let canRun = true; return function() { // 5、在函数开头判断标志是否为 true,不为 true 则中断函数 if(!canRun) { return; } // 6、将 canRun 设置为 false,防止执行之前再被执行 canRun = false; // 7、定时器 setTimeout( () => { fn.call(this, arguments); // 8、执行完事件(比如调用完接口)之后,重新将这个标志设置为 true canRun = true; }, 1000); }; } // 3、需要节流的事件 function sayThrottle() { console.log("节流成功!"); } </script> </body>
-
应用场景:懒加载要监听计算滚动条的位置,使用节流按一定时间的频率获取;搜索框,搜索联想功能
-
promise(更多是基于promise写一些功能实现), promise.all,promise.race手写 ;手写Promise看着一篇就足够了
-
ajax请求手写(封装成基于promise的); 简洁全面的“手写AJAX,以及用Promise封装AJAX请求“,给你一个满分答案
-
数据类型判断;
-
手写new操作符;
-
防抖节流函数手写;手写防抖与节流
-
解析url中的参数;
9. 面试技巧
参考b站后台管理项目
9.1 项目开发流程
- 为什么面试要问项目流程?
- 了解你之前所在公司背景(是否为一个成熟的科技公司)
- 了解你在项目中所扮演的角色
- 项目的生命周期
- 项目规划:
- 公司决策高层对项目进行可行性分析,决定是否为项目投入时间、金钱和资源
- 需求调研
- 产品根据业务梳理流程与功能点整理需求文档(PRD)
- 项目开发
- 需求宣讲,需求评审,定下项目里程碑,任务分解;
- 开发人员功能开发(完成后提测,将代码提交到对应的环境);
- 测试进行功能测试,性能测试,开发人员改bug;
- 项目上线
- 迭代总结
- 项目规划:
- 软件开发常见模式
- 传统开发
- 敏捷性开发
- 传统开发
- 面试官从项目经历能看出什么?
- 项目背景(可以了解公司的当前发展现状和你自己技术复杂度)
- 职责和项目重难点:
- 职责:你是框架搭建人?骨干输出?还是参与者?
- 项目难点:你在开发过程中遇到过哪些难点?你是如何解决的?
9.2 面试简历
- 简历准备
- 准备简历模板
- 准备个人信息
- 准备专业技能
- 两个大坑 杂乱 和 精
- 工作经历
- 项目经历
- 合格的项目应该怎么写
- 教育背景
- 几点注意事项
- 总结
- 前端发展
9.3 常规问题:
面试最后,你还有什么问题要问面试官的吗?
- 问公司的产品情况(这个岗位具体做什么业务)、发展规模、人员结构(部门有多少前端后端等,是否有资深的人能带新员工)、工作氛围等
- 问问面试的部门和岗位所使用的技术栈和技能要求(用什么框架?需要会后端?)
- 双方心照不宣知道面试结果很差的情况下,可以问面试官结果以及给你的一些建议
- 问面试官,您觉得这个岗位,新人应该朝那几个方向努力呢?
10. 算法与数据结构
10.1.数据结构
顺序表:需要大量访问元素而少量增加和删除的程序
链表:需要大量增加和删除元素操作而对访问元素无要求的程序
栈:数的任意进制转换,括号匹配检验,表达式求职,二叉树遍历
队列:利用先进先出的特性,解决具体业务场景,如消息通知、订单处理、异步处理等
哈希表:
- hashMap:适合查找性能要求高,数据元素之间无逻辑关系的情况
- hashSet:
10.2 排序算法
https://juejin.cn/post/6844903444365443080
前端该如何准备数据结构和算法? - 掘金 (juejin.cn)
选择排序:每次排序取一个最大或最小的数字放到前面的有序序列中。
插入排序:将左侧序列看成一个有序序列,每次将一个数字插入该有序序列。插入时,从有序序列最右侧开始比较,若比较的数较大,后移一位。(打扑克牌)
冒泡排序:循环数组,比较当前元素和下一个元素,如果当前元素比下一个元素大,向上冒泡。下一次循环继续上面的操作,不循环已经排序好的数。
快速排序:选择一个目标值,比目标值小的放左边,比目标值大的放右边,目标值的位置已排好,将左右两侧再进行快排。
归并排序:将大序列二分成小序列,将小序列排序后再将排序后的小序列归并成大序列。
堆排序:创建一个大顶堆,大顶堆的堆顶一定是最大的元素。交换第一个元素和最后一个元素,让剩余的元素继续调整为大顶堆。从后往前以此和第一个元素交换并重新构建,排序完成。
希尔排序:1959年Shell发明; 第一个突破O(n^2)的排序算法;是简单插入排序的改进版;它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序
10.3 leetcode算法题:
- 机器人路径(动规问题,理解动态规划思想);
- 数组相关(主要是会使用splice、slice等js方法);
- 链表相关(反转链表);
- 还是需要多刷题,具体关于刷题的建议可以参考我上个视频中关于面试准备的部分
11. ES6
11.1 ES6有哪些东西
ES6, 全称ECMAScript 6.0 ,是 JavaScript 的下一个版本标准
ES6的新特性:
- 新增了快捷作用域(let,const)
- 提供了定义类的语法糖(class)
- 新增了一种基本数据类型(Symbol)
- 新增了变量的解构赋值
- 函数参数允许设置默认值,引入了rest参数,新增了箭头函数
- 数组新增了一些API,如isArray、from、of方法;数组实例新增了entries()、keys()、values()等方法
- 对象和数组新增了扩展运算符
- 新增了模块化(import、export)
- 新增了Set和Map数据结构
- 原生提供Proxy构造函数,用来生成Proxy实例
- 新增了生成器(Generator)和遍历器(Iterator)
11.2 let、var、const
let const var的区别(作用域相关考点,变量提升相关考点); lyyo_cd的博客-CSDN博客
-
var和let的区别:①var是ES5的,let是ES6的;②作用域不同,var支持全局作用域(或者说函数作用域),③而let支持块级作用域;var有变量提升,而let没有;④var可以重复声明,但let不可以
-
let和const的区别:let可以重新赋值,而const不可以
-
备注:什么是变量提升——JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。变量可以在使用后声明,也就是变量可以先使用再声明。
- var声明变量存在变量提升,let和const不存在变量提升
- let、const都是块级局部变量
- const 的特性和 let 完全一样,不同的只是,const声明时候必须赋值;只能进行一次赋值,即声明后不能再修改;如果声明的是复合类型数据,可以修改其属性
- 同一作用域下let和const不能声明同名变量,而var可以
11.3 ES6新语法
- 知不知道ES6有哪些新语法;小北lulu的博客-CSDN博客
- 解决原有语法上的一些问题或不足(比如let,const)
- var 存在变量提升,let 不存在;let在同一个作用域下不可以重复定义同一个变量值,而var可以;有严格的作用域,var属于函数作用域,let属于块级作用域
- const 声明的变量为只读的,一旦声明,常量的值就不能改变;声明的变量一定要初始化,不能只声明不赋值;声明的变量只在块级作用域内有效
- 对原有语法进行增强,更加易用(比如解构,展开,参数默认值,模板字符串)
- 数组的解构
- 对象的解构
- 模板字符串:用反引号来包裹起来字符串: ``;支持换行;${}中可以使用函数表达式,返回最终值
- 字符串的扩展方法:startsWith() 判断是否以某个字符开头;endsWith()判断是否以某个字符结尾;includes()判断是否包含某个字符
- 参数默认值
- 展开运算符(…):合并数组;代替apply;浅拷贝扩展运算符的作用以及使用场景
- 箭头函数:箭头函数不会改变this的指向
- 对象字面量
- Object.assign():用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性覆盖前面的属性;还可用于对象的浅拷贝
- Proxy:在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截
- Reflect用法:为操作对象而提供的新API,将Object对象的属于语言内部的方法放到Reflect对象上,即从Reflect对象上拿Object对象内部方法,比较方便,可读性更强。
- 全新的对象,全新的方法,全新的功能(promise)
- 全新的数据类型和数据结构(symbol,Set,Map)
- 解决原有语法上的一些问题或不足(比如let,const)
11.4 ES6模块化
- ES6模块化的基本使用(常问,有哪些使用方式,怎么引入/导出,有什么区别);博客
- 定义:模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
- 优势:防止命名冲突;代码复用;高维护性
- 方法一(模块化语法):主要由两个命令构成:
export
和import
- 模块导出数据语法:单个导出/合并导出/默认导出
- 模块导入数据语法:通用导入/解构赋值导入/简便方式导入,针对默认暴露
- 方法二:将文件导入都写进一个 app.js 文件中,然后在里面写入要导入的模块,在 index.html 中引入 app.js 文件内容。
- 两种方法的区别
11.5 箭头函数
- 箭头函数和普通函数的区别;少油少盐不要辣的博客-CSDN博客
- 1.this(执行上下文)指向:
- 箭头函数没有自己的this,只会在自己作用域的上一层继承this(指向外部非箭头函数的this)
- 2.构造函数:
- 不能当作构造函数去用,会报错
- 3.arguments对象
- 普通函数内部,arguments为特殊的类数组对象。包含了函数被调用时的参数列表。箭头函数内部是没有arguments对象,同this一样,依赖于外部非箭头函数。
- 4.隐式return
- 普通函数用return去返回一个函数的结果,无return语句或者return后面无表达式时,则返回undefined。若箭头函数仅有一个表达式,那么该表达式的结果会被隐式返回。
- 5.当作为回调方法去使用时
- 6.注意点
- 1.this(执行上下文)指向:
11.6 遍历器、生成器
Iterator 和 for…of 循环 - ECMAScript 6入门 (ruanyifeng.com)
生成器
Generator 函数是一个普通函数,但是有两个特征。
function
关键字与函数名之间有一个星号;- 函数体内部使用
yield
表达式,定义不同的内部状态(yield
在英语里的意思就是“产出”)
注:
- 调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的
next
方法,就会返回一个有着value
和done
两个属性的对象。value
属性表示当前的内部状态的值,是yield
表达式后面那个表达式的值;done
属性是一个布尔值,表示是否遍历结束。
12. 操作系统
12.1 对操作系统的理解,其核心概念
操作系统是什么:是一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序(相当于软件管家)
核心概念:
- 进程(线程):进程(线程)是操作系统对CPU的抽象
- 虚拟内存(地址空间):虚拟内存是操作系统对物理内存的抽象
- 文件:文件是操作系统对物理磁盘的抽象
- shell:它是一个程序,可从键盘获取命令并将其提供给操作系统以执行。
- GUI :是一种用户界面,允许用户通过图形图标和音频指示符与电子设备进行交互
- 计算机架构(computer architecture): 在计算机工程中,计算机体系结构是描述计算机系统功能,组织和实现的一组规则和方法。它主要包括指令集、内存管理、I/O 和总线结构
- 多处理系统(Computer multitasking):是指计算机同时运行多个程序的能力
- 程序计数器(Program counter):程序计数器 是一个 CPU 中的寄存器,用于指示计算机在其程序序列中的位置
- 多线程(multithreading):是指从软件或者硬件上实现多个线程并发执行的技术
- CPU 核心(core):它是 CPU 的大脑,它接收指令,并执行计算或运算以满足这些指令。一个 CPU 可以有多个内核
- 图形处理器(Graphics Processing Unit):又称显示核心、视觉处理器、显示芯片或绘图芯片
- 缓存命中(cache hit):当应用程序或软件请求数据时,会首先发生缓存命中
- RAM((Random Access Memory):随机存取存储器,也叫主存,是与 CPU 直接交换数据的内部存储器
- ROM (Read Only Memory):只读存储器是一种半导体存储器,其特性是一旦存储数据就无法改变或删除
- 虚拟地址(virtual memory): 虚拟内存是计算机系统内存管理的一种机制
- 驱动程序(device driver):设备驱动程序,简称驱动程序(driver),是一个允许高级别电脑软件与硬件交互的程序
- USB(Universal Serial Bus):是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范
- 地址空间(address space):地址空间是内存中可供程序或进程使用的有效地址范
- 进程间通信(interprocess communication): 指至少两个进程或线程间传送数据或信号的一些技术或方法
- 目录(directory): 在计算机或相关设备中,一个目录或文件夹就是一个装有数字文件系统的虚拟容器
- 路径(path name): 路径是一种电脑文件或目录的名称的通用表现形式,它指向文件系统上的一个唯一位置。
- 根目录(root directory):根目录指的就是计算机系统中的顶层目录,比如 Windows 中的 C 盘和 D 盘,Linux 中的 /
- 工作目录(Working directory):它是一个计算机用语。用户在操作系统内所在的目录,用户可在此目录之下,用相对文件名访问文件。
- 文件描述符(file descriptor): 文件描述符是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念
- 客户端(clients):客户端是访问服务器提供的服务的计算机硬件或软件。
- 服务端(servers): 在计算中,服务器是为其他程序或设备提供功能的计算机程序或设备
12.2 进程和线程
进程:是对正在运行中的程序的一个抽象,是系统进行资源分配和调度的基本单位。一般由三部分组成(程序、数据集合、进程控制块)
线程:线程是操作系统能够进行运算调度的最小单位,其是进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,可以运行多个线程,这些线程共享一块内存,线程之间可以共享对象、资源,如果有冲突或需要协调,还可以随时沟通以解决冲突或保持同步。多线程在宏观上是并行的,在微观上是分时切换串行的
区别:
- 本质区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
- 在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小
- 所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
- 内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源
- 包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程
13. Webpack
13.1 支持的模块
ES6、CommonJS、AMD
14. Node
14.1 框架
express
koa
egg
nest
14.2 事件循环机制
ps:
…的用法;
apply的用法;
ES6模块化导入两种方法的区别
- 继承的各种方法手写
手写发布订阅模式
观察者模式
深拷贝和浅拷贝