一、HTML+CSS
1.讲一下盒模型,普通盒模型和怪异盒模型有什么区别?
盒模型:CSS 把每个元素视为一个盒子,每个盒子包括分为内容(content)、填充
(padding)、边框(border)、边界(margin)四个部分。盒模型是 CSS 布局的基本单元。
W3C 标准盒模型(content-box):(属性 width,height 只包含内容 content,不包含
border 和 padding。)
IE 盒模型(也称怪异盒模型)(border-box):(属性 width,height 包含 content、
border 和 padding)
2.块元素和行内元素区别是什么?常见块元素和行内元素有
哪些?
1)块级元素,宽度默认是它容器的 100%,各占据一行,垂直方向排列;行内元素,都是
同一行,水平方向排列;
2)块级元素,能容纳其他块元素或者行内元素;行内元素,只能容纳文本或其他行内
元素;
3)块级元素中 height,line-height 以及 margin 和 padding 都可以控制;行内元素设置
width、 height 无效(可以设置 line-height),margin、padding 上下无效,
常见的行元素:a – 链接、span - 常用内联容器,br 、
常见的块级元素:div、ul ,li 非排序列表、form - 交互表单、h1 -h6 标题、p - 段落
行内块元素:img - 图片 input - 输入框、textarea,
3.HTML 语义化标签有哪些?
通过使用恰当语义的 HTML 标签,让页面具有良好的结构与含义.
header 网页的头部
main 网页的主体部分(只有一个 main)
nav 网页的导航
section 一个独立的区块
aside 和主体相关的其他内容(侧边栏)
article 独立的文章
footer 网页的底部
4.伪类和伪元素的区别是什么?
伪类(pseudo-class)是一个以冒号作为前缀,被添加到一个选择器末尾的关键字,
样式被呈现到指定的元素时。如:(:last-child),通过在元素选择器上加入伪类改变元素
状态。
伪元素是以两个冒号作为前缀,用于创建一些不在文档树中的元素,并为其添加样式。
通过对元素的操作进行对元素的改变。
5.CSS 如何实现垂直居中?
1)设定行高(line-height):设定行高是垂直居中最简单的方式,适用于“单行”的“行内
元素,行内元素的上下都加上行高的 1/2,所以就垂直居中
2)添加伪元素:
.div0::before{
content:'';
width:0;
height:100%;
display:inline-block;
position:relative;
vertical-align:middle;/* vertical-align 针对行内元素,div 是 block*/
background:#f00;
}
3)calc 属性动态计算:top: calc(“50%的外框高度- 50%的 div 高度”
至于为什么不用 margin-top,因为 margin 相对的是水平宽度
4)使用表格或假装表格
5)
Transform:
position:relative;
top:50%
transform:translateY(-50%);
6)绝对定位
7)用 Flexbox
6.水平方向布局
一个元素在父元素中,水平布局满足一下等式:( margin-left+border-left+padding
left+width+padding-right+border-right+margin-right=父元素内容区的 width)
7 .垂直方向布局
如果子元素超过了父元素大小,则会溢出使用 overflow 属性设置父元素如何处理溢出
overflow:overflow-x/overflow-y:
visible(默认值) 会溢出,在父元素外部显示
hidden 溢出的内容会被裁剪,不会显示
scroll 生成滚动条
auto 根据需要生成滚动条
8、CSS 常见的选择器有哪些?
1)元素选择器:元素{}
2)id 选择器:#id{}
3)类选择器:.class{}
4)复合选择器
选择器 1 选择器 2....(交集)
选择器 1,选择器 2,...(并集)
5)关系选择器
5.1 子元素选择器:父元素>子元素{}
5.2 后代选择器:祖先元素 后代元素{}
5.3 兄弟选择器:前一个+后一个{}(只会选择一个)
前一个~后面{}(前面的兄弟不会被选中)
6)属性选择器
[属性名] 选择含有指定属性的元素
[属性名=属性值] 选择含有指定属性和指定值的元素
7)伪类选择器(带一个:)
first-child:第一个子元素
last-child :最后一个子元素
nth-child():选中第 n 个 2n/even 偶数个 2n-1/odd 奇数
first-of-type
last-of-type
not()
8)超链接的伪类
:hover 鼠标移入
:link 表示一个没访问过的链接
:visited 表示一个访问过的链接,只能改变颜色
:active 鼠标点击
9)伪元素选择器(特殊的位置)
::after 元素的结束位置
::before 元素的开始位置
9..CSS 的优先级如何计算?
对于每一条 css 规则,生成四元组(内联,id,class,元素)
!important 无穷大
style 属性 1,0,0,0
id 选择器 0,1,0,0
类、伪类、结构伪类、属性选择器 0,0,1,0
伪元素选择器 0,0,0,1
通配符选择器 0,0,0,0
继承的样式 没有权重
10 长度单位 px、em 和 rem,rpx,vw,vh 的区别是什么?
px:相对长度单位。像素 px 是相对于显示器屏幕分辨率而言的。
em :相对与父元素的 font-size 来计算的
rem:相对于根元素 html 的 font-size
rpx:小程序独有的,
论大小屏幕,
规定屏幕宽为 750rpx
vw:相对于浏览器视口宽度
vh:相对于浏览器视口高度
11.讲一下 flex 弹性盒布局?
弹性容器
1)display:
flex:块级弹性容器
inline-flex:行内的弹性容器(不会独占一行)
2)flex-direction:设置主轴的方向
row:默认值,水平排列
主轴:右——>左
row-reverse:反向水平排列
主轴:左——>右
column:垂直排列
主轴:上——>下
column-reverse:反向垂直排列
主轴:下——>上
主轴:弹性元素的排列方向
侧轴:与主轴垂直
3)flex-wrap:弹性元素是否自动换行
nowrap:不换行
wrap:换行
wrap-reverse:辅轴反方向换行
4)flex-flow:flex-direction 和 flex-wrap 的简写,无顺序要求
5)jusitify-content:主轴上的元素如何排列
flex-start:沿主轴开始
flex-end:沿主轴终边
center:元素居中排列
space-around:空白分布在元素两侧
space-evenly:空白分布在元素单侧
space-between:空白均匀分布到元素两侧
6)align-items:元素在辅轴上如何对齐
strench:默认值,将同一行的元素设置成相同高度
flex-start:元素不会拉伸,沿辅轴起边对齐
flex-end:元素不会拉伸,沿辅轴终边对齐
center:垂直居中
baseline:基线对齐
7)align-content:辅轴空白的如何分配
flex-start:沿主轴开始
flex-end:沿主轴终边
center:元素居中排列
space-around:空白分布在元素两侧
space-evenly:空白分布在元素单侧
space-between:空白均匀分布到元素两侧
弹性元素
弹性容器的子元素,是弹性元素,一个元素可以同时是弹性容器,元素
1)flex-grow:
指定弹性元素的伸长系数,默认是 0
父元素的剩余空间,按比例分配
2)flex-shrink:
指定弹性元素的收缩系数,默认时 1
父元素的空间容纳不下时,对子元素进行收缩
3)align-self:用来覆盖 align-items
4)flex-basis:元素在主轴上基础长度
5)flex:增长 缩减 基础长度
initial:0 1 auto
auto:1 1 auto
none:0 0 auto
6)order:元素的排列顺序
12.float:设置浮动
float:none /left/right 在其父元素内, 设置浮动后,水平等式不需要强制满足
浮动的特点:
1)完全从文档流脱离,不再占用文档流的位置,下边的元素会向上移动
2)通过浮动,可以使一个元素向其父元素的左侧或右侧移动,不会超过其他浮动元
素,不会从父元素中移出
3)如果上边是一个无浮动的元素,浮动元素无法上移
4 )浮动元素不会盖文字,文字会环绕图片
脱离文档流特点
--块元素不再独占一行 宽 高被内容撑 开
--行内元素可以设置宽 高
13.清除浮动有 3 种办法:
1)在浮动元素后面增加一个元素,设置 clear: both;
2)父元素设置伪元素,给伪元素设置 clear: both,设置 clear 的元素不受前面浮动元
素影响,位置会放在浮动元素后面,因此可以把父元素撑开。
3)父元素设置 overflow: hidden。overflow: hidden 让父元素形成一个 BFC,所以可以
清除浮动。
13.浮动塌陷问题解决方法是什么?(bug)
高度塌陷:元素的高度默认是被子元素撑开的,但子元素设置浮动后,脱离文档流,导
致父元素高度丢失,导致下面元素向上移,导致页面混乱
开启 BFC:
1)设置浮动 flaot=left/right
2)display: flex/inline-flex/
3)overflow:hidden/auto/scroll
4)position:absolute/fixed
/* 解决外边距重叠和高度塌陷 */
clearfix::before,
clearfix::after{
content: '';
display: table;
clear: both;
}
14.BFC、IFC 是什么?
BFC(Block Formatting Context)块级格式化环境
IFC (Inline Formatting Context) 内敛格式化上下文。
隐含属性,开启 BFC 后,变成独立的布局区域
特点:
1)开启 BFC 元素后,不会被浮动元素覆盖
2)子与父元素外边距不会重叠
3)可以包含浮动的子元素
15.position 属性的值有哪些?各个值是什么含义?
position : left+margin-left+border-left+padding-left+width+padding-right+border
right+margin-right+right=父元素内容区的 width
static:默认值
relative:相对定位
1) 参照自身在文档流中的位置
2) 会提升元素层级
3)不会脱离文档流
4)不会改变元素性质
5)不设置偏移量,没有变化
absolute:绝对定位
1)不设置偏移量,位置没有变化
2)脱离文档流
3)会改变元素性质
4)会提升元素层级
5)开启了定位的祖先元素,没有则是根元素
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: o;
margin: auto; //实现水平垂直居中
fixed:固定定位
1)不设置偏移量,位置没有变化
2)脱离文档流
3)会改变元素性质
4)会提升元素层级
5)参考于浏览器视口
sticky:粘滞定位
16.隐藏元素的三种方法
1)display:none 元素在页面上将消失,不占据页面空间,会导致浏览器的回流与
重绘,不能响应交互类事件;
2)visible:hidden 元素在页面占据的空间不变,所以它只会导致浏览器重绘而不会回
流,也不能响应交互类事件;
3)opacity:0 元素在页面占据的空间不变,不会导致浏览器回流或重绘,可以响应
交互类事件。
17.HTML5 新增内容
1)语义化标签
2)增强型表单
常用 placeholder:输入框默认提示,在用户输入后消失。
autofocus 属性,是一个 boolean 属性。规定在页面加载时,域自动地获得焦点。
3) websocker
4) localstorage 和 sessionstorage
5)canvas 绘图
6)拖拽
7)地理位置
window.navigator.geolocation
8)DOM 扩展
9)媒体元素
video audio
18.BOM 和 DOM 的区别
BOM 是浏览器对象模型:提供了独立于内容与浏览器窗口进行交互的方法和接口
DOM 文档对象模型:处理网页内容的方法和接口
DOM 的最根本的对象是 BOM 的 window 对象的子对象
19. window.location
url = window.location.href; /* 获取完整 URL */
/* http://127.0.0.1:8020/Test/index.html#test?name=test */
url = window.location.pathname; /* 获取文件路径(文件地址) */
/* /Test/index.html */
url = window.location.protocol; /* 获取协议 */
/* http */
url = window.location.host; /* 获取主机地址和端口号 */
/* http://127.0.0.1:8020/ */
url = window.location.hostname; /* 获取主机地址 */
/* http://127.0.0.1/ */
url = window.location.port; /* 获取端口号 */
/* 8020 */
url = window.location.hash; /* 获取锚点(“#”后面的分段) */
/* #test?name=test */
url = window.location.search; /* 获取属性(“?”后面的分段) */
/* 如果需要 URL 中的某一部分,可以自己进行处理 */
url = window.location.pathname;
url = url.substring(url.lastIndexOf('/') + 1, url.length);
/* /index.html */
20. window.navigation
navigation.appcodename :返回浏览器的代码名
navigation.appname:
返回浏览器的名称
navigation,appVersion:
返回浏览器的平台和版本信息
navigation.platform:
返回运行浏览器的操作系统平台
navigation.userAgent:
返回由客户机发送服务器的
21.herf 和 src 的区别
href
标识超文本引用,用在
link
和
a
等元素上,
href
是引用和页面关联,是在当前
元素和引用资源之间建立联系
src
表示引用资源,表示替换当前元素,用在
img
,
script
,
iframe
上
22.link 和@import 的区别
1)从属关系:link 是 html 的标签,不仅可以加载 CSS 文件,还可以定义 RSS、rel
连接属性等;而@import 是 css 的语法,只有导入样式表的作用.
2)加载顺序:页面被加载时,link 会和 html 同时被加载而;@import 引入的 CSS
将在页面加载完毕后被加载。
3)兼容性:@import 是 CSS2.1 才有的语法,所以只能在 IE5 以上 才能识别;而
link 是 HTML 标签,所以不存在兼容性问题。
4)DOM:javascript 只能控制 dom 去改变 link 标签引入的样式,而@import 的样式
不是 dom 可以控制的。
5)link 方式的样式权重高于@import 的权重。
23. css 画一个三角形
.box1{
width: 0;
height: 0;
border: 10px solid ;
border-color: red transparent transparent transparent;
}
24.实现三栏布局
左右两边宽度固定,中间自适应
<div class="container">
<div class="left">Left</div>
<div class="right">Right</div>
<div class="main">Main</div>
</div>
1)position 和 margin
/*左右进行绝对定位*/
.left,.right{ position: absolute;
height:100%;
top: 0;
background: #ff69b4;
}
.left{
left: 0;
width: 100px;
}
.right{
right: 0;
width: 200px;
}
/*中间用 margin 空出左右元素所占的空间*/
.main{
height:100%;
margin: 0 100px 0 200px;
background: #659;
}
2)float 和 margin
/*左边栏左浮动*/
.left{
float:left;
height:100%;
width:100px;
background:#ff69b4;
}
/*中间栏自适应*/
.main{
height:100%;
margin:0 200px 0 100px;
background: #659;
}
/*右边栏右浮动*/
.right{
float:right;
height:100%;
width:200px;
background:#ff69b4;
}
3)flex
.container{
display: flex;
} .left{
width:200px;
background: red;
}
.main{
flex: 1;
background: blue;
}
.right{
width:200px;
background: red;
}
4)gird
.container{
display: grid;
width: 100%;
grid-template-rows: 100px; /*设置行高*/
grid-template-columns: 100px auto 200px; /*设置列数属性
*/
}
.left{
background: red;
}
.main{
background: blue;
}
.right{
background:red;
}
5)圣杯布局
<div class="container">
<div class="main col">Main</div>
<div class="left col">Left</div>
<div class="right col">Right</div>
</div>
/* 两边定宽,中间自适用 */
body,html,.container{
height: 100%;
padding:0;
margin: 0;
}
.col{
float: left; /* 三个 col 都设置 float: left,为了把 left 和
right 定位到左右部分 */ position:relative;
}
/*父元素空出左右栏位子: 因为上一步中,左右栏定位成功了,但是中间
栏的内容会被遮盖住*/
.container{
padding:0 200px 0 100px;
}
/*左边栏*/
.left{
left:-100px;
width: 100px;
height:100%;
margin-left: -100%;
background: #ff69b4;
}
/*中间栏*/
.main{
width:100%;
height: 100%;
background: #659;
}
/*右边栏*/
.right{
right:-200px;
width:200px;
height:100%;
margin-left: -200px;
background: #ff69b4;
}
25.transition(过渡)
transition:属性 时间 延迟时间 时序
transition-property: 执行过渡的属性 属性之间用“,”隔开,所有属性可以用 all
transition-duration:执行过渡的时间
transition-delay: 过渡延迟,等待一段时间再过渡
transition-timing-function:过渡的时序方式
贝塞尔曲线:cubic-bezier()
ease 默认值 慢-块-慢
linear 匀速运动
ease-in 慢-快 加速运动
ease-out 快-慢 减速运动
ease-in-out 加速-减速
step(2,end/start) 分步执行
26.animation(动画)
animation-name: 动画名字 ;
animation-duration: 执行时间 ;
animation-delay: 延迟执行时间 ;
animation-timing-function: 执行的时序;
animation-iteration-count: 动画执行的次数; infinite 无限次
animation-direction:执行的方向
normal
reverse 反方向
alternate 重复执行时反向执行
alternate-reverse 重复执行时反向执行
animation-play-state: 动画执行的状态 running; paused;
animation-fill-mode:填充方式
none 结束回到 from 位置
forwards 结束回到 to 位置
backwards 动画延时等待时,就会到 from 位置
both 结合了 forwards backwards
关键帧 @keyframes run(动画的名字){
from{}
to{}
}
26.transform(变形)
通过 css 改变元素的形状和位置,不会脱离文档流,不会影响页面布局
transform-origin 指定变形的原点
1)transform:平移 平移元素是相对于自己来定位的
translateX() translateY() translateZ():元素和人眼之间的距离,近大远小
2)transform:旋转
rotateX() rotateY() rotateZ()
deg 度数 turn 圈
3)transform:缩放
scale() 双方向的缩放
scaleX() 水平缩放
scaleY() 垂直缩放
27.水平垂直居中
1)flex
2)positiong
3)transform
28.文字省略
1)单行
white-space: nowrap;
overflow: hidden; text-overflow: ellipsis;
2)多行
overflow:hidden
text-overflow:ellipsis
display:-webkit-box
-webkit-line-lamp:2 第几行
-webkit-box-ovient:verficat
29.实现边框 0.5PX
1)渐变
原理:高度 1px,背景渐变,一半有颜色,一半透明。
.border-gradient:after {
content: " ";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background-image: linear-gradient(0deg, #f00 50%,
transparent 50%);
}
2)缩放
.border-scale:after{
content: " ";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background-color: #f00;
/* 如 果 不 用 background-color, 使 用 border-top:1px
solid #f00; 效果是一样的*
transform:scaleY(.5);
}
二、JavaScript
1.谈谈对原型链的理解。
1)原型对象:每一个函数在创建时都会被赋予一个 prototype 属性,它指向函数的原
型对象,这个对象可以包含所有实例共享的属性和函数。
2)原型链:对象的每个实例都具有一个___proto__属性,指向的是构造函数的原型对
象,而原型对象同样存在一个_proto__属性指向上一级构造函数的原型对象,就这样层层
往上,直到最上层某个原型对象为 null。
3)是 null 而不是 object.prototype
原型链上的所有节点都是对象,不能是字符串、数字、布尔值等原始类型。规范要
求原型链必须是有限长度的。你没法访问 null 的属性,所以起到了终止原型链的作用;另
一方面,null 在某种意义上也是一种对象,即空对象,因为 null 一开始就是为表示一个“空”
的对象存在的
2. js 有哪些数据类型?
1)基本数据类型 Number String Boolean Null undefined
2)引用数据类型 Array object
3. js 有哪些判断类型的方法?
1)typeof:用于一般变量返回的类型都是字符串形式,
2)instanceof:后面一定要是对象类型
3)Object.prototype.toString.call():适用于所有了类型
4.ES5 如何实现继承?
原型链继承/构造继承/复制继承/组合继承/寄生组合继承
4.1 原型链继承
son.prototype=new Father()
son.constructor=son
优点:
1.简单,容易实现
2 继承关系纯粹:生成的实例即是子类的实例,也是父类的实例。
3.可以通过子类直接访问父类原型链属性和函数
缺点:
1.子类的所有实例共享父类的属性,如果父类中有引用类型,某个子类改变值会
引起其他子类中的值发生变化。
2.在创建子类实现时,无法向父类的构造函数传递参数
3.无法实现多继承
4.2 构造继承
是在子类的构造函数中通过 call 函数改变 this 的指向,调用父类的构造函数,从而能
将父类的实例的属性和函数绑定到子类的 this 上.
function Son(name){
Animal.call(this);
this.name=name
}
优点:
1.可解决子类实例的共享父类属性的问题:call 函数实际是改变了父类构造函数中
this 的指向,
2.创建子类的实例时,可以向父类传递参数
3.可以实现多继承
缺点:
1.实例只是子类的实例,并不是父类的实例。
2 只能继承父类实例的属性和函数。并不能继承原型对象上的属性和函数。
3.无法复用父类的实例函数,子类生成的每个实例都会拥有父类实例函数的引用,
这回造成不必要的内存消耗。影响性能。
4.3 复制继承
首先生成父类的实例,然后通过 for ..in 遍历父类实例的属性和函数,并将其依次设置
为子类实例的属性和函数或者原型对象上的属性和函数
优点:
1.支持多继承
2.能同时继承父类实例的属性和函数与原型对象上的属性和函数
3.可以向父类构造函数中传递值
缺点:
1.父类的所有属性都需要复制,消耗内存
2.实例只是子类的实例,不是父类的实例
4.4 组合继承
组合了构造继承和原型继承两种方法,一方面在子类的构造函数中通过 call)函数调用
父类的构造函数,将父类的实例的属性和函数绑定到子类的 this 中,另一方面,通过改变
子类的 prototype 属性,继承父类的原型对象上的属性和函数。
优点:
1.既能继承父类实例的属性和函数,又能继承原型对象的属性和函数。
2.即是子类的实例,又是父类的实例。
3.不存在引用属性共亨的问题
4.可以向父类的构造函数中传递参数:call 函数可以向父类的构造函数中传递参数。
4.5 寄生组合继承
为了解决父类的构造函数被调用两次的问题,
inheritPrototype(SubType, SuperType);
5.ES5 和 ES6 继承区别
ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到 this 上
(Parent.apply(this)).
ES6 的继承机制完全不同,实质上是先创建父类的实例对象 this(所以必须先调用
父类的 super()方法),然后再用子类的构造函数修改 this。
ES5 的继承时通过原型或构造函数机制来实现。
ES6 通过 class 关键字定义类,里面有构造方法,类之间通过 extends 关键字实现
继承。子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因为子类
没有自己的 this 对象,而是继承了父类的 this 对象,然后对其进行加工。如果不调用
super 方法,子类得不到 this 对象。
6.如何判断一个变量是否数组?
1)instanceof arr instanceof Array
2)constructor arr.constructor === Array
3)Array.isArray()
4)Object.prototype.toString.call() Object.prototype.toString.call(arr) === '[object
Array]'
5)arr.__proto__===)Array.prototype
6)Object.getPrototypeOf() Object.getPrototypeOf(arr) === Array.prototype
7.call bind apply 的区别?
1)bind 返回对应函数, 便于稍后调用; apply, call 则是立即调用。
2)call 和 bind 传递的参数列表,bind 也可以分多次传入,而 apply 传递数组
call 源码
Function.prototype.call = function(ctx) {
ctx = ctx || window // 判断 context 是否存在,存在则为 context
不存在则赋予全局作用域
ctx.fn = this // 谁调用这个函数,this 就指向谁
var arg = [] // 存储参数
for(let i =0; i<arguments.length;i++) { // 将所有的参数都 push
到 arg 里面
arg.push('arguments[' + i + ']')
}
var result = ctx.fn(...array)
delete ctx.fn // 删除属性
return result // 返回 result
}
8.防抖节流的概念?实现防抖和节流。
防抖在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时,重新
出发定时器。search 搜索联想,用户在不断输入值时,用防抖来节约请求资源。
function debounce(fn,denay,immediately=false){
let timer =null;// //使用闭包,缓存变量
var this=_this;
const args=arguments;
return function(){
if(timer) clearInterval(timer);
if(immediately){
let callNow=!timer;
timer=setTimeout(()=>{
timer=null;
},denay);
if(callNow){
fn.apply(this,args) }
}else{
timer=setTimeout(()=>{
fn.apply(this,args);
},denay)
}
}
}
节流:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函
数,只有一次生效。鼠标不断点击触发,mousedown(单位时间内只触发一次)监听滚动事
件,比如是否滑到底部自动加载更多,用 throttle 来判断
function throttle(fn,denay,immediately=false){
let timer=0;//使用闭包,缓存变量
const args=arguments;
var this=_this
return function(){
if(!timer){
time=Date.now();
if(immediately){
fn.apply(_this,args)
}
return ;
}
if(Date.now()-time>denay){
fn.apply(this,args);
time=Date.now();
}
}
}
节流是为了限制函数的执行次数,而防抖是为了限制函数的执行时机。
9.深拷贝、浅拷贝的区别?如何实现深拷贝和浅拷贝?
1)浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属
性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址 ,
所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
实现方法:Object.assign() concat() 扩展运算符
2)深拷贝:不管原数据中值是什么类型的数据,拷贝后的新数据跟原数据是相互独立,
没有关联的。
实现方法:利用 json 数据和 json 字符串之间的转换 Number, String, Boolean, Array,
扁平对象,即那些能够被 json 直接表示的数据结构
。
JSON 缺点:
1
..取不到值为 undefined 的键
2.NaN 和 无 穷 转 变 为 null
3. 无法获取自己原型链上的内容,只能获取 Object 原型内容
4.date 对象转为 date 字符串
递归
function deepClone(obj){
// 判断当前对象是对象还是数组
let result = Array.isArray(obj) ? [] : --{};
if(obj && typeof obj === "object"){
for(let key in obj){
// 判断是否为自身属性
if(obj.hasOwnProperty(key)){
if(obj[key] && typeof obj[key] === "object"){
//子元素为对象,进入递归
result[key] = deepClone(obj[key]);
}else{
result[key] = obj[key];
}
}
}
}
return result;
}
10.对比一下 var、const、let。
1)var 是 ES5 提出的,let 和 const 是 ES6 提出的。
2)var 声明的变量存在变量提升现象,let 和 const 声明的变量不存在变量提升现象
(遵循:“先声明,后使用”的原则,否则会报错)。
3)var 允许重复声明同一个变量,let 和 const 在同一作用域下不允许重复声明同一个
变量。
4)var 声明的变量不存在块级作用域(是函数级作用域),let 和 const 声明的变量存
在块级作用域并且声明的变量只在所声明的块级作用域内有效。
5)var 不存在暂时性死区,let 和 const 存在暂时性死区。(let/const 命令会使区块形
成 封 闭 的 作 用 域 。 若 在 声 明 之 前 使 用 变 量 , 就 会 报 错 。
总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的)
6)var 声明的变量在 window 上,而 let 和 const 不在
11.ES6 新特性有哪些?
1)新增关键字 let const
2)箭头函数:this 是静态的,始终指向函数声明所在作用域下的 this 的值
3)函数默认参数值(function sum(a,b=5){ return a+b}
4) 运算符…(分散、合并)
5)对象词法扩展(简写变量,忽略 function 关键字)
6)支持二进制,八进制字面量
7)解构赋值
8)对象超类
9)模板语法
10)for 0f(遍历值) 和 for in(遍历键)
11)Map 和 WeakMap 一个对象由多个 key-val 对构成,在 Map 中,任何类型都可以作
为对象的 key()
12)set 和 SetMap :Set 对象是一组不重复的值,重复的值将被忽略,值类型可以是
原始类型和引用类型:
13)类
14)Symbol:新数据类型,他的值唯一,不可变
15)迭代器 iterator
16)Gnerators
17)Promise
18)模块:export import
12.箭头函数和普通函数区别是什么?
1)箭头函数都是匿名函数
2)箭头函数不能用于构造函数,不能使用 new
3)this 的指向不同:在普通函数中,this 总是指向调用它的对象,如果用作构造函数,
this 指向创建的对象实例。箭头函数中的 this 是静态的,this 的指向永远是声明时所在作用
域下的 this 的值,它并没有自己的 this,call apply bind 也没有办法改变 this 的指向
4)箭头函数不具有 prototype 原型对象。
5)箭头函数不绑定 arguments,取而代之用 rest 参数…解决
13.使用 new 创建对象的过程是什么样的?
1
)在内存中创建一个新的空对象。
2)让 this 指向这个新的对象。
3 )执行构造函数里面的代码,给这个新对象添加属性和方法。
4
)返回这个新对象(所以构造函数里面不需要 return )。
14.this 指向系列问题。
,this 的意思为“这个;当前”,是一个指针型变量,它动态指向当前函数的运行环境。
1)箭头函数中:this->声明时所在作用域下的 this
2)在全局作用域中:this->winsdow
3)在普通函数中:this->调用者
4)事件绑定中:this->事件源
5)定时器:this->window
6)构造函数中:this->实例化对象
15.谈谈对闭包的理解?什么是闭包?闭包有哪些应用场景?
闭包有什么缺点?如何避免闭包?
1)什么是闭包
闭包是一个函数加上创建函数的函数的作用域的连接,闭包关闭了函数的自由变量
function fun(){
var a=10;
return function fuu(){
console.log(a)//a 是自由变量
}
}
2)闭包的应用场景
封装功能时,函数防抖、函数节流、函数柯里化、给元素伪数组添加事件需要使用元
素的索引值。
3)闭包的缺点
比普通函数更占用内存,会导致网页性能变差,在 IE 下容易造成内存泄露。
4)闭包优点
访问其他函数内部变量,延长变量的生命周期,避免定义全局变量所造成的污染
5)如何避免闭包引起的内存泄漏
在退出函数之前,将不使用的局部变量赋值为 null
16.JS 作用域
1)什么是作用域
是在运行时代码中某些特定部分的变量、函数、和对象的可访问性。作用域决定
了代码区块中变量和其它资源的可见性,分为局部作用域和全局作用域
2)自由变量:
当前作用域没有定义的变量,值向父级作用域寻找。要到创建函数的的那个域去找。
创建,不是调用。在 A 中作用域要用到的变量 x,并没有在 A 中声明,要到别的作用域中
找到他,这个变量 x 就是自由变量
3)作用域链:由子级作用域返回父级作用域中寻找变量,就叫做作用域链
17.谈谈对 promise 理解。
1)promise 是为解决异步处理回调金字塔问题而产生的
2)两个特点:Promise 状态不受外界影响,(pending,fulfilled,rejuected);Promise 状
态一旦改变,就不会再变
3)三个缺点:
1.无法取消 Promise,一旦新建它就会立即执行,无法中途取消;
2. 如 果 不 设 置 回 调 函 数 , Promise 内 部 抛 出 的 错 误 , 不 会 反 映 到 外 部
3.当处于 pending 状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将
完成
4)Promise 在哪存放成功回调序列和失败回调序列?
1.onResolvedCallbacks 成功后要执行的回调序列 是一个数组
2.onRejectedCallbacks 失败后要执行的回调序列 是一个数组
5)手写 promise
class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// 成功回调函数数组和失败回调函数数组
this.onResolveCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if(this.state === 'pending'){
this.state = 'fulfilled';
this.value = value;
// 成功的话遍历成功回调函数数组然后执行这些函数
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if(this.state === 'pending'){
this.state = 'rejected';
this.reason = reason;
// 失败的话遍历失败回调函数数组然后执行这些函数
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve,reject)
}catch(err){
reject(err);
}
}
}
Promise.prototype.then=function(onFulfilled, onRejected){
if(this.state === 'fulfilled'){
onFulfilled(this.value);
}
if(this.state === 'rejected'){ onRejected(this.reason);
}
// 当状态为等待态时,我们要将成功/失败的回调函数加入到对应的数组中
if(this.state === 'pending'){
// onFulfilled 传入到成功数组
this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value);
})
// onRejected 传入到失败数组
this.onRejectedCallbacks.push(()=>{
onRejeced(this.reason);
})
}
}
6)手写 Promise.all 方法(返回一个 promise 对象)
Promise2.all = function(arrP) {
let list = []
let len = 0
return new Promise2((resolve, reject) => {
for(let i = 0; i < arrP.length; i++) {
arrP[i].then( data=> {
list[i] = data
len++
if(len === arrP.length) resolve(list)
}, error => {
reject(error)
})
}
})
}
18.Typescript 中 type 和 interface 的区别是什么?
1)接口是通过继承的方式来扩展,类型别名是通过 & 来扩展。
2)接口可以自动合并,而类型别名不行
19.柯里化是什么?有什么用?怎么实现?
1)是什么:把一个多参数的函数转化为单参数函数的方法。
2)作用:
1.惰性求值 (Lazy Evaluation):柯里化收的函数是分步执行的,第一次调用返回的是一
个函数,第二次调用的时候才会进行计算。起到延时计算的作用,通过延时计算求值,称
之为惰性求值。
function sum(a){
return function(b){
return a+b
}
}
2.动态生成函数
function power(n){
return function (number){
let result = 1;
for(let i = 0; i < n; ++i){
result *= number;
}
return result;
}
}
20.CmmonJS 和 ESM 区别?
1)CommonJs: module.exports= {} 导出; require 导入
1.CommonJs 可以动态加载语句,代码发生在运行时
2.CommonJs 混合导出如果使用 exports 导出单个值之后,就不能在导出一个对象值,
3.CommonJs 导出值是拷贝,可以修改导出的值,这在代码出错时,不好排查引起
变量污染
2)Es Module: export default 导出,import 导入
1.Es Module 是静态的,不可以动态加载语句,只能声明在该文件的最顶部,代码发
生在编译时
2.Es Module 混合导出,单个导出,默认导出,完全互不影响
3.Es Module 导出和引用值之前都存在映射关系,并且值都是可读的,不能修改
20.js 垃圾回收机制。
js 内存分为栈内存和堆内存,栈 JavaScript 用于存储静态数据,静态数据是引擎在编
译时知道大小的数据。这包括指向对象和函数的引用和原始值(strings, numbers, booleans,
undefined 和 null);由于 JS 引擎知道大小不会变,所以它们将为每个值分配固定大小的内存。
堆是用于存储 JavaScript 对象和函数。与栈不同,JS 引擎不会为这些对象分配固定大小的
内存空间。栈包含对堆中对象数据的引用。
栈垃圾回收的方式非常简便,当一个函数执行结束之后,JavaScript 引擎销毁该函数保
存在栈中的执行上下文。当函数直接结束,栈空间处理完成了,但是堆空间的数据还是存
储在堆空间中,需要垃圾回收器将堆空间中的垃圾数据回收。在 V8 中会把堆分为新生代
和老生代两个区域,新生代中存放的是生存时间短的对象,老生代中存放的生存时间久的
对象。 新生区中使用 Scavenge 算法,老生区中使用标记-清除算法和标记-整理算法。
Scavenge 算法:
1. 标记:对对象区域中的垃圾进行标记 2. 清除垃圾数据和整理碎片
化内存:副垃圾回收器会把这些存活的对象复制到空闲区域中,并且有序的排列起来,复
制后空闲区域就没有内存碎片了 3. 角色翻转:完成复制后,对象区域与空闲区域进行角色
翻转,也就是原来的对象区域变成空闲区域,原来的空闲区域变成了对象区域
标记-清除算法: 1. 标记:标记阶段就是从一组根元素开始,递归遍历这组根元素,
在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。
2. 清除:将垃圾数据进行清除。 3. 产生内存碎片:对一块内存多次执行标记 - 清除算法后,
会产生大量不连续的内存碎片。而碎片过多会导致大对象无法分配到足够的连续内存。
标记-整理算法 1. 标记:和标记 - 清除的标记过程一样,从一组根元素开始,递归遍
历这组根元素,在这个遍历过程中,能到达的元素标记为活动对象。 2. 整理:让所有存活
的对象都向内存的一端移动 3. 清除:清理掉端边界以外的
21.实现一个发布订阅。
// 定义事件中心类
class MyEvent {
handlers = {} // 存放事件 map,发布者,存放订阅者
// 绑定事件
$on(type, fn) {
if (!Reflect.has(this.handlers, type)) { // 如果没有定义过该事件,
初始化该订阅者列表,Reflect.has 检查一个对象是否含有某个属性 this.handlers[type] = []
}
this.handlers[type].push(fn) // 存放订阅的消息
}
// 触发事件
$emit(type, ...params) {
if (!Reflect.has(this.handlers, type)) { // 如果没有该事件,抛出错
误
throw new Error(`未注册该事件${type}`)
}
this.handlers[type].forEach((fn) => { // 循环事件列表,执行每一个
事件,相当于向订阅者发送消息
fn(...params)
})
}
// 取消订阅
$remove(type, fn) {
if (!Reflect.has(this.handlers, type)) {
throw new Error(`无效事件${type}`)
}
if (!fn) { // 如果没有传入方法,表示需要将该该类型的所有消息取消订阅
return Reflect.deleteProperty(this.handlers, type)
} else {
const inx = this.handlers[type].findIndex((handler) => handler
=== fn)
if (inx === -1) { // 如果该事件不在事件列表中,则抛出错误
throw new Error('无效事件')
}
this.handlers[type].splice(inx, 1) // 从事件列表中删除该事件
if (!this.handlers[type].length) { // 如果该类事件列表中没有事件
了,则删除该类事件
return Reflect.deleteProperty(this.handlers, type)
}
}
}
}
22. EventLoop 事件循环
JS 是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,所以会先将同步代
码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微
任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列。
宏任务:setTimeout setInterval I/O UI 渲染 setimmediate
微任务:process.nextick promise
MutaionObserver
process.nextick
会再下次事件循环的开始前被调用;而 setTimeout 和 setInterval 是在
下一次事件循环队列中
23.浏览器进程
GUI 线程就是渲染页面的,他解析 HTML 和 CSS,然后将他们构建成 DOM 树和渲染
树就是这个线程负责的。
JS 引擎
线程就是负责执行 JS 的主线程,前面说的"JS 是单线程的"就是指的这个线程。
的 Chrome V8 引擎就是在这个线程运行的。
需要注意的是,这个线程跟 GUI 线程是互斥的。
23.如何判断元素是否在视口
1) offsetTop, scrollTop
o
ffsetTop 当前元素顶端距离父元素顶端距离,、scrollTop 当前元素顶端距离窗口顶端
距离 el.offsetTop - e.scrollTop <= viewPortHeight
2)getBoundingClientRect()
bottom: height: left: right: top: width:
top 大于等于 0 left 大于等于 0
bottom 小于等于视窗高度 right 小于等于视窗宽度
3)IntersectionObserver
是以 new 的形式声明一个对象,接收两个参数 callback 和 options
callback 是添加监听后,当监听目标发生滚动变化时触发的回调函数。接收一个参
数 entries,即 IntersectionObserverEntry 实例
options 是一个对象:配置 root rootMargin threshol
d
24.for 0f 和 for in
1)for..of 适用遍历数/数组对象/字符串/map/set 等拥有迭代器对象的集合.但是不能遍
历对象,因为没有迭代器对象.与 forEach()不同的是,它可以正确响应 break、continue 和
return 语句 ,for-of 循环不支持普通对象
2)for-in 可以迭代一个对象的属性,使用 for in 会遍历数组所有的可枚举属性,包括
原型,
25.map,weakmap,set,weakset
介绍下 Set、Map、WeakSet 和 WeakMap - 掘金 (juejin.cn)
map,weakmap,set,weakset ,ES6 新增的一种新的数据结构,
Set 是一种叫做
集合
的数据结构,Map 是一种叫做
字典
的数据结构
1)Set 类似于数组,但成员是唯一且无序的,没有重复的值。常用于数组去重
let set1 = new Set([1, 2, 3])
let set2 = new Set([4, 3, 2])
交 集 ( Union ) : let intersect = new Set([...set1].filter(value =>
set2.has(value)))
并集(Union):let union = new Set([...set1, ...set2])
差集(Difference):let difference = new Set([...set1].filter(value
=> !set2.has(value)))
操作方法
add(value):新增,相当于 array 里的 push
delete(value):存在即删除集合中 value
has(value):判断集合中是否存在 value
clear():清空集合
遍历方法(遍历顺序为插入顺序)
keys():返回一个包含集合中所有键的迭代器
values():返回一个包含集合中所有值得迭代器
entries():返回一个包含 Set 对象中所有元素得键值对迭代器
forEach(callbackFn, thisArg):用于对集合成员执行 callbackFn 操作,如果提供了
thisArg 参数,回调中的 this 会是这个参数,没有返回值
2)WeakSet 结构与 Set 类似,也是不重复的值的集合。 WeakSet
只
能储存对象引用,
不能存放值。
值都是被弱引用的,即垃圾回收机制不考虑 WeakSet 对该对象的应用,如果
没有其他的变量或属性引用这个对象值,则这个对象将会被垃圾回收掉(不考虑该对象还
存在于 WeakSet 中),所以,WeakSet 对象里有多少个成员元素,取决于垃圾回收机制有
没有运行,运行前后成员个数可能不一致,ES6 规定 WeakSet 对象是无法被遍历的
3)JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只
能用字符串当作键。这给它的使用带来了很大的限制。ES6 提供了 Map 数据结构,类似于
对象,也是键值对的集合,但是 “键”的范围不限于字符串,各种类型的值(包括对象)都
可以当作键。Map 的键实际上是跟内存地址绑定的
4)WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合,
WeakMap 只接受
对象作为键名(null 除外),不接受其他类型的值作为键名,WeakMap 的键名所指向的对
象,不计入垃圾回收机制。
,一是
没有遍历操作
(即没有 keys()、values()和 entries()方法),
也
没有 size 属性
。
WeakMap 只有四个方法可用:get()、set()、has()、delete()
。
26.Gnerators
ES6 新增了 Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,从而可以
改变执行流程,或者可以暂停或者延迟执行。有人说他是懒惰的迭代器。使用 Generator
函数和一般函数一样,是在函数名后面加上 (),但是 Generator 函数不会像普通函数那样
马上执行,而是会返回一个指针,这个指针指向对象的内部状态,所以要调用遍历器对象
Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行。为不具备
Iterator 接口的对象提供遍历方法。
27.async 和 await
Promise,用 then 链来解决多层回调问题,但是来连续调用 then,会使得代码很
冗长,代码的可读性不好,所以在 ES7 中就提出了 async 和 await 来解决这一问题,
他其实就是 promise 的语法糖。async 会将其后的函数(函数表达式或 Lambda)的返
回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其结果返回
出来。
28.事件冒泡
DOM 事件流(event
flow
)存在三个阶段:
事件捕获阶段、处于目标阶段、事件冒泡阶
段。
事件捕获
(
event capturing
)
:
当鼠标点击或者触发 dom 事件时,浏览器会从根节
点开始
由外到内
进行事件传播。即点击了子元素,如果父元素对应的事件的话,会先触发
父元素绑定的事件。
事件冒泡
(
dubbed bubbling
)
:
鼠标点击或者触发 dom 事件时,浏览器会从当前节
点是
由内到外
进行事件传播,直到根节点。
29.事件委托
事件委托就是把原本需要绑定在子元素上的事件(onclick、onkeydown 等)委托给它的
父元素,让父元素来监听子元素的冒泡事件,并在子元素发生事件冒泡时找到这个子元素。
30.JS 类型转换
显示转换
Number()pasreInt() pasreFloat()
String() String() toString()
Boolean()
隐式转换
31.01.+02 为什么不等于 0.3
因为浮点数运算的精度问题。在计算机运行过程中,需要将数据转化成二进制,然后
再进行计算。小数转化为 IEEE754 的过程:先转化为二进制的形式,然后再用科学计数法
表示,接着把通过科学计数法表示的二进制数用 IEEE754 标准表示。js 中的 Number 类型
遵循 IEEE754 标准,在 IEEE754 标准的 64 位浮点数相加,因为浮点数自身小数位数的限
制而截断的二进制在转化为十进制,就变成 0.30000000000000004,所以在计算时会产生
误差。
32.{},new object ,object.creat 的区别
字面量和 new 关键字创建的对象是 Object 的实例,原型指向 Object.prototype,继承内置
对象 Object
Object.create(arg, pro)创建的对象的原型取决于 arg,arg 为 null,新对象是空对象,没有
原型,不继承任何对象;arg 为指定对象,新对象的原型指向指定对象,继承指定对象
三、Vue
1. Vuex 的使用方法。
Vuex 是一个专门为 vue.js 设计的集中式状态管理架构
Vuex 的五个核心:state,getter,mutations,actions,module
1)state:定义我们所需要管理的数组、对象、字符串等等
2)getter:可以对 state 中的成员加工后传递给外界
3)mutations:mutations 是操作 state 数据的方法的集合,比如对该数据的修改、增加、
删除等等。
4)action :由于直接在 mutation 方法中进行异步操作,将会引起数据失效。所以提
供了 Actions 来专门进行异步操作,最终提交 mutation 方法。
5)modules: 当项目庞大,状态非常多时,可以采用模块化管理模式。Vuex 允许我们
将 store 分割成
模块(module)
2.mvc 和 mvvm 区别是什么?
1)mvc:model-view-controller, 是一种设计模式,是一种业务逻辑。
M:model(业务模型)
V:view(用户视图)
C:controller(控制器)
2)mvvm:Model–View–ViewModel
3) 区别:它实现了 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不
用再自己手动操作 Dom 元素,来改变 View 的显示,而是改变属性后该属性对应 View 层
显示会自动改变。
3.Vue 双向绑定原理。
实现数据的双向绑定,,通过数据劫持和消息的订阅与发布来实现。首先要对数据进行劫
持监听,所以我们需要设置一个监听器 Observer,用来监听所有属性。如果属性发上变化
了,就需要告诉订阅者 Watcher 看是否需要更新。因为订阅者是有很多个,所以我们需要
有一个消息订阅器 Dep 来专收集和通知订阅者,然后在监听器 Observer 和订阅者 Watcher
之间进行统一管理的。接着,我们还需要有一个指令解析器 Compile,对每个节点元素进
行扫描和解析相关指令(如 v-model,v-on),对应初始化成一个订阅者 Watcher,并替换
模板数据或者绑定相应的函数。此时当订阅者 Watcher 接收到相应属性的变化,就会执行
对应的更新函数,从而更新视图。
因此接下去我们执行以下 3 个步骤,实现数据的双向绑定
(1)实现一个监听器 Observer,用来劫持并监听所有属性,如果有变动的,就通知订
阅者。
(2)实现一个订阅者 Watcher,每一个 Watcher 都绑定一个更新函数,watcher 可以收
到属性的变化通知并执行相应的函数,从而更新视图。
(3)实现一个解析器 Compile,可以扫描和解析每个节点的相关指令(v-model,v-on
等指令),如果节点存在 v-model,v-on 等指令,则解析器 Compile 初始化这类节点的模
板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)。
数据劫持和消息的订阅和发布
1.通过 observer 对所有的属性进行数据劫持监听,核心 object.defineProperty(get、
set),递归
2.HTML 解析,替换 DOM 内数据。解析器 Compile,
3.依赖-收集和通知订阅者
4.watcher
4.Vue 组件间通信方式有哪些?
总结:
父子关系的组件数据传递选择 props 与 $emit 进行传递,也可选择 ref
兄弟关系的组件数据传递可选择$bus,其次可以选择$parent 进行传递
祖先与后代组件数据传递可选择 attrs 与 listeners 或者 Provide 与 Inject
复杂关系的组件数据传递可以通过 vuex 存放共享的变量
1)props:父->子
2)$emit 触发自定义事件:子->父
3)EventBus :$emit/$on:这种方法通过一个空的 Vue 实例作为中央事件总线(事件
中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、
兄弟、跨级
this.$emit(事件名,数据);//发送数据
this.$on(事件名,data => {});//接收数据
4)Vuex
5)使用 ref: 这两种方法的弊端是,无法在跨级或兄弟间通信。
6)$attrs ( v-bind:$attrs ) 与 ( v-on ) $listeners: 祖 先 传 递 数 据 给 子 孙 , (
v
bind="$attrs" v-on="$listeners")
7)Provide 与 Inject: 在祖先组件定义 provide 属性,返回传递的值
在后代组件通过 inject 属性接收组件传递过来的值
8) this.$parent 或 this.$root: 祖先传递数据给子孙
5.computed 和 watch 区别是什么?
计算属性 computed:具有响应式的返回值
1)可以从组件数据中派生出新的数据,最常见的是设置一个函数,返回计算之后的结
果
2)支持缓存,只有依赖数据发生改变,才会重新进行计算
3)不支持异步,当 computed 内有异步操作时无效,无法监听数据的变化
侦听属性 watch:侦测变化,执行回调
1)不支持缓存,数据变,直接会触发相应的操作;
2)watch 没有返回值,但支持异步
选择方案:看是否要派生新值,基本能用计算属性就用计算属性
6.v-for 和 v-if 同时使用有问题吗?
v-for 比 v-if 优先级高,所以使用的话,每次 v-for 都会执行 v-if,造成不必要的计算,
影响性能,尤其是当之需要渲染很小一部分的时候。
7.前端路由原理。
前端不同页面的状态管理器,可以不向后台发送请求而直接通过前端技术实现多个页面的
效果。URL 与 UI 之间的映射关系,这种映射是单向的
1)hash 实现
hash 是 URL 中 hash (#) 及后面的那部分,常用作锚点在页面内进行导航,改变
URL 中的 hash 部分不会引起页面刷新,通过 hashchange 事件监听 URL 的变化,改变
URL 的方式只有这几种:通过浏览器前进后退改变 URL、通过<a>标签改变 URL、通
过 window.location 改变 URL,这几种情况改变 URL 都会触发 hashchange 事件
2)history 实现
history 提供了 pushState 和 replaceState 两个方法,这两个方法改变 URL 的 path
部分不会引起页面刷新,history 提供类似 hashchange 事件的 popstate 事件,但
popstate 事件有些不同:通过浏览器前进后退改变 URL 时会触发 popstate 事件,通
过 pushState/replaceState 或<a>标签改变 URL 不会触发 popstate 事件。好在我们可
以拦截 pushState/replaceState 的调用和<a>标签的点击事件来检测 URL 变化,所以监
听 URL 变化可以实现,只是没有 hashchange 那么方便。
8.比较一下 history 和 hash 这两种路由。
1)history
1.地址干净,美观
2.兼容性比 hash 略差
3.应用部署会出现 404 问题,需要后端解决
2)hash
1.地址中带有#,不美观
2.兼容性较好
3.若将地址通过第三方手机 app 分享,若 app 校验严格,地址会被标记不合法
9. Vue diff 算法。
1)特点
1.比较只会在同层级进行, 不会跨层级比较
2.在 diff 比较的过程中,循环从两边向中间比较
2)方式:递归+双指针
1.判断是否是同一元素,不是同一个元素,直接替换
2.是同一个元素
比对属性->比对儿子(老有儿子,新没有儿子;新的有儿子,老的没有儿子;都是文
本;都有儿子)->双指针比较->对比查找进行复用
得分点 patch、patchVnode、updateChildren、vue 优化时间复杂度为 O(n)
标准回答 Diff 算法比较过程
第一步:patch 函数中对新老节点进行比较 如果新节点不存在就销毁老节点 如果老
节点不存在,直接创建新的节点 当两个节点是相同节点的时候,进入 patctVnode 的过程,
比较两个节点的内部
第二步:patchVnode 函数比较两个虚拟节点内部 如果两个虚拟节点完全相同,返
回 当前 vnode 的 children 不是 textNode,再分成三种情况 - 有新 children,没有旧
children,创建新的 - 没有新 children,有旧 children,删除旧的 - 新 children、旧 children
都有,执行`updateChildren`比较 children 的差异,这里就是 diff 算法的核心 当前 vnode 的
children 是 textNode,直接更新 text
第三步:updateChildren 函数子节点进行比较。
第一步 头头比较。若相似,旧头新头指针后移(即 `oldStartIdx++` &&
`newStartIdx++`),真实 dom 不变,进入下一次循环;不相似,进入第二步。
第 二 步 尾 尾比 较 。 若相 似 , 旧 尾 新 尾 指 针 前 移 ( 即 `oldEndIdx--` &&
`newEndIdx--`),真实 dom 不变,进入下一次循环;不相似,进入第三步。
第三步 头尾比较。若相似,旧头指针后移,新尾指针前移(即 `oldStartIdx++`
&& `newEndIdx--`),未确认 dom 序列中的头移到尾,进入下一次循环;不相似,进入第
四步。
第四步 尾头比较。若相似,旧尾指针前移,新头指针后移(即 `oldEndIdx--`
&& `newStartIdx++`),未确认 dom 序列中的尾移到头,进入下一次循环;不相似,
进入第五步。
第五步 若节点有 key 且在旧子节点数组中找到 sameVnode(tag 和 key 都一
致 ), 则 将 其 dom 移 动 到 当 前 真 实 dom 序 列 的 头 部 , 新 头 指 针 后 移 ( 即
`newStartIdx++`);否则,vnode 对应的 dom(`vnode[newStartIdx].elm`)插入当前真
实 dom 序列的头部,新头指针后移(即 `newStartIdx++`)。
但结束循环后,有两种情况需要考虑: - 新的字节点数组(newCh)被遍历完
(`newStartIdx > newEndIdx`)。那就需要把多余的旧 dom(`oldStartIdx -> oldEndIdx`)
都删除,上述例子中就是`c,d`; - 新的字节点数组(oldCh)被遍历完(`oldStartIdx >
oldEndIdx`)。那就需要把多余的新 dom(`newStartIdx -> newEndIdx`)都添加
10.Vue 的虚拟 DOM 原理,优缺点是什么?
1)原理:
用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
diff 算法 — 比较两棵虚拟 DOM 树的差异;
pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树
2)好处:
1.具备跨平台的优势–由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台
环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等。
2.操作 DOM 慢,js 运行效率高。我们可以将 DOM 对比操作放在 JS 层,提高效率。
运用 patching 算法来计算出真正需要更新的节点,最大限度地减少 DOM 操作,从而显著
提高性能。Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。
3.提升渲染性能 Virtual DOM 的优势不在于单次的操作,而是在大量、频繁的数据更
新下,能够对视图进行合理、高效的更新
11.Vue 的 keep-alive 使用及原理。
keep-alive 是 vue.js 的内置组件,它能够把不活动的组件的实例保存在内存中,而不是直
接的销毁,它是一个抽象组件,不会被渲染到真实 DOM 中,也不会出现在父组件链中。
它提供了 exclude 和 include 两个属性,允许组件有条件的缓存。
1)props
include: patternTypes, // 缓存白名单
exclude: patternTypes, // 缓存黑名单
2)created () {
this.cache = Object.create(null) // 缓存组件
3)destroyed ()// 删除所有的缓存
4)mounted () {// 实时监听黑白名单的变动
5)render()
1.获取 keep-alive 包裹着的第一个子组件对象及其组件名;
2.根据设定的黑白名单(如果有)进行条件匹配,决定是否缓存。不匹配,直接返
回组件实例(VNode),否则执行第三步;
3.根据组件 ID 和 tag 生成缓存 Key,并在缓存对象中查找是否已缓存过该组件实例。
如果存在,直接取出缓存值并更新该 key 在 this.keys 中的位置(更新 key 的位置是实
现 LRU 置换策略的关键),否则执行第四步;
4.在 this.cache 对象中存储该组件实例并保存 key 值,之后检查缓存的实例数量是否
超过 max 的设置值,超过则根据 LRU 置换策略删除最近最久未使用的实例(即是下标
为 0 的那个 key。
5.
将该组件实例的 keepAlive 属性值设置为 true
12.Vue 父子组件生命周期触发顺序是怎样的?
加载渲染过程
父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->
子 beforeMount->子 mounted->父 mounted
父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed
13.vue 生命周期
beforeCreate created
beforeMount mounted
beforeUpdate updated
beforeDestroy destroyed
keep-alive 组件独有
activated deactivated
14.Vue.nextTick 的实现?
vue 中的 nextTick 主要用于处理数据动态变化后,DOM 还未及时更新的问题,用
nextTick 就可以获取数据更新后最新 DOM 的变化。
1)是什么:等待下一次 DOM 更新刷新的工具方法。
在修改数据之后立即使用这个方
法,获取更新后的 DOM。
2)当数据发生变化,Vue 将开启一个异步更新队列,视图需要等队列中所有数据变
化完成之后,再统一进行更新。nextTick 本质是为了利用 JavaScript 的这些异步回调任
务队列来实现 Vue 框架中自己的异步回调队列。
3)使用场景
created()函数中想要用到 DOM
响应式数据变化后获取 DOM 更新后的状态,比如希望获取列表更新后的高度
15.v-if 和 v-show 的区别
(1)v-if 和 v-show 用于视图层进行条件判断视图展示
(2)v-if 的原理是根据判断条件来动态的进行增删 DOM 元素,v-show 是根据判断
条件来动态的进行显示和隐藏元素,频繁的进行增删 DOM 操作会影响页面加载速度
和性能,由此我们可以得出结论:当您的项目程序不是很大的时候,v-if 和 v-show 都
可以用来进行判断展示和隐藏(这种场景使用 v-if 只是影响不大,并不是没有影响);
当您的项目程序比较大的时候,不推荐使用 v-if 来进行判断展示和隐藏,推荐使用 v
show;
(3)只有 v-if 能和 v-else 连用进行分支判断,v-show 是不能和 v-else 连用的,如果
出现多种条件场景的情况下,可以使用 v-if 来进行判断
16.常用 VUE 内置指令
v-on v-if v-else v-show v-for v-bind v-model
1)v-once
v-once 仅渲染指定组件或元素一次,并跳过未来对其的更新。如果编译器发现元
素或者组件上面有 v-once 时,就会将首次的计算结果存入缓存对象,组件再次渲染时
从中获取。
2)v-pre
v-pre 可以用来阻止预编译,有 v-pre 指令的标签内部的内容不会被编译,会原样输
出。
3)v-cloak
v-cloak 来解决屏幕闪动的问题,当网络较慢,网页还在加载 Vue.js ,而导致 Vue 来
不及渲染,这时页面就会显示出 Vue 源代码
17.data 为什么是一个函数
vue 中的组件是被复用的,如果 vue 中的 data 是一个对象类型,对象类型的数据是
按引用传值的,这就会导致所有组件的实例都共享同一份数据,这是不对的,我们要
的是每个组件实例都是独立的。为了解决对象类型数据共享的问题,我们需要
将 data 定义成一个函数,每个实例需要通过调用函数生成一个独立的数据对象
四、工程化
1.webpack 性能优化有哪些方法?
1)JS 代码压缩
2)CSS 代码压缩
3)Html 文件代码压缩
4)文件大小压缩
5)图片压缩
6)Tree Shaking
7)代码分离
2 .webpack 的 loader 和 plugin 区别是什么?常用的 plugin
和 loader 有哪些?
loader 即为文件加载器,操作的是文件,将文件 A 通过 loader 转换成文件 B,是一
个单纯的文件转化过程。
plugin 即为插件,是一个扩展器,丰富 webpack 本身,增强功能,webpack 打包的
整个过程,他并不直接操作文件,而是基于事件机制工作,监听 webpack 打包过程中
的某些节点,执行广泛的任务。
loader: postcss-loader、css-loader、style-loader、scss-loader、less-loader、eslint
loader
plugins: HtmlWebpackPlugin、 clean-webpack-plugin、 mini-css-extract-plugin 、
HMR(HotModuleReplacementPlugin、babel、babel-preset-env
3.webpack 构建流程是什么?
1)初始化参数。获取用户在 webpack.config.js 文件配置的参数
2)开始编译。初始化 compiler 对象,注册所有的插件 plugins,插件开始
监听 webpack 构建过程的生命周期事件,不同环节会有相应的处理,然后开始
执行编译。
3)确定入口。根据 webpack.config.js 文件的 entry 入口,开始解析文件构
建 ast 语法树,找出依赖,递归下去。
4)编译模块。递归过程中,根据文件类型和 loader 配置,调用相应的
loader 对不同的文件做转换处理,再找出该模块依赖的模块,递归本操作,直
到项目中依赖的所有模块都经过了本操作的编译处理。
5)完成编译并输出。递归结束,得到每个文件结果,包含转换后的模块以及
他们之前的依赖关系,根据 entry 以及 output 等配置生成代码块 chunk
6)打包完成。根据 output 输出所有的 chunk 到相应的文件目录
4.tree-shaking 原理。
是一种删除不需要的额外代码的技术优化代码体积,ES6 Module 引入进行
静态分析,故而编译的时候正确判断到底加载了那些模块。静态分析程序流,
判断那些模块和变量未被使用或者引用,进而删除对应代码
五、性能优化
1.前端页面性能如何优化?
网络请求类,webpack 类,css 类,js 类,图片类,渲染类
1) 减少 HTTP 请求。一个完整的 HTTP 请求需要经历 DNS 查找,TCP 握
手,浏览器发出 HTTP 请求,服务器接收请求,服务器处理请求并发回响应,
浏览器接收响应等过程
2)善用缓存,不重复加载相同的资源,http 缓存,本地缓存,keep
alive 缓存
3)静态资源使用 CDN
4)使用 tree-shaking 删除不需要的代码,减少代码体积
5)使用 code-Split 对代码进行分割成多个 js 文件,从而便单个文件体积
更小。
6)并通过 import 动态导入语法进行按需加载,从而达到需要使用时才加
载该资源。不用时不加载资源。
7)压缩文件 JS CSS
8)减少重绘重排
使用 transform 和 opacity 属性更改来实现动画
9) 降低 CSS 选择器的复杂性
10)使用事件委托
事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某
一类型的所有事件。
11) 合理 Web Workers
12)防抖,节流
13)图片懒加载,图片压缩,图片较小时,使用 base64 格式,使用字体图
标 iconfont 代替图片图标,压缩图片
14)使用服务端渲染
客户端渲染: 获取 HTML 文件,根据需要下载 JavaScript 文件,运行文
件,生成 DOM,再渲染。
服务端渲染:服务端返回 HTML 文件,客户端只需解析 HTML
15)将 CSS 放在文件头部,JavaScript 文件放在底部
2.首屏渲染优化
原因:网络延时问题、资源文件体积是否过大、请求资源是否过多、资源是
否重复发送请求去加载了、加载脚本的时候,渲染内容阻塞了
解决办法:减小入口文件体积 压缩图片大小,使用 tree-shaking 删除不必
要的代码,代码分段;使用 UI 框架按需加载 ,图片压缩,图片的懒加载;善
用 http 缓存,本地存储等;合理使用 Web Worker;使用 SSR 渲染,服务端只
需要解析 HTML 文件
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创
建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker
线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果
返回给主线程。
3.回流和重绘的区别
回流:
当 render tree 中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需
要重新构建。这就称为回流(reflow)。
重绘:
当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风
格,而不会影响布局的.
区别:回流一定引起重绘,重绘不一定引起回流。
4.如何减少回流和重绘?
1)CSS 中避免回流、重绘
1.尽可能在 DOM 树的最末端改变尺寸,大小,是否隐藏
2.避免设置多层内联样式
3.动画效果应用到 position 属性为 absolute 或 fixed 的元素上
4.避免使用 table 布局
table
比其他
html
标记占更多的字节、布局比较死板不灵活、会阻挡浏览器渲染
引擎的渲染顺序从而使得加载速度慢
5.使用 transform、opacity 等动画效果不会引起回流重绘
2)JS 操作避免回流、重绘
1.避免使用 JS 一个样式修改完接着改下一个样式,最好一次性更改 CSS 样式,或者
将样式列表定义为 class 的名称
2.避免频繁操作 DOM,使用文档片段创建一个子树,然后再拷贝到文档中
3.先隐藏元素,进行修改后再显示该元素,
4.避免循环读取 offsetLeft 等属性,在循环之前把它们存起来
六、安全
1.网络攻击有哪些?如何防御?
1)XSS 漏洞
xss:中文名称跨站脚本攻击,通常出现在搜索框、留言板、评论区等地方
分类:反射性、存储型、DOM 型
攻击方式:构造恶意链接,诱骗用户点击盗取用户的 cookie 信息
反射性 xss:通常这一类 xss 危害较低,对网站没有什么严重的影响,具体表现在用
户在搜索框输入 xss 语句返回弹框,仅出现一次
存储型 xss:存储型对网站危害较大,用户插入 xss 语句后,恶意语句会存入网站数
据库中,用户访问过程都会出现弹框
DOM 型 xss:利用浏览器的 dom 解析,更不容易被发现,可以更改页面布局
漏洞危害:xss 蠕虫、会话、流量劫持、网站挂马、盗取 cookie
防护方法:对用户输入进行过滤、入参字符过滤、出参字符转义、设置 httponly
2)CSRF 漏洞(Cross-site request forgery)
csrf 漏洞:中文名称跨站请求伪造,攻击者冒充用户身份执行用户操作
漏洞原理:
1、用户登录信任网站在未退出的情况下访问攻击者构造的恶意网站
2、恶意网站发出访问第三方网站的请求,信任网站收到请求以用户信息访问
3、攻击者在用户不知情的情况下冒充用户身份访问成功
危害:盗用用户身份、执行用户操作,修改信息
防护方法:
1、设置 referer 字段;但是并不是所有服务器在任何时候都可以接受 referer
字段,所以这个方法存在一定的局限性
2、设置验证码;在用户操作的过程中设置身份确认的验证码,该方法会很有
用,不过验证码频繁的话会影响用户体验
3、设置 token 值,在用户请求中设置一个随机没有规律的 token 值可以有
效的防止攻击者利用漏洞攻击
区别
xss:跨站脚本攻击、诱骗用户点击恶意链接盗取用户 cookie 进行攻击、不需
要用户进行登录、xss 除了利用 cookie 还可以篡改网页等
csrf:跨站请求伪造、无法获取用户的 cookie 而是直接冒充用户、需要用户登
录后进行操作
七、计算机基础
1.进程与线程区别是什么?
进程是具有独立功能的程序在一个数据集合上运行的过程,是资源分配的独立单位
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实
际运作单位。
区别:进程程是操作系统资源分配的基本单位,线程是任务的调度执行的基本单位。
开销方面:进程都有自己的独立数据空间,程序之间的切换开销大;
2.TCP 三次握手、四次挥手,为什么要三次握手、四次挥手?
通过三次握手确认双方收发功能都正常,四次挥手确认双方都要关闭连接
3.为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
建立连接:因为服务端在 LISTEN 状态下,收到建立连接请求的 SYN 报文后,把
ACK 和 SYN 放在一个报文里发送给客户端。
关闭连接:当收到对方的 FIN 报文时,仅表示对方不再发送数据但还能接收收据,
我们也未必把全部数据都发给了对方,所以我们可以立即 close,也可以发送一些数据
给对方后,再发送 FIN 报文给对方表示同意关闭连接。因此我们的 ACK 和 FIN 一般会
分开发送。
4.TCP 和 UDP 区别是什么?
1)TCP 提供的是面向连接的,UDP 提供的是非面向连接的
2)TCP 提供可靠的服务,通过 TCP 连接传送的数据,无差错、不丢失、不重复,按
序到达;UDP 尽最大努力交付,即不保证可靠交付。
3)TCP 面向字节流;UDP 面向报文。
4)TCP 连接只能是点到点的;UDP 支持一对一、一对多、多对一和多对多的交互通
信。
5)UDP 具有较好的实时性,工作效率比 TCP 高,适用于对高速传输和实时性有较高
的通信或广播通信。
6)TCP 对系统资源要求较多,UDP 对系统资源要求较少。TCP 首部有 20 字节;UDP
的首部只有 8 个字节。
5.TCP/IP 如何保证数据包传输的有序可靠?
通过对字节流分段并进行编号然后通过 ACK 回复和超时重发这两个机制来保证。
1)为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区;并为
每个已发送的数据包启动一个超时定时器;
2)如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以
是对本包后续包的应答),则释放该数据包占用的缓冲区;否则,重传该数据包,直到收
到应答或重传次数超过规定的最大次数为止。
3)接收方收到数据包后,先进行 CRC 校验,如果正确则把数据交给上层协议,然
后给发送方发送一个累计应答包,表明该数据已收到,如果接收方正好也有数据要发
给发送方,应答包也可方在数据包中捎带过去。
6.关于为什么出现粘包问题及如何解决!
1.什么是粘包现象:TCP 粘包是指发送方发送的若干包数据到接收方接收时粘成一包,
从接收缓冲区看,后一包数据的头紧接着前一包数据的尾
2. 发送端原因: 由于 TCP 协议本身的机制(面向连接的可靠地协议-三次握手机制)
客户端与服务器会维持一个连接(Channel),数据在连接不断开的情况下,可以持续
不断地将多个数据包发往服务器,但是如果发送的网络数据包太小,那么他本身会启
用 Nagle 算法(可配置是否启用)对较小的数据包进行合并(基于此,TCP 的网络延
迟要 UDP 的高些)然后再发送(超时或者包大小足够)。那么这样的话,服务器在接
收到消息(数据流)的时候就无法区分哪些数据包是客户端自己分开发送的,这样产
生了粘包.
接收端原因: 服务器在接收到数据包后,放到缓冲区中,如果消息没有被及时从缓存
区取走,下次在取数据的时候可能就会出现一次取出多个数据包的情况,造成粘包现
象。
3.为了避免粘包现象,可采取以下几种措施:
1.对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP 提供了强制数据立
即传送的操作指令 push,TCP 软件收到该操作指令后,就立即将本段数据发送出去,
而不必等待发送缓冲区满;
2.对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收
进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;
3. 两种途径:
1)格式化数据:每条数据有固定的格式(开始符、结束符),这种方法简单易行,
但选择开始符和结束符的时候一定要注意每条数据的内部一定不能出现开始符或结束
符。
2)发送长度:发送每条数据的时候,将数据的长度一并发送,比如可以选择每条数
据的前 4 位是数据的长度,应用层处理时可以根据长度来判断每条数据的开始和结束。
7.七层网络结构
物理层:
解决两个硬件之间怎么通信的问题,常见的物理媒介有光纤、电缆、中继器等。它主
要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。
它的主要作用是传输比特流(就是由 1、0 转化为电流强弱来进行传输,到达目的地后在
转化为 1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。
数据链路层:
在计算机网络中由于各种干扰的存在,物理链路是不可靠的。该层的主要功能就是:
通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路。
它的具体工作是接收来自物理层的位流形式的数据,并封装成帧,传送到上一层;同样,
也将来自上层的数据帧,拆装为位流形式的数据转发到物理层。这一层的数据叫做帧。
网络层:
计算机网络中如果有多台计算机,怎么找到要发的那台?如果中间有多个节点,怎么
选择路径?这就是路由要做的事。该层的主要任务就是:通过路由选择算法,为报文(该
层的数据单位,由上一层数据打包而来)通过通信子网选择最适当的路径。这一层定义的
是 IP 地址,通过 IP 地址寻址,所以产生了 IP 协议。
传输层:
当发送大量数据时,很可能会出现丢包的情况,另一台电脑要告诉是否完整接收到全
部的包。如果缺了,就告诉丢了哪些包,然后再发一次,直至全部接收为止。
简单来说,传输层的主要功能就是:监控数据传输服务的质量,保证报文的正确传输。
会话层:
虽然已经可以实现给正确的计算机,发送正确的封装过后的信息了。但我们总不可能
每次都要调用传输层协议去打包,然后再调用 IP 协议去找路由,所以我们要建立一个自动
收发包,自动寻址的功能。于是会话层出现了:它的作用就是建立和管理应用程序之间的
通信。
表示层:
表示层负责数据格式的转换,将应用处理的信息转换为适合网络传输的格式,或者将
来自下一层的数据转换为上层能处理的格式。
应用层:
应用层是计算机用户,以及各种应用程序和网络之间的接口,其功能是直接向用户提
供服务,完成用户希望在网络上完成的各种工作。前端同学对应用层肯定是最熟悉的。
八、网络通信
1.说说从输入 url 到页面展示出来的整个过程。
1) 输入 url
2)查找缓存:浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,
如果有则显示页面内容。如果没有则进行下一步。
浏览器缓存:浏览器会记录 DNS 一段时间,因此,只是第一个地方解析 DNS 请求;
操作系统缓存:如果在浏览器缓存中不包含这个记录,则会使系统调用操作系统, 获
取操作系统的记录(保存最近的 DNS 查询缓存);
路由器缓存:如果上述两个步骤均不能成功获取 DNS 记录,继续搜索路由器缓存;
ISP 缓存:若上述均失败,继续向 ISP 搜索。
3)DNS 域名解析:浏览器向 DNS 服务器发起请求,解析该 URL 中的域名对应的 IP
地址。DNS 服务器是基于 UDP 的,因此会用到 UDP 协议。
4)建立 TCP 连接:解析出 IP 地址后,根据 IP 地址和默认 80 端口,和服务器建立
TCP 连接
5)发起 HTTP 请求:浏览器发起读取文件的 HTTP 请求,,该请求报文作为 TCP 三次
握手的第三次数据发送给服务器
6)服务器响应请求并返回结果:服务器对浏览器请求做出响应,并把对应的 html 文
件发送给浏览器
7)关闭 TCP 连接:通过四次挥手释放 TCP 连接
8)浏览器渲染:客户端(浏览器)解析 HTML 内容并渲染出来,浏览器接收到数据
包后的解析流程为:
1.构建 DOM 树:词法分析然后解析成 DOM 树(dom tree),是由 dom 元素及属
性节点组成,树的根是 document 对象
2.构建 CSS 规则树:生成 CSS 规则树(CSS Rule Tree)
3.构建 render 树:Web 浏览器将 DOM 和 CSSOM 结合,并构建出渲染树
4.布局(Layout):计算出每个节点在屏幕中的位置
5.绘制(Painting):即遍历 render 树,并使用 UI 后端层绘制每个节点。
2.什么是跨域?为什么会出现跨域?如何解决跨域问题?
跨域:当一个请求 url 的协议、域名、端口三者之间任意一个与当前页面 url 不同
即为跨域
为什么:出于浏览器的同源策略限制。同源策略会阻止一个域的 javascript 脚本和
另外一个域的内容进行交互。
解决:
1> document.domain + iframe (只有在主域相同的时候才能使用该方法)
2> location.hash + iframe
3> window.name + iframe
4> web sockets web sockets 是一种浏览器的 API,它的目标是在一个单独的持久
连接上提供全双工、双向通信。web sockets 原理:在 JS 创建了 web socket 之后,会
有一个 HTTP 请求发送到服务器以发起连接。取得服务器响应后,建立的连接会使用
HTTP 升级从 HTTP 协议交换为 web sockt 协议。只有在支持 web socket 协议的服务器
上才能正常工作
5>JSONP;
6>跨域资源共享 CORS,服务器设置 Access-Control-Allow-OriginHTTP 响应头之后,
浏览器将会允许跨域请求;
7>nginx:反向代理;
8>window.postMessage + iframe
3.jsonp 原理 是什么?
基本原理: 主要就是利用了 script 标签的 src 没有跨域限制来完成的。
4.http 各版本的改进都是什么?
1)HTTP/1.0–>HTTP/1.1
1.使用 TCP 长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
2.支持 管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,
就可以发第二个请求出去,可以减少整体的响应时间头部压缩
2)HTTP/1.1–>HTTP/2
1.头部压缩
HTTP/2 会压缩头(Header),如果你同时发出多个请求,他们的头是一样的
或是相似的,那么,协议会帮你消除重复的部分。
这就是所谓的 HPACK 算法:在客户端和服务器同时维护一张头信息表,所有
字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引
号,这样就提高速度了。
2.二进制格式
HTTP/2 不再像 HTTP/1.1 里的纯文本形式的报文,而是全面采用了二进制格式。
3.数据流
HTTP/2 的数据包不是按顺序发送的,同一个连接里面连续的数据包,可能属于
不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。每个请求或回
应的所有数据包,称为一个
数据流(Stream)
。
4 多 路 复 用
HTTP/2 是可以在一个连接中并发多个请求或回应,而不用按照顺序一一对应。
5. 服 务 器 推 送
HTTP/2 还在一定程度上改善了传统的「请求 - 应答」工作模式,服务不再是
被动地响应,也可以主动向客户端发送消息
3)
HTTP/2–>HTTP/3
HTTP/2 主要的问题在于:多个 HTTP 请求在复用一个 TCP 连接,下层的 TCP 协
议是不知道有多少个 HTTP 请求的。所以一旦发生了丢包现象,就会触发 TCP 的重传
机制,这样在一个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回
来。
所以 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!
UDP 是不可靠传输的,但
基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输; QUIC 是一个在 UDP 之上的
伪 TCP + TLS + HTTP/2 的多路复用的协议。
5.https 原理是什么?为什么可以保证安全性?
通过非对称加密算法进行交互协商获得对称加密算法与密钥,使用协商获得的加密算
法与秘钥对交互数据加密和解密
1)将此请求发送给到服务端,此时客户端同时将自己支持的加密算法带给服务
端;
2)服务器从中选出一组加密算法与 HASH 算法,并将自己的身份信息以证书的形式
发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构,hash 算
法等信息。
3)浏览器获得网站证书之后浏览器要做以下工作:
验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在
访问的地址一致),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出
证书不受信的提示。 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生
成一串随机数的密码,并用证书中提供的公钥加密。 使用约定好的 HASH 算法计算握
手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息请求给服
务器。
4)服务器接收浏览器发来的数据之后要做以下的操作:
使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验
证自己的 HASH 值是否与浏览器发来的一致。如果验证无误,同样使用随机字符串加
密握手信息和握手信息 hash 值发回给到客户端 5)浏览器用开始自己生成的随机数进
行解密解密并计算握手消息的 HASH,如果与服务端发来的 HASH 一致,此时握手过
程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进
行加密。
6.http 常见状态码有哪些?
类别 原因短语
1XX Informational(信息性状态码) 接受的请求正在处理
2XX Success(成功状态码) 请求正常处理完毕
3XX Redirection(重定向状态码) 需要进行附加操作以完成请求
4XX Client Error(客户端错误状态码) 服务器无法处理请求
5XX Server Error(服务器错误状态码) 服务器处理请求出错
2XX
1)200 OK:请求已正常处理。
2)204 No Content:请求处理成功,但没有任何资源可以返回给客户端。
3)206 Partial Content:是对资源某一部分的请求,该状态码表示客户端进行了范围
请求,而服务器成功执行了这部分的 GET 请求。
3XX
4)301 Moved Permanently:永久性重定向,请求的资源已经被分配了新的 URI,以
后应使用资源现在所指的 URI。
5)302 Found:临时性重定向,资源的 URI 已临时定位到其他位置了。
6)303 See Other:资源的 URI 已更新,303 状态码明确表示客户端应当采用 GET 方
法获取资源。
7)304 Not Modified:资源已找到,但未符合条件请求。
8)307 Temporary Redirect:临时重定向。
4XX
9)400 Bad Request:服务器端无法理解客户端发送的请求,请求报文中可能存在语
法错误。
10)401 Unauthorized:该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证,
DIGEST 认证)的认证信息。
11)403 Forbidden:不允许访问那个资源。
12)404 Not Found:服务器上没有请求的资源。路径错误等。
5XX
13)500 Internal Server Error:内部资源出故障了
14)503 Service Unavailable:该状态码表明服务器暂时处于超负载或正在停机维护,
现在无法处理请求。
7.http 有哪些方法?
GET
请求指定的页面信息,并返回实体主体。
HEAD
类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
POST
向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。
POST 请求可能会导致新的资源的建 bv 立和/或已有资源的修改。
PUT
从客户端向服务器传送的数据取代指定的文档的内容。
DELETE
请求服务器删除指定的页面。
CONNE
CT
HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
OPTIO
NS
允许客户端查看服务器的性能。
TRACE
回显服务器收到的请求,主要用于测试或诊断。
PATCH
是对 PUT 方法的补充,用来对已知资源进行局部更新 。
8.get 和 post 区别是什么?
1)参数传递
get 请求会将参数添加到 URL 后面,没有请求主体,调用 send()函数,传递的参数为
null;
post 请求的数据会将数据放在请求体中。用户无法通过 URL 直接看到,调用 send()
函数时,传递的数据为 data,即 XML)send(data)
2)服务端参数获取
使用 Express 作为服务器框架,get 通过 Request)query 获取参数;
post 需要添加中间件,同时通过 Request)boby 获取参数
3)传递的数据量
get 请求传输的数据量小,post 请求的数据量较大,一般不受限制;
4)安全性
get 请求安全较低,因为其请求的参数出现 URL,而且采用明文进行数据传输;通过浏
览器缓存或者历史记录可以容易获取到某些隐私请求的参数.
POST 通过请求体进行数据传输,数据不会出现在 URL 中,隐藏了请求数据的信息
5)处理 form 表单的差异性
get 请求时,action 指定的 url 中请求的参数会被丢弃 提交时只会将表单中的元素进行
拼接向服务器传递。解决办法:使用隐藏域 type=hidden
6)GET 请求会被浏览器主动 cache,而 POST 不会,除非手动设置。
9.http、浏览器缓存缓存机制。
HTTP 缓存主要分强制缓存和对比缓存
强制缓存的 HTTP 相关头部 Cache-Control(HTTP1.1),Exipres(HTTP1.0),浏览器
直接读本地缓存,不会再跟服务器端交互,状态码 200。
对比缓存的 HTTP 相关头部 Last-Modified / If-Modified-Since, Etag / If-None
Match (Etag 优先级比 Last-Modified / If-Modified-Since 高,Etag 强校验器,Last
Modified 弱校验器),每次请求需要让服务器判断一下资源是否更新过,从而决定浏览
器是否使用缓存,如果是,则返回 304,否则重新完整响应。
包括:强制缓存:header 中会有两个字段来标明失效规则(Expires/Cache-Control),
在缓存数据未失效的情况下,可以直接使用缓存数据,不需要再请求服务器,
对比缓存:响应 header 中会有两个字段来标明规则 Last-Modified / If-Modified
Since,Etag / If-None-Match(优先级高于 Last-Modified / If-Modified-Since)
10.Cache-Control 或 Expires 的区别
Cache-Control 是 HTTP / 1.1 中引入的,它们可以用于完成相同的操作,但是的
数据值 Expires 是 HTTP 日期,而 Cache-Control,max-age 允许您指定相对的时间量,
因此可以指定“请求页面后 X 个小时”。 如果响应中包含 Cache-Control 带有 max-age
指令的字段,则接收者必须忽略该 Expires 字段。
11.cdn 是什么?它的原理是什么?
CDN 内容分发网络。CDN 解决的是如何将数据快速可靠从源站传递到用户的问题。
原理:
1)当用户去访问一个内容 URL,本地的 DNS 系统会将其解析,最终将域名的解
析权交给 CNAME 指向的 CDN 专用 DNS 服务器。
2)CDN 的 DNS 服务器将 CDN 的全局负载均衡设备的 IP 地址返回用户。用户向
CDN 的全局负载均衡设备发起内容 URL 的访问请求。
4)区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务,选择的依据
包括:根据用户的 IP 地址,判断哪一台服务器距用户最近;根据用户所请求的 URL 中
携带的内容名称,判断哪一台服务器上有用户所需内容;查询各个服务器当前的负载
情况,判断哪一台服务器尚有服务能力。基于以上这些条件的综合分析之后,区域负
载均衡设备会向全局负载均衡设备返回一台缓存服务器的 IP 地址。
5)全局负载均衡设备把缓存服务器的 IP 地址返回给用户。
6)用户向缓存服务器发起请求,缓存服务器响应用户请求,将用户所需内容传送
到用户终端。如果这台缓存服务器上并没有用户想要的内容,而区域均衡设备依然将
它分配给了用户,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯
到网站的源服务器,将内容拉取到本地。
12. localStorage、sessionStorage、cookie 区是什么?
1)cookie 数据大小不能超过 4k;sessionStorage 和 localStorage 的存储比 cookie 大
得多,可以达到 5M+
2)cookie 设置的过期时间之前一直有效;localStorage 永久存储,浏览器关闭后数据
不丢失除非主动删除数据;sessionStorage 数据在当前浏览器窗口关闭后自动删除
3)cookie 的数据会自动的传递到服务器;sessionStorage 和 localStorage 数据保存在
本地
13.http 和 https 的区别
1)HTTPS 协议需要到 CA 申请证书,一般免费证书很少,需要交费。
2)HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,HTTPS 运行在 SSL/TLS
之上,SSL/TLS 运行在 TCP 之上,所有传输的内容都经过加密的,安全更好
3)HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,
后者是 443。
4)http 的连接很简单,是无状态的。https 握手阶段比较费时,会使页面加载时间
延长 50%,增加 10%~20%的耗电。
14.websocker
HTTP 协议是一种无状态的、无连接的、单向的应用层协议,随着 互联网发展。催
生了这样一个需求,当服务器有更新时,需要立即将数据发送给客户端,这就是基于
服务器端的推送技术。
WEBSOCKET 之前的解决方法大概这么几种:
1)轮询:客户端设置一个时间间隔,
时间到以后,向服务器发送 request 询问有无新数据,服务器立即返回 response,如
果有更新则携带更新的数据。
2)长轮询:为阻塞模式的轮询,客户端请求新的数据
request, 服务器会阻塞请求,直到有新数据后才返回 response 给客户端;然后客户端
再重复此过程。这两种方式的特点,不断的建立 HTTP 连接,然后发送请求 request,
之后服务器等待处理。服务端体现的是一种被动性,同时这种处理方式,非常耗费网
络带宽和服务器资源。
WebSocket 用于在 Web 浏览器和服务器之间进行任意的双向数据传输的一种技术。
WebSocket 协议基于 TCP 协议实现,包含初始的握手过程,以及后续的多次数据帧双
向传输过程。其目的是在 WebSocket 应用和 WebSocket 服务器进行频繁双向通信时,
可以使服务器避免打开多个 HTTP 连接进行工作来节约资源,提高了工作效率和资源
利用率。
WebSocket 技术的优点有:
1)通过第一次 HTTP Request 建立了连接之后,后续的 数据交换都不用再重新发送 HTTP Request,节省了带宽资源;
2) WebSocket 的连接是
双向通信的连接,在同一个 TCP 连接上,既可以发送,也可以接收;。
15 什么是 cookie
HTTP 协议本身是无状态的。什么是无状态呢。即每次的请求都是独立的,服务器并
不知道请求之间有什么联系。Cookie 实际上是一小段的文本信息(key-value 格式)。
客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用浏览器颁发一个
Cookie。浏览器会把 Cookie 保存起来。当浏览器再请求该网站时,浏览器把请求的网
址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态。
NAME=VALUE 键值对,可以设置要保存的 Key/Value, Expires 过期时间,在设置的
某个时间点后该 Cookie 就会失效 maxAge 有 3 种值,分别为正数,负数和 0。
maxAge 为 0,则表示删除该 Cookie maxAge 为负数,则表示该 Cookie 仅在本浏览器窗
口以及本窗口打开的子窗口内有效,关闭窗口后该 Cookie 即失效。maxAge 为负 数的
Cookie,为临时性 Cookie
Domain 生成该 Cookie 的域名,
Path 该 Cookie 是在当前的哪个路径下生成的,如 path=/wp-admin/ Secure
Secure 如果设置了这个属性,那么只会在 SSH 连接时才会回传该 Cookie
HttpOnly 如果 cookie 中设置了 HttpOnly 属性,那么通过 js 脚本将无法读取到
cookie 信息,
16.token
token 是用来身份验证
其实就是服务端生成的一串加密字符串、以作客户端进行请求的一个“令牌”。当用
户第一次使用账号密码成功进行登录后,服务器便生成一个 Token 及 Token 失效时间
并将此返回给客户端,若成功登陆,以后客户端只需在有效时间内带上这个 Token 前
来请求数据即可,无需再次带上用户名和密码。
17.token 放在 cookie 中
token:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前
几位以哈希算法压缩成的一定长度的十六进制字符串) `token`可以存放在`Cookie`
中,`token` 是否过期,应该由后端来判断,不该前端来判断,所以`token`存储在
`cookie`中只要不设置`cookie`的过期时间就 ok 了,如果 `token` 失效,就让后端在
接口中返回固定的状态表示`token` 失效,需要重新登录,再重新登录的时候,重
新设置 `cookie` 中的 `token` 就行。
18.http 的 keep-alive
HTTP 是短连接,client 向 server 发送一个 request,得到 response 后,连接就关闭。
http1.1 就支持了 TCP 长连接,多个 http 请求共用一个 tcp 连接,避免频繁的进行
三次握手和四次挥手
HTTP 的 Keep-Alive 与 TCP 的 Keep Alive,有些不同,两者意图不一样。前者主要是
TCP 连接复用,避免建立过多的 TCP 连接。而 TCP 的 Keep Alive 的意图是在于保持
TCP 连接的存活,就是发送心跳包。隔一段时间给连接对端发送一个探测包,如果收
到对方回应的 ACK,则认为连接还是存活的,在超过一定重试次数之后还是没有收到
对方的回应,则丢弃该 TCP 连接。