前端题目—

html部分

Doctype作用? 严格模式与混杂模式如何区分?它们有何意义?

1)<!DOCTYPE> 声明位于文档中的最前面,处于 标签之前。告知浏览器以何种模式来渲染文档。
2)严格模式的排版和 JS 运作模式是 以该浏览器支持的最高标准运行。
3)在混杂模式中,页面以宽松的向后兼容的方式显示。模拟老式浏览器的行为以防止站点无法工作。
4)DOCTYPE不存在或格式不正确会导致文档以混杂模式呈现。

你知道多少种Doctype文档类型?

该标签可声明三种 DTD 类型,分别表示严格版本、过渡版本以及基于框架的 HTML 文档。

HTML 4.01 规定了三种文档类型:Strict、Transitional 以及 Frameset。
XHTML 1.0 规定了三种 XML 文档类型:Strict、Transitional 以及 Frameset。
Standards (标准)模式(也就是严格呈现模式)用于呈现遵循最新标准的网页,而 Quirks
(包容)模式(也就是松散呈现模式或者兼容模式)用于呈现为传统浏览器而设计的网页。

HTML与XHTML——二者有什么区别
1.功能上
(1)HTML对于各大浏览器兼容性较差bai(pc端浏览器、手du机端浏览器、PAD),对于网页页面编写技巧要求比较高,现在web前端开发的静态网页,一般都是html4.0,HTML5就另当别论了。
(2)XHTML可以很好处理各大浏览器的兼容,XHTML的语法较为严谨,习惯松散结构的HTML编写者刚开始接触XHTML有些不习惯。XHTML结合了部分XML的强大功能及大多数HTML的简单特性。
2.书写习惯上
(1)HTML标签不区分大小写XHTML所有标签都必须小写。
(2)XHTML标签必须成双成对.
(3)html对标签顺序要求不严格,XHTML标签顺序必须正确。

html5有哪些新特性、移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分 HTML 和 HTML5?
用于绘画的 canvas 元素
用于媒介回放的 video 和 audio 元素
对本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
新的特殊内容元素,比如 article、footer、header、nav、section
新的表单控件,比如 calendar、date、time、email、url、search
sessionStorage 的数据在浏览器关闭后自动删除
新的技术webworker, websocket,Geolocation
支持HTML5新标签:
IE8/IE7/IE6支持通过document.createElement方法产生的标签,
可以利用这一特性让这些浏览器支持HTML5新标签,
当然最好的方式是直接使用成熟的框架、使用最多的是html5shim框架

css部分:

css盒子模型:
1)标准模式:总宽度=width+margin(左右)+padding(左右)+border(左右)
(width=width(content))
2)怪异模式:总宽度=width+margin(左右)(width直接包括了padding(左右)+border(左右) )
高度同理

position 和 display 的取值和各自的意思和用法
1). position
①position属性取值:static(默认)、relative、absolute、fixed、inherit,sticky。
②postision:static;取消定位,为正常状态。
③除了static值,在其他三个值的设置下,z-index才会起作用。(确切地说z-index只在定位元素上有效)
④position:relative和absolute都可以用于定位,区别在于前者的div还属于正常的文档流,后者已经是脱离了正常文档流,不占据空间位置,不会将父类撑开。定位原点relative是相对于它在正常流中的默认位置偏移,它原本占据的空间任然保留;absolute相对于第一个position属性值不为static的父类。所以设置了position:absolute,其父类的该属性值要注意,而且overflow:hidden也不能乱设置,因为不属于正常文档流,不会占据父类的高度,也就不会有滚动条。
⑤position:fixed 旧版本IE不支持,定位原点相对于浏览器窗口,而且不能变。常用于header,footer,或者一些固定的悬浮div,随滚动条滚动又稳定又流畅,比JS好多了。fixed可以有很多创造性的布局和作用,兼容性是问题。
⑥position:inherit。规定从父类继承position属性的值。但是任何版本的IE都不支持该属性值。
7⃣️ sticky基于用户的滚动位置来定位
当页面滚动超出目标区域时,它的表现就像 position:fixed;,它会固定在目标位置。当页面滚动在目标区域时为相对定位。
top, right, bottom 或 left 有这4个值的某个,或多个才可使粘性定位生效。否则其行为与相对定位相同
2) display
1、display属性取值:none、inline、inline-block、block、flex、inherit。
2、display属性规定元素应该生成的框的类型。文档内任何元素都是框,块框或行内框。
3、display:none和visiability:hidden都可以隐藏div,区别有点像absolute和relative,前者不占据文档的空间,后者还是占据文档的位置。
4、display:inline和block,又叫行内元素和块级元素。表现出来的区别就是block独占一行,在浏览器中通常垂直布局,可以用margin来控制块级元素之间的间距;而inline以水平方式布局,垂直方向的margin和padding都是无效的,大小跟内容一样,且无法设置宽高。inline就像塑料袋,内容怎么样,就长得怎么样;block就像盒子,有固定的宽和高。
5、inline-block就介于两者之间。
6、display: flex 意为"弹性盒布局模型",用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为flex布局。设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。采用flex布局的元素,称为flex容器。它的所有子元素自动成为容器成员,称为flex项目(flex item)。
容器的属性:以下6个属性设置在容器上。
flex-direction 决定主轴的方向(项目的排列方向)。
row(默认值):主轴为水平方向,起点在左端。
row-reverse:主轴为水平方向,起点在右端。
column:主轴为垂直方向,起点在上沿。
column-reverse:主轴为垂直方向,起点在下沿。
flex-wrap 默认情况下,项目都排在一条线上。flex-wrap属性定义,如果一条轴线排不下,如何换行。
nowrap(默认):不换行。
wrap:换行,第一行在上方。
wrap-reverse:换行,第一行在下方。
flex-flow flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。
justify-content 定义了项目在主轴上的对齐方式。假设主轴为从左到右。
flex-start(默认值):左对齐
flex-end:右对齐
center: 居中
space-between:两端对齐,项目之间的间隔都相等。
space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
align-items 定义项目在交叉轴上如何对齐。假设交叉轴从上到下。
flex-start:交叉轴的起点对齐。
flex-end:交叉轴的终点对齐。
center:交叉轴的中点对齐。
baseline: 项目的第一行文字的基线对齐。
stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
align-content 义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
flex-start:与交叉轴的起点对齐。
flex-end:与交叉轴的终点对齐。
center:与交叉轴的中点对齐。
space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
stretch(默认值):轴线占满整个交叉轴。
flex:1
== flex-grow:1/flex-shrike:1/flex-basis:0%;
flex-grow 1为等比扩张,0为原来状态相当没有变
flex-shrike1为等比缩小,0为原来状态相当没有变

水平垂直居中
第一种
#container{
position:relative;
width: 600px;
height: 300px;
background: aqua;
}
#center{
width:50%;
height:50%;
position:absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
background: red;
}
第二种
#container{
position:relative;
width: 600px;
height: 300px;
background: aqua;
}
#center{
width:100px;
height:100px;
position:absolute;
top:50%;
left:50%;
margin:-50px 0 0 -50px;
background: red;
}
第三种
#container{
width: 600px;
height: 300px;
background: aqua;
position:relative;
}
#center{
position:absolute;
width: 50px;
height: 50px;
margin:auto;
top:0;
bottom:0;
left:0;
right:0;
}
第四种
#container{
width: 600px;
height: 300px;
background: aqua;
display:flex;
}
#center{
width:50%;
height:50%;
background: red;
align-self: center;
}
第五种
#container{
width: 600px;
height: 300px;
background: aqua;
display:block;
}
#container:before{
content:’ ',
display:inline-block;
height:100%;
}
#center{
width:50%;
height:50%;
background: red;
display:inline-block;
vertical-align:middle;
}
第六种
#container{
width: 600px;
height: 300px;
background: aqua;
}
#center{
width:50%;
height:50%;
background: red;
position:relative;
top:50%;
transform:translateY(-50%);
}
line-height(文本垂直居中)
样式的层级关系,选择器优先级,CSS 选择符有哪些,以及抽离样式模块怎么写
1)样式的层级关系:一个是权重,另一个就是共用样式和私用样式了,比如说两个ul,它们的子元素除了背景色之外都一样,那可以直接用li {}来定义相同的公用样式,用 .ul_1 li {} , .ul_2 li {} 来定义不相同的样式。
2)选择器优先级:!important(10000)>内联样式(1000)>id选择器(100)>class选择器(10)(属性选择器/伪类选择器)>标签选择器(1)(伪元素选择器)

3)①.id选择器( # myid)
②.类选择器(.myclassname)
③.标签选择器(div, h1, p)
④.相邻选择器(h1 + p)
⑤.子选择器(ul > li)
⑥.后代选择器(lia)
⑦.通配符选择器( * )
⑧.属性选择器(a[rel = “external”])
⑨.伪类选择器(a: hover, li:nth-child)

4)抽离样式模块

使用css3实现一个持续的动画效果;
animation:mymove 5s infinite;
@keyframes mymove {
from {top:0px;}
to {top:200px;}
}
animation 用法:
animation-name 规定需要绑定到选择器的keyframe名称
animation-duration 规定完成动画所花费的时间,一般以秒计算
animation-timing-function 规定动画的速度曲线
animation-delay 规定在动画开始之前的延迟
animation-iteration-count 规定动画播放的次数
animation-direction 规定是否应该轮流 反向播放动画

canvas、svg的区别
1) Canvas依赖分辨率SVG不依赖分辨率
2) Canvas不支持事件处理器,SVG支持事件处理器
3) Canvas弱的文本渲染能力,SVGA适合渲染大型区域应用程序(谷歌地图)
4) canvas能够以 .png 或 .jpg 格式保存结果图像
5) canvas适合图像密集型的游戏,其中的许多对象会被频繁重绘,SVG不适合游戏应用
6) 复杂度高会减慢渲染速度

CSS3新增伪类举例
p:first-of-type 选择属于其父元素的首个p元素
p:last-of-type 选择属于其父元素的最后 p元素
p:only-of-type 选择属于其父元素唯一p元素
p:only-child 选择属于其父元素的唯一子元素
p:nth-child(2) 选择属于其父元素的第二个子元素
:enabled、:disabled 控制表单控件的禁用状态。
:checked,单选框或复选框被选中。

px和em和rem的区别
1)px像素(Pixel),它是相对于显示屏幕分辨率而言的,浏览器的默认字体尺寸(16px),所有未经调整的浏览器豆腐额:1em=16px
2)em的值并不是固定的,em会继承父级元素的字体大小。
3)rem是css3新增的一个相对长度单位,它的出现是为了解决em的缺点,em可以说是相对于父级元素的字体大小,当父级元素字体大小改变时,又得重新计算。rem出现就可以解决这样的问题,rem只相对于根目录,即HTML元素。所以只要在html标签上设置字体大小,文档中的字体大小都会以此为参照标准,一般用于自适应布局。

link标签和import标签的区别
link属于html标签,而@import是css提供的
页面被加载时,link会同时被加载,而@import引用的css会等到页面加载结束后加载。
link是html标签,因此没有兼容性,而@import只有IE5以上才能识别。
link方式样式的权重高于@import的。

BootStrap响应式原理,BT优缺点
是通过栅格系统和媒体查询实现的
使用@media (min-width:751px){这里写不要的,在这隐藏掉}@media (max-width:750px){这里写移动端需要的东西}
优点:
①BT的优势之一就是可以根据用户屏幕尺寸调整页面,使其在各个尺寸上都表现良好
②BT预先定义了很多CSS类,使用的时候直接给class赋予对应的类名即可
③BT的JS插件非常丰富,既可以用现成的也可以自己扩充
缺点
BT对IE6,7的兼容性肯定不好,对IE8的支持也需要一些额外的文件。
比如:BT将所有的元素盒模型都设置成了border-box,这是IE混杂模式下的盒模型,光这点就导致了不能兼容IE。

CSS3有哪些新特性?
1) CSS3实现圆角(border-radius),阴影(box-shadow),边框图片border-image

2)对文字加特效(text-shadow、),强制文本换行(word-wrap),线性渐变(linear-gradient)

3)旋转,缩放,定位,倾斜:transform:rotate(90deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);

4) 增加了更多的CSS选择器、多背景、rgba();

5)在CSS3中唯一引入的伪元素是 ::selection ;

6) 媒体查询(@media),多栏布局(flex)

display:none和visibility:hidden的区别?

display:none 隐藏对应的元素,在文档布局中不再给它分配空间,它各边的元素会合拢,就当他从来不存在。

visibility:hidden 隐藏对应的元素,但是在文档布局中仍保留原来的空间。

position:absolute和float属性的异同

§ 共同点:对内联元素设置float和absolute属性,可以让元素脱离文档流,并且可以设置其宽高。

§ 不同点:float仍会占据位置,absolute会覆盖文档流中的其他元素。

介绍一下box-sizing属性?

box-sizing属性主要用来控制元素的盒模型的解析模式。默认值是content-box。

content-box:某个box的宽等于它的width+padding+border+margin

border-box:当取值为border-box时,这个box的宽就等于它的width+margin了,也就是说该box的width是由content部分的width和padding以及border共同组成的了,换言之,padding和border不再向外延伸,而是往里边挤

传统页面布局和弹性盒子布局优缺点
传统方式:
布局目标的实现,属性赋值非常松散。
严重依赖于页面结构与内容实际页面大小。
当页面属性发生变化的时候,页面的布局取值都要重新调整。
不是很能够灵活的引用页面结构的变化。
用弹性盒子布局:
相关的css属性赋值,比较统一。
布局方式灵活,可以应对页面结构元素变化。
实现了一种整体控制方法,比较直观,高效。

解释下浮动和它的工作原理?清除浮动的技巧

浮动元素脱离文档流,不占据空间。浮动元素碰到包含它的边框或者浮动元素的边框停留。

1.使用空标签清除浮动。

这种方法是在所有浮动标签后面添加一个空标签 定义样式clear:both. 弊端就是增加了无意义标签。

2.使用overflow。

给包含浮动元素的父标签添加css属性 overflow:auto; zoom:1; zoom:1用于兼容IE6。

3.使用after伪对象清除浮动。

该方法只适用于非IE浏览器。具体写法可参照以下示例。使用中需注意以下几点。一、该方法中必须为需要清除浮动元素的伪对象中设置 height:0,否则该元素会比实际高出若干像素;

浮动元素引起的问题和解决办法?

浮动元素引起的问题:

(1)父元素的高度无法被撑开,影响与父元素同级的元素(父级高度为0,清楚浮动之后,高度为子级高度一样,不会出现高度坍塌现象)
(2)与浮动元素同级的非浮动元素(内联元素)会跟随其后
(3)若非第一个元素浮动,则该元素之前的元素也需要浮动,否则会影响页面显示的结构

解决方法:
使用CSS中的clear:both;属性来清除元素的浮动可解决2、3问题,对于问题1,添加如下样式,给父元素添加clearfix样式:
.clearfix:after{content:".";display: block;height:0;clear: both;visibility: hidden;}
.clearfix{display: inline-block;} /* for IE/Mac */

清除浮动的几种方法:
1、额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(优点:通俗易懂,方便;缺点:添加无意义标签,语义化差。不推荐)
css
.clear{ clear:both; }
html
<div class="fahter">
<div class="big">big</div>
<div class="small">small</div>
<div class="clear">额外标签法</div>
</div>
2、使用after伪类(推荐使用)
在需要清除浮动的标签里引入类为clearfix
.clearfix:after{/伪元素是行内元素 正常浏览器清除浮动方法/
content:".";
height:0;
display:block;
visibility: hidden;
clear:both;
}
.clearfix{
*zoom: 1;/*ie6清除浮动的方式 号只有IE6-IE7执行,其他浏览器不执行/
}

3 、 使用before和after双伪元素清除浮动(推荐使用)
.clearfix:after,.clearfix:before{ content: ""; display: table; }
.clearfix:after{ clear: both; }
.clearfix{ *zoom: 1; }

<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>
</div>

4、设置overflow为hidden或者auto
.fahter{
overflow: hidden;
}
(优点:代码简洁;缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素。不推荐使用)

水平居中
行内元素:display: inline-block;
块级元素:margin: 0 auto;
Flex: display: flex; justify-content: center
垂直居中
行高 = 元素高:line-height: height
flex: display: flex; align-item: center

css3实现border渐变色
.box{
width: 100px;
height: 100px;
border:10px solid #ddd;
border-image: -webkit-linear-gradient(#ddd,#000) 30 30;
border-image: -moz-linear-gradient(#ddd,#000) 30 30;
border-image: linear-gradient(#ddd,#000) 30 30;
}
less,sass,styus三者的区别
变量
Sass声明变量必须是『$』开头,后面紧跟变量名和变量值,而且变量名和变量值需要使用冒号:分隔开。
Less 声明变量用『@』开头,其余等同 Sass。
Stylus 中声明变量没有任何限定,结尾的分号可有可无,但变量名和变量值之间必须要有『等号』。
作用域
Sass:三者最差,不存在全局变量的概念
Less:最近的一次更新的变量有效,并且会作用于全部的引用!
Stylus:Sass 的处理方式和 Stylus 相同,变量值输出时根据之前最近的一次定义计算,每次引用最近的定义有效;
嵌套
三种 css 预编译器的「选择器嵌套」在使用上来说没有任何区别,甚至连引用父级选择器的标记 & 也相同
(https://juejin.im/post/5d87985d6fb9a06add4e6ac3)

js部分

JavaScript 里有哪些数据类型,解释一下 null 和 undefined
基本类型: string,number,boolean,undefined,null,symbol,bigint
引用类型: Function,Array,Object
undefined:是声明了没有赋值 如var a
null是表示空值,一般 拿null 来清空变量。
console.log(undefined == null);//为true
var a = 1 + “1”; // “11”
var b = 1 + {}; // “1[object Object]”
var c = 1 + []; // “1”
var d = 1 + true; // 2
var e = { name: ‘aa’ } + [1, 2]; // “[object Object]1,2”
var f = null + undefined;// NaN
var g = true + null; // 1

null表示”没有对象”,即该处不应该有值。典型用法是:
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。

如何解决跨域问题
1.JSONP:
原理是:动态插入script标签,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。
由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出JSON数据并执行回调函数,从而解决了跨域的数据请求。
优点是兼容性好,简单易用,支持浏览器与服务器双向通信。缺点是只支持GET请求。JSONP:json+padding(内填充),顾名思义,就是把JSON填充到一个盒子里
(https://blog.csdn.net/inite/article/details/80333130)
CORS:
JSONP原理,其不足,就是只能使用GET提交,若传输的数据量大,这个JSONP方式就歇菜了。就来介绍另一种跨域介绍方案—CORS。
相对JSONP,CORS支持POST提交,并且实施起来灰常简单,CORS原理只需要向响应头header中注入Access-Control-Allow-Origin,这样浏览器检测到header中的Access-Control-Allow-Origin,则就可以跨域操作了。
2.CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
(https://www.cnblogs.com/keyi/p/6726089.html)
vue 使用proxyTable(在config的index的dev里配置)
3.proxyTable: {
‘/a’: { //使用"/api"来代替"http://f.apiplus.c"
target: ‘http://localhost:80’, //源地址
changeOrigin: true, //改变源
pathRewrite: {
‘^/a’: ‘http://localhost:80’ //路径重写
}
},
‘/CAPI’: { //使用"/api"来代替"http://f.apiplus.c"
target: ‘http://localhost:8088’, //源地址
changeOrigin: true, //改变源
pathRewrite: {
‘^/CAPI’: ‘http://localhost:8088’ //路径重写
}
}
},
4.谷歌降版本

XML和JSON的区别?
(1).数据体积方面。
JSON相对于XML来讲,数据的体积小,传递的速度更快些。
(2).数据交互方面。
JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。
(3).数据描述方面。
JSON对数据的描述性比XML较差。
(4).传输速度方面。
JSON的速度要远远快于XML。
(XML使用小结: https://blog.csdn.net/chenkaibsw/article/details/80337274)

谈谈你对webpack的看法

Webpack 是当下最热门的前端资源模块化管理和打包工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。
1)webpack的两大特色:
①code splitting(可以自动完成)
②loader 可以处理各种类型的静态文件,并且支持串联操作
webpack 是以commonJS的形式来书写脚本滴,但对 AMD/CMD 的支持也很全面,方便旧项目进行代码迁移。
2)webpack具有requireJs和browserify的功能,但仍有很多自己的新特性:
① 对 CommonJS 、 AMD、ES6的语法做了兼容
②对js、css、图片等资源文件都支持打包
③ 串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持
④有独立的配置文件webpack.config.js
⑤可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间
⑥支持 SourceUrls 和SourceMaps,易于调试
⑦ 具有强大的Plugin接口,大多是内部插件,使用起来比较灵活
⑧webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快

weapack配置:
postcss-loader/less-loader/sass-loader/file-loader/thread-loader/bable-loader/bable-polyfill/happypack/tree-shaking/exclude,include/uglifyjs-webpack-plugin/optimize-css-assets-webpack-plugin

说说你对作用域链的理解
作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。

创建ajax过程
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.
https://www.cnblogs.com/yangguoe/p/8461623.html

渐进增强和优雅降级
渐进增强 :针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
优雅降级 :一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

Web Worker 和webSocket
Web Worker方法:
postMessage(发)/onMessage(收)/terminate(关主线程)/close(关子线程)
/onerror(获取报错)
HTML5的Web Socket可以让服务器主动向客户端发送消息,非常适合开发聊天室,多人游戏等协作应用。
Web Worker能够让JavaScript正真意义上实现多线程,并擅长处理大数据计算。
https://www.jianshu.com/p/be1d342fd5b8

Javascript垃圾回收方法

标记清除(mark and sweep)

这是JavaScript最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。
垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了

引用计数(reference counting)

在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。

在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的,
也就是说只要涉及BOM及DOM就会出现循环引用问题。

谈谈性能优化问题

css
• 避免把样式写在html标签里
• 避免使用css表达式,避免使用高级、通配选择器。
• 使用外部 CSS
• 优化 CSS Sprite
• 避免图片 src 为空
• 使用 iconfont
• 使用css图片精灵,初始首屏之外的图片资源按需加载,静态资源延迟加载。
js
• 少用全局变量
• 多个变量声明合并
• 用innerHTML代替DOM操作,减少DOM操作次数,减少 DOM 元素数量
• 用setTimeout来避免页面失去响应
• 压缩文件,开启GZIP
• 缓存Ajax,使用CDN,添加Expires头,服务端配置Etag,减少DNS查找
(CDN:https://www.cnblogs.com/blogbyhuer/p/9335257.html)
• 减少 HTTP 请求数
• 避免重定向
• 图片懒加载
• 使用外部 JavaScript
• 压缩 JavaScript 、 CSS 、字体、图片等
• 避免使用with(with会创建自己的作用域,会增加作用域链长度)
• 多域名分发划分内容到不同域名
• 尽量减少 iframe 使用
• 把脚本放在页面底部(script就是脚本)

移动端性能优化

尽量使用css3动画,开启硬件加速。
适当使用touch事件代替click事件。
避免使用css3渐变阴影效果。
可以用transform: translateZ(0)来开启硬件加速。
不滥用Float。Float在渲染时计算量比较大,尽量减少使用
不滥用Web字体。Web字体需要下载,解析,重绘当前页面,尽量减少使用。
合理使用requestAnimationFrame动画代替setTimeout
CSS中的属性(CSS3 transitions、CSS3 3D transforms、Opacity、Canvas、WebGL、Video)会触发GPU渲染,请合理使用。过渡使用会引发手机过耗电增加

PC端的在移动端同样适用

JS的几条基本规范
1、不要在同一行声明多个变量
2、请使用===/!==来比较true/false或者数值
3、使用对象字面量替代new Array这种形式
4、不要使用全局变量
5、Switch语句必须带有default分支
6、函数不应该有时候有返回值,有时候没有返回值
7、For循环必须使用大括号
8、IF语句必须使用大括号
9、for-in循环中的变量 应该使用var关键字明确限定作用域,从而避免作用域污染

** 快速 排序的思想并实现一个快排?**

“快速排序”的思想很简单,整个排序过程只需要三步:
  (1)在数据集之中,找一个基准点
  (2)建立两个数组,分别存储左边和右边的数组
  (3)利用递归进行下次比较
<scripttype=“text/javascript”>
function quickSort(arr){
if(arr.length<=1){
return arr;//如果数组只有一个数,就直接返回;
}
var num = Math.floor(arr.length/2);//找到中间数的索引值,如果是浮点数,则向下取整
var numValue =arr.splice(num,1);//找到中间数的值
var left = [];
var right = [];
for(var i=0;i<arr.length;i++){
if(arr[i]<numValue){
left.push(arr[i]);//基准点的左边的数传到左边数组
}
else{
right.push(arr[i]);//基准点的右边的数传到右边数组
}
}
return quickSort(left).concat(numValue,quickSort(right));//递归不断重复比较
}
var arr =[32,45,37,16,2,87]
console.log(quickSort(arr));//[2, 16, 32, 37, 45, 87]

数组操作
map: 遍历数组,返回回调返回值组成的新数组
forEach: 无法break,可以用try/catch中throw new Error来停止
filter: 过滤
some: 有一项返回true,则整体为true
every: 有一项返回false,则整体为false
join: 通过指定连接符生成字符串
push / pop: 末尾推入和弹出,改变原数组, 返回推入/弹出项【有误】
unshift / shift: 头部推入和弹出,改变原数组,返回操作项【有误】
sort(fn) / reverse: 排序与反转,改变原数组
concat: 连接数组,不影响原数组, 浅拷贝
slice(start, end): 返回截断后的新数组,不改变原数组
splice(start, number, value…): 返回删除元素组成的数组,value 为插入项,改变原数组
indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标
reduce / reduceRight(fn(prev, cur), defaultPrev): 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)

ES6的了解
新增模板字符串(为JavaScript提供了简单的字符串插值功能)、箭头函数(操作符左边为输入的参数,而右边则是进行的操作以及返回的值Inputs=>outputs。)、for-of(用来遍历数据—例如数组中的值。)arguments对象可被不定参数和默认参数完美代替。ES6将promise对象纳入规范,提供了原生的Promise对象。增加了let和const命令,用来声明变量。增加了块级作用域。let命令实际上就增加了块级作用域。ES6规定,var命令和function命令声明的全局变量,属于全局对象的属性;let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。。还有就是引入module模块的概念

js继承方式及其优缺点

原型链继承的缺点:
第一个问题是由于子类型的原型是父类型的实例,也就是子类型的原型中包含的父类型的属性,从而导致引用类型 值的原型属性会被所有实例所共享。
第二个问题就是:在创建子类型 的实例时,不能向超类型的构造函数中传递参数。

借用构造函数(类式继承):
借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起。所以我们需要原型链+借用构造函数的模式,这种模式称为组合继承

组合式继承

组合式继承是比较常用的一种继承方法,其背后的思路是 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。

defer和async

defer并行加载js文件,会按照页面上script标签的顺序执行
async并行加载js文件,下载完成立即执行,不会按照页面上script标签的顺序执行

用过哪些设计模式?

工厂模式:

主要好处就是可以消除对象间的耦合,通过使用工程方法而不是new关键字。将所有实例化的代码集中在一个位置防止代码重复。

工厂模式解决了重复实例化的问题,但还有一个问题,那就是识别问题,因为根本无法搞清楚他们到底是哪个对象的实例。

function createPerson(name,age,job){//集中实例化的函数
var obj = new Object();
obj.name =name;
obj.age = age;
obj.job= job;
obj.sayName =function () {
console.log(this.name);
};
return obj;
}
var person1=new Person(“张三”,22,“coder”);
console.log(person1)
person1.sayName();
var person2=new Person(“李四”,22,“coder”);
console.log(person2)
person2.sayName();

构造函数模式:
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
};
}
var person1=new Person(“张三”,22,“coder”);
console.log(person1)
person1.sayName();
var person2=new Person(“李四”,22,“coder”);
console.log(person2)
person2.sayName();
使用构造函数的方法 ,即解决了重复实例化的问题 ,又解决了对象识别的问题,该模式与工厂模式的不同之处在于:

1.构造函数方法没有显示的创建对象 (new Object());
2.直接将属性和方法赋值给 this 对象;
3.没有 renturn 语句。

组合使用构造函数模式和原型模式:
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
}
Person.prototype={
constructor:Person,
sayName:function(){
console.log(this.name);
}
}
var person1=new Person(“张三”,22,“coder”);
console.log(person1)
person1.sayName();
var person2=new Person(“李四”,22,“coder”);
console.log(person2)
person2.sayName();

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。时共享着对方法的引用,最大限度的节省了内存。同时支持向构造函数传递参数。
 
说说你对闭包的理解

使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念

闭包有三个特性:

1.函数嵌套函数

2.函数内部可以引用外部的参数和变量

3.参数和变量不会被垃圾回收机制回收

请你谈谈Cookie的弊端

cookie虽然在持久保存客户端数据提供了方便,分担了服务器存储的负担,但还是有很多局限性的。

第一:每个特定的域名下最多生成20个cookie

1.IE6或更低版本最多20个cookie

2.IE7和之后的版本最后可以有50个cookie。

3.Firefox最多50个cookie

4.chrome和Safari没有做硬性限制

IE和Opera 会清理近期最少使用的cookie,Firefox会随机清理cookie。

cookie的最大大约为4096字节,为了兼容性,一般不能超过4095字节。

IE 提供了一种存储可以持久化用户数据,叫做userdata,从IE5.0就开始支持。每个数据最多128K,每个域名下最多1M。这个持久化数据放在缓存中,如果缓存没有清理,那么会一直存在。

优点:极高的扩展性和可用性

1.通过良好的编程,控制保存在cookie中的session对象的大小。
2.通过加密和安全传输技术(SSL),减少cookie被破解的可能性。
3.只在cookie中存放不敏感数据,即使被盗也不会有重大损失。
4.控制cookie的生命期,使之不会永远有效。偷盗者很可能拿到一个过期的cookie。

缺点:

1.Cookie 数量和长度的限制。每个domain最多只能有20条cookie,每个cookie长度不能超过4KB,否则会被截掉.

2.安全性问题。如果cookie被人拦截了,那人就可以取得所有的session信息。即使加密也与事无补,因为拦截者并不需要知道cookie的意义,他只要原样转发cookie就可以达到目的了。

3.有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器。如果我们把这个计数器保存在客户端,那么它起不到任何作用。

浏览器本地存储

在较高版本的浏览器中,js提供了sessionStorage和globalStorage。在HTML5中提供了localStorage来取代globalStorage。

html5中的Web Storage包括了两种存储方式:sessionStorage和localStorage。

sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。

而localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。

web storage和cookie的区别

Web Storage的概念和cookie相似,区别是它是为了更大容量存储设计的。Cookie的大小是受限的,并且每次你请求一个新的页面的时候Cookie都会被发送过去,这样无形中浪费了带宽,另外cookie还需要指定作用域,不可以跨域调用。

除此之外,Web Storage拥有setItem,getItem,removeItem,clear等方法,不像cookie需要前端开发者自己封装setCookie,getCookie。

但是cookie也是不可以或缺的:cookie的作用是与服务器进行交互,作为HTTP规范的一部分而存在 ,而Web Storage仅仅是为了在本地“存储”数据而生

浏览器的支持除了IE7及以下不支持外,其他标准浏览器都完全支持(ie及FF需在web服务器里运行),值得一提的是IE总是办好事,例如IE7、IE6中的userData其实就是javascript本地存储的解决方案。通过简单的代码封装可以统一到所有的浏览器都支持web storage。

localStorage和sessionStorage都具有相同的操作方法,例如setItem、getItem和removeItem等

cookie 和session 的区别:

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行cookie欺骗, 考虑到安全应当使用session。

3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

5、所以个人建议:

将登陆信息等重要信息存放为SESSION

其他信息如果需要保留,可以放在COOKIE中

DOM操作——怎样添加、移除、移动、复制、创建和查找节点。
1)创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点

2)添加、移除、替换、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //并没有insertAfter()

3)查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
getElementById() //通过元素Id,唯一性

new操作符具体干了什么呢?

1、创建一个空对象,使用 this引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。

var obj = {};

obj.proto = Base.prototype;

Base.call(obj);

js延迟加载的方式有哪些?

defer和async、动态创建DOM方式(创建script,插入到DOM中,加载完毕后callBack)、按需异步载入js

call() 和 apply() 的区别和作用?
(1)基本用法
function add(a,b){
return a+b;
}
function sub(a,b){
return a-b;
}
var a1 = add.apply(this,[4,2]);  
var a2 = sub.apply(this,[4,2]);
call的用法
add.call(this,4,2);  
sub.call(this,4,2);

(2)实现继承

function Animal(name){
this.name = name;
this.showName = function(){
alert(this.name);
}
}
function Cat(name){
Animal.apply(this,[name]);
}
var cat = new Cat(“咕咕”);
cat.showName();

call的用法
Animal.call(this,name);

(3)多重继承

function Class10(){
this.showSub = function(a,b){
alert(a - b);
}
}

function Class11(){
this.showAdd = function(a,b){
alert(a + b);
}
}

function Class12(){
Class10.apply(this);
Class11.apply(this);
}

var c2 = new Class12();
c2.showSub(3,1); //2
c2.showAdd(3,1); //4

(4).一些妙用:比如计数最大,最小值;拼接数组
var numbers=[5,458,120,-215];
var minInnumbers = Math.min.apply(this,numbers);//math为对象,min()/max()为方法
console.log(minInnumbers) //-215
var maxInnumbers = Math.max.call(this,5,458,120,-215);
console.log(maxInnumbers) //458
/…/
var arr1=new Array(“1”,“2”,“3”);
var arr2=new Array(“4”,“5”,“6”);
Array.prototype.push.apply(arr1,arr2);

什么是闭包?
函数A 里面包含了 函数B,而 函数B 里面使用了 函数A 的变量,那么 函数B 被称为闭包。
function A() {
var a = 1;
function B() {
console.log(a);
}
return B();
}
闭包的特征:
1)函数内再嵌套函数
2)内部函数可以引用外层的参数和变量
3)参数和变量不会被垃圾回收制回收

使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。

eval是做什么的?ajax 是什么?ajax 的交互流程?同步和异步的区别?ajax的优、缺点,还有它的特点?

eval的功能是把对应的字符串解析成JS代码并运行;应该避免使用eval,不安全,非常耗性能(2次,一次解析成js语句,一次执行)。

ajax是异步的 JavaScript 和 XML。通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

交互流程:
1–启动 获取XMlHttpRequest对象
2–open 打开url通道,并设置异步传输
3–send 发送数据到服务器
4–服务器接受数据并处理,处理完成后返回结果
5–客户端接收服务器端返回

同步:脚本会停留并等待服务器发送回复然后再继续
异步:脚本允许页面继续其进程并处理可能的回复

ajax的优点
1) 通过异步模式,提升了用户体验
2) 优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用
3)Ajax在客户端运行,承担了一部分本来由服务器承担的工作,减少了大用户量下的服务器负载。

ajax的缺点
1)ajax不支持浏览器back按钮。
2)安全问题 AJAX暴露了与服务器交互的细节。
3)对搜索引擎的支持比较弱。
4)破坏了程序的异常机制。
5)不容易调试。

Ajax的最大的特点是什么。
Ajax可以实现动态不刷新(局部刷新)
readyState属性 状态 有5个可取值: 0=未初始化 ,1=启动 2=发送,3=接收,4=完成

javascript对象的几种创建方式

1,工厂模式
2,构造函数模式
3,原型模式
4,混合构造函数和原型模式
5,动态原型模式
6,寄生构造函数模式
7,稳妥构造函数模式
https://www.cnblogs.com/zczhangcui/p/6389023.html
https://www.jianshu.com/p/206d15c35f3f

javascript继承的6种方法

1,原型链继承

2,借用构造函数继承

3,组合继承(原型+借用构造)

4,原型式继承

5,寄生式继承

6,寄生组合式继承

{
//原型链继承
function SuperType() {
this.property=[“red”, “blue”, “green”]
}
SuperType.prototype.getSuperValue = function () {
return this.property
}
function SubType() {
this.subproperty = false
}
SubType.prototype = new SuperType()
var instance = new SubType()
console.log(instance.getSuperValue())//[“red”, “blue”, “green”]
}
{
//借用构造函数继承
function SuperType() {
this.colors = [‘red’, ‘blue’, ‘green’]
}
function SubType() {
// 继承SuperType
SuperType.call(this)
}
var instance1 = new SubType()
var instance2 = new SubType()
instance1.colors.push(‘black’)
console.log(instance1.colors) [‘red’, ‘blue’, ‘green’,‘black’]
console.log(instance2.colors) [‘red’, ‘blue’, ‘green’]
}
{
//组合继承(原型+借用构造)
function SuperType(name) {
this.name = name
this.colors = [‘red’, ‘blue’, ‘green’]
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, job) {
// 继承属性
SuperType.call(this, name)
this.job = job
}
// 继承方法
SubType.prototype = new SuperType()
SubType.prototype.constructor = SuperType
SubType.prototype.sayJob = function() {
console.log(this.job)
}
var instance1 = new SubType(‘Jiang’, ‘student’)
instance1.colors.push(‘black’)
console.log(instance1.colors) //[“red”, “blue”, “green”, “black”]
instance1.sayName() // ‘Jiang’
instance1.sayJob() // ‘student’
var instance2 = new SubType(‘J’, ‘doctor’)
console.log(instance2.colors) // //[“red”, “blue”, “green”]
instance2.sayName() // ‘J’
instance2.sayJob() // ‘doctor’
}

https://blog.csdn.net/mshkkhhgy/article/details/79476539

** 请解释一下 JavaScript 的同源策略? 为什么要有同源限制?还有它的缺点?**

概念:同源策略是客户端脚本(尤其是Javascript)的重要的安全度量标准。它最早出自Netscape Navigator2.0,其目的是防止某个文档或脚本从多个不同源装载。

这里的同源策略指的是:协议,域名,端口相同,同源策略是一种安全协议。

指一段脚本只能读取来自同一来源的窗口和文档的属性。
我们举例说明:比如一个黑客程序,他利用Iframe把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名,密码登录时,他的页面就可以通过Javascript读取到你的表单中input中的内容,这样用户名,密码就轻松到手了。

缺点:

现在网站的JS 都会进行压缩,一些文件用了严格模式,而另一些没有。这时这些本来是严格模式的文件,被 merge 后,这个串就到了文件的中间,不仅没有指示严格模式,反而在压缩后浪费了字节。

事件、IE与火狐的事件机制有什么区别? 如何阻止冒泡?

① 我们在网页中的某个操作(有的操作对应多个事件)。例如:当我们点击一个按钮就会产生一个事件。是可以被 JavaScript 侦测到的行为。

②事件处理机制:IE是事件冒泡、firefox同时支持两种事件模型,也就是:捕获型事件和冒泡型事件。;

③.ev.stopPropagation();注意旧ie的方法ev.cancelBubble = true;

ajax的缺点和在IE下的问题?

ajax的缺点

1、ajax不支持浏览器back按钮。

2、安全问题 AJAX暴露了与服务器交互的细节。

3、对搜索引擎的支持比较弱。

4、破坏了程序的异常机制。

5、不容易调试。

IE缓存问题

在IE浏览器下,如果请求的方法是GET,并且请求的URL不变,那么这个请求的结果就会被缓存。解决这个问题的办法可以通过实时改变请求的URL,只要URL改变,就不会被缓存,可以通过在URL末尾添加上随机的时间戳参数(‘t’= + newDate().getTime())

或者:
open(‘GET’,‘demo.php?rand=+Math.random()’,true);

Ajax请求的页面历史记录状态问题

可以通过锚点来记录状态,location.hash。让浏览器记录Ajax请求时页面状态的变化。

还可以通过HTML5的history.pushState,来实现浏览器地址栏的无刷新改变

谈谈你对重构的理解

网站重构:在不改变外部行为的前提下,简化结构、添加可读性,而在网站前端保持一致的行为。也就是说是在不改变UI的情况下,对网站进行优化,
在扩展的同时保持一致的UI。

对于传统的网站来说重构通常是:

表格(table)布局改为DIV+CSS

使网站前端兼容于现代浏览器(针对于不合规范的CSS、如对IE6有效的)

对于移动平台的优化

针对于SEO进行优化

深层次的网站重构应该考虑的方面

减少代码间的耦合

让代码保持弹性

严格按规范编写代码

设计可扩展的API

代替旧有的框架、语言(如VB)

增强用户体验

通常来说对于速度的优化也包含在重构中

压缩JS、CSS、image等前端资源(通常是由服务器来解决)

程序的性能优化(如数据读写)

采用CDN来加速资源加载

对于JS DOM的优化

HTTP服务器的文件缓存

说说你对Promise的理解

依照 Promise/A+ 的定义,Promise 有四种状态:

pending: 初始状态, 非fulfilled 或 rejected.

fulfilled: 成功的操作.

rejected: 失败的操作.

settled: Promise已被fulfilled或rejected,且不是pending

另外, fulfilled 与 rejected 一起合称 settled。

Promise 对象用来进行延迟(deferred) 和异步(asynchronous ) 计算。

Promise 的构造函数

构造一个 Promise,最基本的用法如下:
var promise = new Promise(function(resolve, reject) {
if (…) { // succeed
resolve(result);
} else { // fails
reject(Error(errMessage));
}
});
Promise 实例拥有 then 方法(具有 then 方法的对象,通常被称为 thenable)。它的使用方法如下:

promise.then(onFulfilled, onRejected)

接收两个函数作为参数,一个在 fulfilled 的时候被调用,一个在 rejected 的时候被调用,接收参数就是 future,onFulfilled对应 resolve, onRejected 对应 reject。

说说严格模式的限制

变量必须声明后再使用
函数的参数不能有同名属性,否则报错
不能使用with语句
不能对只读属性赋值,否则报错
不能使用前缀0表示八进制数,否则报错
不能删除不可删除的属性,否则报错
不能删除变量delete prop,会报错,只能删除属性delete global[prop]
不能使用arguments.callee
不能使用arguments.caller
不能使用fn.caller和fn.arguments获取函数调用的堆栈
eval不会在它的外层作用域引入变量
eval和arguments不能被重新赋值
arguments不会自动反映函数参数的变化
禁止this指向全局对象
增加了保留字(比如protected、static和interface)
设立”严格模式”的 目的:

1)消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
2) 消除代码运行的一些不安全之处,保证代码运行的安全;
3) 提高编译器效率,增加运行速度;
4) 为未来新版本的Javascript做好铺垫。
注:经过测试IE6,7,8,9均不支持严格模式。

如何删除一个cookie

1.将时间设为当前时间往前一点。
var date = newDate();
date.setDate(date.getDate() - 1);//真正的删除
setDate()方法用于设置一个月的某一天。
2.expires的设置
document.cookie= ‘user=’+ encodeURIComponent(‘name’) + ';
expires = ’ + newDate(0)
<strong><em><b><i>标签
标签和 标签一样,用于强调文本,但它强调的程度更强一些。
em 是 斜体强调标签,更强烈强调,表示内容的强调点。相当于html元素中的<i>...</i>;
< b >< i >是视觉要素,分别表示无意义的加粗,无意义的斜体。
em 和 strong 是表达要素(phraseelements)。

document.write()的用法

document.write()方法可以用在两个方面:页面载入过程中用实时脚本创建页面内容,以及用延时脚本创建本窗口或新窗口的内容。
document.write只能重绘整个页面。innerHTML可以重绘页面的一部分
编写一个方法求一个字符串的字节长度
假设:一个英文字符占用一个字节,一个中文字符占用两个字节
functionGetBytes(str){
var len = str.length;
var bytes = len;
for(var i=0; i<len; i++){
if (str.charCodeAt(i) >255) bytes++;
}
return bytes;
}
alert(GetBytes(“hello,as”);

git fetch和git pull的区别

git pull:相当于是从远程获取最新版本并merge到本地
git fetch:相当于是从远程获取最新版本到本地,不会自动merge

请解释什么是事件代理

事件代理(Event Delegation),又称之为事件委托。是 JavaScript 中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是可以提高性能。

attribute和property的区别是什么?

attribute是dom元素在文档中作为html标签拥有的属性;
property就是dom元素在js中作为对象拥有的属性。

所以:
对于html的标准属性来说,attribute和property是同步的,是会自动更新的,
但是对于自定义的属性来说,他们是不同步的,

axios有什么特点和常用方法有那些?
特点:
1)Axios 是一个基于 promise 的 HTTP 库,支持promise所有的API
2)它可以拦截请求和响应
3)它可以转换请求数据和响应数据,并对响应回来的内容自动转换
成 JSON类型的数据
4)安全性更高,客户端支持防御 XSRF

常用方法:
1)axios.get(url[, config]) //get请求用于列表和信息查询
2)axios.delete(url[, config]) //删除
3)axios.post(url[, data[, config]]) //post请求用于信息的添加
4)axios.put(url[, data[, config]]) //更新操作

去重
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
计算i,j值
{
// 标签为foo的循环
foo: for (var i=0; i<4; i++) {
// console.log(i)//0,1,2,3
for (var j=0; j<4; j++) {
// 如果j和i相等,继续外层循环
if (j== i) {
// 跳转到foo的下一个循环
continue foo;
}
// 跳过奇数结果
if ((j * i) % 2 == 1) { //(3,1)
// 继续内层循环(没有标签的)
continue;
}
console.log( i, j );
}
}
// 1 0
// 2 0
// 2 1
// 3 0
// 3 2
}

找出数组中重复数字
function findSame(arr) {
let map = {}, res=[];
for (let i = 0; i < arr.length; i++) {
if (!map[arr[i]]) {
map[arr[i]] = 1;
} else {
map[arr[i]]++;
}
}
for (let i in map) {
if (map[i] > 1) {
res.push(i);
}
}
return res;
}
var arr12 = [1,5,2,3,1,3,4]
console.log(findSame(arr12))

全排列
//字符串全排列1
function Permutation(str)
{
let res=[],pStr="";
if(str.length<=0) return res;
arr=str.split("");//将字符串转化为字符数组
res=permutate(arr,pStr,res);
return res;
}
function permutate(arr,pStr,res){
if(arr.length==0){
return res.push(pStr);
}else{
let isRepeated=new Set();
for(let i=0;i<arr.length;i++){
if(!isRepeated.has(arr[i])){//避免相同的字符交换
let char=arr.splice(i,1)[0];
pStr+=char;
permutate(arr,pStr,res);
arr.splice(i,0,char);//恢复字符串,回溯
pStr=pStr.slice(0,pStr.length-1);//回溯
isRepeated.add(char);
}
}
}
return res;
}
console.log(Permutation(‘abc’))

//字符串全排列2
function Permutation1(str)
{
let res=[];
if(str.length<=0) return res;
arr=str.split("");//将字符串转化为字符数组
res=permutate(arr,’’,res);
res=[…new Set(res)];//去重
res.sort();//排序
return res;
}
function permutate1(arr,index,res){
if(arr.length==index){
let s="";
for(let i=0;i<arr.length;i++){
s+=arr[i];
}
return res.push(s);
}else{
for(let i=index;i<arr.length;i++){
[arr[index],arr[i]]=[arr[i],arr[index]];//交换
permutate(arr,index+1,res);
[arr[index],arr[i]]=[arr[i],arr[index]];//交换
}
}
return res;
}
console.log(Permutation1(‘acd’))

通过得到这个元素的索引,使用js数组自己固有的函数去删除某元素:
Array.prototype.remove = function(val) {
var index = this.indexOf(val);
console.log(index)//3
if (index > -1) {
this.splice(index, 1);
}
};
var emp = [‘abs’,‘dsf’,‘sdf’,‘fd’]
emp.remove(‘fd’);
console.log(emp)

把对象转变成数组
法一:
var obj={
};
var arr2 = [] //定义数组
for (var i in obj) {
arr2.push(obj[i]);
}
console.log(arr2);
// 法二:
var obj={
};
var arr3 = Object.values(obj);
console.log(arr3);

最大,最小值,有两种方法(apply,call) 前者是未知参数,且参数还是数组;后者是知道参数的
var numbers=[5,458,120,-215];
var minInnumbers = Math.min.apply(this,numbers);//math为对象,min()/max()为方法
console.log(minInnumbers)
var maxInnumbers = Math.max.call(this,5,458,120,-215);
console.log(maxInnumbers)
判断一个数组元素是不是在数组里面
var arr = [‘a’,‘s’,‘d’,‘f’];
console.log(isInArray(arr,‘g’));//调用
function isInArray(arr,value){
for(var i = 0; i < arr.length; i++){
if(value === arr[i]){
return true;
}
}
return false;
}

金字塔
for(var n=3,i=0;i<=n;i++){
//创建一个四行的*号排列//0,1,2,3
// i=0是初始化部分;i<3是循环判断条件部分(当满足此条件时才进入执行for循环中的语句);i++是执行完循环体语句后的操作
for(var j=0;j<n-i;j++){
//计算*号前的空格//j<=3-(0,1,2,3)==3,2,1,0
// console.log(j)
document.write(" &nbsp");
}
for(var j=0;j<=i2;j++){
//计算每行出现的
号//0,012,01234,0123456
document.write("*");
}
document.write("<br/>");
}

去空格
使用trim或replace方法
trim的缺点:可以去两边不可以去中间
var stringValue = " hello world “;
var trimmedStringValue = stringValue.trim();
console.log(trimmedStringValue); //“hello world”
stringValue1 = stringValue.replace(/\s*/g,”");// 去除所有空格
console.log(stringValue1);
stringValue2 = stringValue.replace(/^\s*|\s*$/g,"");//去除两头空格
console.log(stringValue2);
stringValue3 = stringValue.replace( /^\s*/, “”);//去除左空格
console.log(stringValue3);
stringValue4 = stringValue.replace(/(\s*$)/g, “”);//去除右空格
console.log(stringValue4);

怎么判断两个对象相等?
obj={
a:1,
b:2
}
obj2={
a:1,
b:2
}
obj3={
a:1,
b:3
}
console.log(JSON.stringify(obj)==JSON.stringify(obj2));//true
console.log(JSON.stringify(obj)==JSON.stringify(obj3));//false

一行代码实现数组去重?
[…new Set([1,2,3,1,‘a’,1,‘a’])]

使用addEventListener点击li弹出内容,并且动态添加li之后有效
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>

var ulNode = document.getElementById(“ul”);
ulNode.addEventListener(‘click’, function (e) {
if (e.target && e.target.nodeName.toUpperCase() == “LI”) {
alert(e.target.innerHTML);
}
}, false);

实现一下双向绑定?
<body>
<div id="app">
<input type="text" id="txt">
<p id="show-txt">``</p>
</div>
<script>
var obj = {}
Object.defineProperty(obj, 'txt', {
get: function () {
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue
document.getElementById('show-txt').innerHTML = newValue
}
})
document.addEventListener('keyup', function (e) {
obj.txt = e.target.value
})
</script>
</body>

重排和重绘

部分渲染树(或者整个渲染树)需要重新分析并且节点尺寸需要重新计算。这被称为重排。注意这里至少会有一次重排-初始化页面布局。
由于节点的几何属性发生改变或者由于样式发生改变,例如改变元素背景色时,屏幕上的部分内容需要更新。这样的更新被称为重绘。

什么情况会触发重排和重绘?
添加、删除、更新 DOM 节点
display: none 隐藏一个 DOM 节点-触发重排和重绘
通过 visibility: hidden 隐藏一个 DOM 节点-只触发重绘,因为没有几何变化
移动或者给页面中的 DOM 节点添加动画
添加一个样式表,调整样式属性
用户行为,例如调整窗口大小,改变字号,或者滚动。

const与let与var的区别?
const一般用于定义一个常量,如果定义好了,在全局(不管那里,都不能再定义,否者报错)
let 可以用来定义多种,只是在同一块级作用域内不可以重复定义否者报错,注意,声明变量的时候记得赋值;要么在严格模式下也会报错;
var 可以多次声明,当时后面声明的值会覆盖前面声明 的值,声明的时候不赋值的时候不会报错

使用js实现一个持续的动画效果

1)动画实现
var e = document.getElementById(‘e’)
e.style.position = “absolute”;
console.log(‘e’,e)
var falg = true;
var left = 0;
setInterval(() => {
left == 0 ? falg = true : left = = 100 ? falg = false : ‘’
falg ? e.style.left = ${left++}px : e.style.left = ${left--}px(加``)
}, 1000 / 60)

2)使用requestAnimationFrame。
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();

var e = document.getElementById(“e”);
var flag = true;
var left = 0;

function render() {
left == 0? flag = true : left = = 100 ? flag = false : ‘’;
flag ? e.style.left = ${left++}px :
e.style.left = ${left--}px;(加``)
}

(function animloop() {
render();
requestAnimFrame(animloop);
})()

this 指向的问题
普通函数的this是由动态作用域决定,它总指向于它的直接调用者。具体可以分为以下四项:
this总是指向它的直接调用者, 例如 obj.func() ,那么func()里的this指的是obj。
在默认情况(非严格模式,未使用 ‘use strict’),如果函数没有直接调用者,this为window
在严格模式下,如果函数没有直接调者,this为undefined
使用call,apply,bind绑定的,this指的是绑定的对象。
vue中,this如果指向data,就this.data拿数据/或者给data赋值,如果指向一个函数或者方法,同样this.什么什么就好
小程序中,this跟vue差不多,只是赋值的时候不是data,而是setData({}),如this.setData({name:‘wjz’})

WeakMap 和 Map 的区别?
WeakMap 结构与 Map 结构基本类似,唯一的区别是它只接受对象作为键名( null 除外),不接受其他类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。
WeakMap 最大的好处是可以避免内存泄漏。一个仅被 WeakMap 作为 key 而引用的对象,会被垃圾回收器回收掉。
WeakMap 拥有和 Map 类似的 set(key, value) 、get(key)、has(key)、delete(key) 和 clear() 方法, 没有任何与迭代有关的属性和方法。

执行顺序:
&& || ? : ?后先执行

bind的用法:

语法
fun.bind(this,arg1,arg2,…)
bind()方法会创建一个新的函数,称为绑定函数,fun方法在this环境下调用
该方法可传入两个参数,第一个参数作为this,第二个及以后的参数则作为函数的参数调用

1) 创建绑定函数(使用bind把函数绑定到特定的所需的环境下。)
this.a = 1;
var module = {
a : 2,
getA:function() {
return this.a;
}
};
module.getA();//2
var getA1 = module.getA;
// getA在外部调用,此时的this指向了全局对象
getA1();//1

// 再把getA1方法绑定到module环境上
var getA2 = getA1.bind(module);
getA2();

2) 让函数拥有预设的参数

使用bind()方法使函数拥有预设的初始参数,这些参数会排在最前面,传给绑定函数的参数会跟在它们后面

function list(){
// 让类数组arguments拥有数组的方法slice,这个函数实现了简单把类数组转换成数组
return Array.prototype.slice.call(arguments);
}

list(1,2,3);//[1,2,3]

//给list绑定一个预设参数4
var list1 = list.bind(undefined,4);

list1();//[4]

list1(1,2,3);//[4,1,2,3]

3)setTimeout的使用

正常情况下,调用setTimeout的时候this会指向全局对象,但是使用类的方法时我们需要指向类的实例,所以要把this,绑定要回调函数方便继续使用实例
function Fun1() {
this.name = 1;
}
Fun1.prototype.fun2 = function() {
window.setTimeout(this.fun3.bind(this), 1000);
}
Fun1.prototype.fun3 = function(){
console.log(‘name:’+this.name);//name:1
}
var fun = new Fun1();
fun.fun2();

4) 快捷方法–把类数组转换成数组

第一种方法是使用apply方法
function fun1() {
var slice = Array.prototype.slice;
return slice.apply(arguments);
}
fun1(1,2,3);//[1,2,3]

第二种方法是使用call方法和bind方法一起使用
function fun2() {
var unboundSlice = Array.prototype.slice;
// 把函数的call方法绑定在数组slice方法上,之后再给call方法传递参数
var slice = Function.prototype.call.bind(unboundSlice);
return slice(arguments);
}

fun2(1,2,3);//[1,2,3]

** object 与 map 异同?如何选择 Map 或 Object**
object 通常有原型即对象实例有 prototype 属性,Map 无 prototype 属性。虽然 ES5 开始可以使用 map = Object.create(null) 创建无 prototype 的对象。
Map 的键名可以是对象、原始值或二者的结合,而对象的属性只能是 string 或 symbols 类型(Symbol 类型为 ES6 新的基础数据类型)。
Map 使用 size 属性可以非常用以获取键值对个数。而对象仅能手动确认。

如果你需要解决下面这些问题,那么果断拥抱 Map:

  • 在运行之前 key 是否是未知的,是否需要动态地查询 key 呢?
  • 是否所有的值都是统一类型,这些值可以互换么?
  • 是否需要不是字符串类型的 key ?
  • 键值对经常增加或者删除么?
  • 是否有任意个且非常容易改变的键值对?
  • 这个集合可以遍历么(Is the collection iterated)?

Object.keys()方法会返回一个由一个给定对象的自身可枚举属性组成的数组。
var data={a:1,b:2,c:9,d:4,e:5};
console.log(data);//{a: 1, b: 2, c: 9, d: 4, e: 5}
console.log(Object.keys(data));//[“a”, “b”, “c”, “d”, “e”]
Object.keys(data).map((key,item)=>{
console.log(key,data[key]);//key=>属性名 data[key]=>属性值
// a 1
// b 2
// c 9
// d 4
// e 5
});

substring(), substr(),splice(),split()的区别
substring(start,stop):
不包括stop;如果前者参数大于后者参数,它会自动换位计算//substring(2,4)==substring(4,2)
substr(start,length)
splice()
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
split():字符串转数组
"2:3:4:5".split(":") //将返回["2", "3", "4", "5"]
"|a|b|c".split("|") //将返回["", "a", "b", "c"]
"hello".split("") //可返回["h", "e", "l", "l", "o"]
"hello".split("", 3) //可返回 ["h", "e", "l"]

介绍下 Set、Map、WeakSet 和 WeakMap 的区别?
1)Set:
①成员唯一、无序且不重复;
②[value, value],键值与键名是一致的(或者说只有键值,没有键名);
③可以遍历,方法有:add、delete、has。

2)WeakSet:
①成员都是对象;
②成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM 节点,不容易造成内存泄漏;
③不能遍历,方法有 add、delete、has。

3)Map
①本质上是键值对的集合,类似集合;
②可以遍历,方法很多,可以跟各种数据格式转换。

4)WeakMap
只接受对象最为键名(null 除外),不接受其他类型的值作为键名;
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的;
不能遍历,方法有 get、set、has、delete。

把url地址参数转为对象
function getUrlkey(url) {
var params = {};
var urls = url.split("?");
var arr = urls[1].split("&");
for (var i = 0, l = arr.length; i < l; i++) {
var a = arr[i].split("=");
params[a[0]] = a[1];
}
return params;
}
提取字符串出现最多的字符
const str = ‘abbbbbcdddeee’
let obj = str.split(’’).reduce((prev,next,index)=>{
if(prev[next]){
prev[next]++
}else{
prev[next]=1
}
return prev
},{})
在这里插入图片描述

let max = 0;
for(let key in obj){
if(max<obj[key]){
max = obj[key]
}
}
for(let key in obj){
if(obj[key]==max){
console.log(‘最多的字符是’+key);
console.log(‘出现的次数是’+max)
}
}
手机号验证:
function isPhone(tel)
{
var regx = /^1[34578]\d{9} $ /;
return regx.test(tel);
}
邮箱验证:
var email = this.value;
var reg = /^([a-zA-Z]|[0-9])( \ w| -)+@[a-zA-Z0-9]+.([a-zA-Z]{2,4})$/;
if(reg.test(email)){
alert(“邮箱格式正确”);
}else{
alert(“邮箱格式不正确”);
}

写一个sum(1,2),sum(1)(2)例子
function sum() {
var num = arguments[0];
if (arguments.length == 1) {
return function (sec) {
return num + sec;
}
} else {
var num = 0;
for (var i = 0; i < arguments.length; i++) {
num = num + arguments[i];
}
return num;
}
}
console.log(sum(1,2),sum(1)(2),sum(1,2,3))// 3 3 6

HTTP协议

HTTP 报文结构:起始行 + 头部 + 空行 + 实体
GET /home HTTP/1.1 (方法 + 路径 + http版本。)

urI结构:
协议 : // 主机名(host):端口 (port) 请求路径(path)?请求参数(query)

问: 如果说在头部中间故意加一个空行会怎么样?
==>那么空行后的内容全部被视为实体。

HTTP有哪些方法?
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT

这些方法的具体作用是什么?
GET: 通常用于请求服务器发送某些资源
HEAD: 请求资源的头部信息, 并且这些头部与 HTTP GET 方法请求时返回的一致. 该请求方法的一个使用场景是在下载一个大文件前先获取其大小再决定是否要下载, 以此可以节约带宽资源
OPTIONS: 用于获取目的资源所支持的通信选项
POST: 发送数据给服务器
PUT: 用于新增资源或者使用请求中的有效负载替换目标资源的表现形式
DELETE: 用于删除指定的资源
PATCH: 用于对资源进行部分修改
CONNECT: HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器
TRACE: 回显服务器收到的请求,主要用于测试或诊断

GET和POST有什么区别?
数据传输方式不同:GET请求通过URL传输数据,而POST的数据通过请求体传输。
安全性不同:POST的数据因为在请求主体内,所以有一定的安全性保证,而GET的数据在URL中,通过历史记录,缓存很容易查到数据信息。
数据类型不同:GET只允许 ASCII 字符,而POST无限制
GET无害: 刷新、后退等浏览器操作GET请求是无害的,POST可能重复提交表单
特性不同:GET是安全(这里的安全是指只读特性,就是使用这个方法不会引起服务器状态变化)且幂等(幂等的概念是指同一个请求方法执行多次和仅执行一次的效果完全相同),而POST是安全非幂等
PUT和POST都是给服务器发送新增资源,有什么区别?

PUT 和POST方法的区别是,PUT方法是幂等的:连续调用一次或者多次的效果相同(无副作用),而POST方法是非幂等的。举个例子,我们在开发一个博客系统,当我们要创建一篇文章的时候往往用POST https://www.jianshu.com/articles,这个请求的语义是,在articles的资源集合下创建一篇新的文章,如果我们多次提交这个请求会创建多个文章,这是非幂等的。
而PUT https://www.jianshu.com/articles/820357430的语义是更新对应文章下的资源(比如修改作者名称等),这个URI指向的就是单一资源,而且是幂等的,比如你把『刘德华』修改成『蔡徐坤』,提交多少次都是修改成『蔡徐坤』
(https://juejin.im/post/5d032b77e51d45777a126183)

PUT和PATCH都是给服务器发送修改资源,有什么区别?
PUT和PATCH都是更新资源,而PATCH用来对已知资源进行局部更新。

客户端向服务器发送请求的报文时使用的首部
Accept 客户端或者代理能够处理的媒体类型 ✨
Accept-Encoding 优先可处理的编码格式
Accept-Language 优先可处理的自然语言
Accept-Charset 优先可以处理的字符集
If-Match 比较实体标记(ETage) ✨
If-None-Match 比较实体标记(ETage)与 If-Match相反 ✨
If-Modified-Since 比较资源更新时间(Last-Modified)✨
If-Unmodified-Since比较资源更新时间(Last-Modified),与 If-Modified-Since相反 ✨
If-Rnages 资源未更新时发送实体byte的范围请求
Range 实体的字节范围请求 ✨
Authorization web的认证信息 ✨
Proxy-Authorization 代理服务器要求web认证信息
Host 请求资源所在服务器 ✨
From 用户的邮箱地址
User-Agent 客户端程序信息 ✨
Max-Forwrads 最大的逐跳次数
TE 传输编码的优先级
Referer 请求原始放的url
Expect 期待服务器的特定行为

100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息
200 OK 正常返回信息
201 Created 请求成功并且服务器创建了新的资源
202 Accepted 请求已接受,但是还没执行,不保证完成请求
301 Moved Permanently 请求的网页已永久移动到新位置。
302 Found 临时性重定向。
303 SeeOther 临时性重定向,且总是使用 GET 请求新的 URI。
304 Not Modified 自从上次请求后,请求的网页未修改过。
400 BadRequest 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。
401 Unauthorized 请求未授权。
403Forbidden 禁止访问。
404 NotFound 找不到如何与 URI 相匹配的资源。
500 InternalServer Error 表示服务器端在执行请求时发生了错误
503 ServiceUnavailable 表明服务器暂时处于超负载或正在停机维护,无法处理请求。
HTTP2相对于HTTP1.x有什么优势和特点?
1)HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。
2)服务器推送;3)头部压缩;4)多路复用
vue开启gzip:
https://blog.csdn.net/u013788943/article/details/79786558?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

HTTP请求
1、GET;2、POST;3、PUT;4、DELETE;5、HEAD;6、TRACE;7、OPTIONS;
GET方法:对这个资源的查操作。
DELETE方法:对这个资源的删操作。(注意:客户端无法保证删除操作一定会被执行,因为HTTP规范允许服务器在不通知客户端的情况下撤销请求。)
HEAD方法:与GET方法的行为很类似,但服务器在响应中只返回实体的主体部分。这就允许客户端在未获取实际资源的情况下,对资源的首部进行检查,使用HEAD,我们可以更高效的完成以下工作:在不获取资源的情况下,了解资源的一些信息,比如资源类型;通过查看响应中的状态码,可以确定资源是否存在;通过查看首部,测试资源是否被修改;
TRACE方法:会在目的服务器端发起一个“回环”诊断,我们都知道,客户端在发起一个请求时,这个请求可能要穿过防火墙、代理、网关、或者其它的一些应用程序。这中间的每个节点都可能会修改原始的HTTP请求,TRACE方法允许客户端在最终将请求发送服务器时,它变成了什么样子。由于有一个“回环”诊断,在请求最终到达服务器时,服务器会弹回一条TRACE响应,并在响应主体中携带它收到的原始请求报文的最终模样。这样客户端就可以查看HTTP请求报文在发送的途中,是否被修改过了。
OPTIONS方法:用于获取当前URL所支持的方法。若请求成功,则它会在HTTP头中包含一个名为“Allow”的头,值是所支持的方法,如“get, post”。
put和post区别?
put和post都有更改指定URI的语义.但put被定义为idempotent的方法,post则不是.idempotent的方法:如果一个方法重复执行多次,产生的效果是一样的,那就是idempotent的。也就是说:
put请求:如果两个请求相同,后一个请求会把第一个请求覆盖掉。(所以put用来改资源)
post请求:后一个请求不会把第一个请求覆盖掉。(所以post用来增资源)
get和post区别
① get参数通过url传递,post放在Request body中。
② get请求会被浏览器主动cache,而post不会,除非手动设置。
③ get请求参数会被完整保留在浏览器历史记录里,而post中的参数不会被保留。
④ get 请求中有非 ASCII 字符,会在请求之前进行转码,post不用,因为post在Request body中,通过 MIME,也就可以传输非 ASCII 字符。
⑤ 一般我们在浏览器输入一个网址访问网站都是get请求
⑥ HTTP的底层是TCP/IP。HTTP只是个行为准则,而TCP才是get和post怎么实现的基本。get/post都是TCP链接。get和post能做的事情是一样一样的。但是请求的数据量太大对浏览器和服务器都是很大负担。所以业界有了不成文规定,(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。
⑦ get产生一个TCP数据包;post产生两个TCP数据包。对于get方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于post,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
⑧ 在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。但并不是所有浏览器都会在post中发送两次包,Firefox就只发送一次。
一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?

简洁版:

浏览器根据请求的URL交给DNS域名解析,找到真实的IP,向服务器发起请求;
服务器交给后台处理完成后返回数据,浏览器接收文件(HTML、CSS、JavaScript等);
浏览器对加载到的资源(HTML、CSS、JavaScript等)进行语法解析,构建相应的内部数据结构(DOM树、CSS树、render树等);
载入解析到的资源文件、渲染页面、完成。
详细版:

1.首先浏览器开启一个线程来处理这个请求,对URL分析判断,如果是http协议就按照Web方式来处理;
2.其次浏览器会对URL进行解析,一般包括(协议头、主机域名或IP地址、端口号、请求路径、查询参数、hash等),然后开启网络线程发出一个完整到http请求;
3.当然一般我们输入的URL是服务器域名,这时就需要DNS通过域名查询得到对应的IP;
4.DNS首先会查看浏览器DNS缓存,没有就查询计算机本地DNS缓存,还没有就询问递归式DNS服务器(即网络提供商,一般这个服务器都会有自己的缓存,所以IP查询一般在这里完成),如果没有缓存,那就需要通过根域名和TLD域名服务器指到对应的权威DNS服务器找回记录,并缓存到递归式服务器,然后递归服务器在将记录返回给本地。
5.有了IP地址,此时网络层便会通过IP地址寻的对应服务器的物理地址
6.寻得服务器地址,客户端在网络传输层便可以和服务器通过三次握手建立tcpip连接
7.连接建立后网络数据链路层将数据包装成帧;
8.最后物理层利用物理介质进行传输;
9.到了服务器,就会通过相反的方式将数据一层一层的还原回去;
10.请求到了后台服务器,一般会有统一的验证,如安全验证、跨域验证等,验证未通过就直接返回相应的http报文
11.验证通过后,就会进入后台代码,此时程序收到请求,然后执行对应的操作(如查询数据库等);
12.如果浏览器访问过,且缓存上有对应的资源,便会与服务器最后修改时间对比,一致便返回304,告诉浏览器可使用本地缓存;
13.前端浏览器接收到响应成功的报文后便开始下载网页
14.下载完的网页将被交给浏览器内核(渲染进程)进行处理:

1.根据顶部定义的DTD类型进行对应的解析方式;
2.渲染进程内部是多线程的,网页的解析将会被交给内部的GUI渲染线程处理;
3.首先渲染线程中的HTML解释器,将HTML网页和资源从字节流解释转换成字符流;
4.再通过词法分析器将字符流解释成词语;
5.之后经过语法分析器根据词语构建成节点;6.最后通过这些节点组建一个DOM树;
这个过程中,如果遇到的DOM节点是JavaScript代码,就会调用JavaScript引擎对JavaScript代码进行解释执行,此时由JavaScript引擎和GUI渲染线程的互斥,GUI渲染线程就会被挂起,渲染过程停止;如果JavaScript代码的运行中对DOM树进行了修改,那么DOM的构建需要从新开始;
7.如果节点需要依赖其他资源,如(图片,CSS等),便会调用网络模块的资源加载器来加载它们,但它们是异步的,不会阻塞当前DOM树的构建;
8.如果遇到的是JavaScript资源URL(没有标记异步),则需要停止当前DOM的构建,直到JavaScript的资源加载并被JavaScript引擎执行后才继续构建DOM;
9.对于CSS,CSS解释器会将CSS文件解释成内部表示结构,生成CSS规则树;
10.然后合并CSS规则树和DOM树,生成render渲染树;
11.最后对render树进行布局和绘制,并将结果通过IO线程传递给Browser控制进程进行显示。

浏览器缓存
浏览器缓存分为强缓存和协商缓存。当客户端请求某个资源时,获取缓存的流程如下:
先根据这个资源的一些 http header 判断它是否命中强缓存,如果命中,则直接从本地获取缓存资源,不会发请求到服务器;当强缓存没有命中时,客户端会发送请求到服务器,服务器通过另一些request header验证这个资源是否命中协商缓存,称为http再验证,如果命中,服务器将请求返回,但不返回资源, 而是告诉客户端直接从缓存中获取,客户端收到返回后就会从缓存中获取资源;强缓存和协商缓存共同之处在于,如果命中缓存,服务器都不会返回资源;区别是,强缓存不对发送请求到服务器,但协商缓存会。当协商缓存也没命中时,服务器就会将资源发送回客户端。当 ctrl+f5 强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;当 f5 刷新网页时,跳过强缓存,但是会检查协商缓存;
强缓存
Expires(该字段是 http1.0 时的规范,值为一个绝对时间的 GMT 格式的时间字符串,代表缓存资源的过期时间)
Cache-Control:max-age(该字段是 http1.1 的规范,强缓存利用其 max-age 值来判断缓存资源的最大生命周期,它的值单位为秒)
协商缓存
• Last-Modified(值为资源最后更新时间,随服务器response返回)
• If-Modified-Since(通过比较两个时间来判断资源在两次请求期间是否有过修改,如果没有修改,则命中协商缓存)
• ETag(表示资源内容的唯一标识,随服务器response返回)
• If-None-Match(服务器通过比较请求头部的If-None-Match与当前资源的ETag是否一致来判断资源是否在两次请求之间有过修改,如果没有修改,则命中协商缓存)

如何确定服务端开启了gzip
1、客户端请求中增加Accept-Encoding: gzip表示客户端支持gzip; 2、服务端接收到请求后,将结果通过gzip压缩后返回给客户端并在响应头中增加Content-Encoding:gzip 表示响应数据已被压缩 3、客户端接收请求,响应头中有Content-Encodin:gzip表示数据需解压处理

HTTP和HTTPS

HTTP协议通常承载于TCP协议之上,在HTTP和TCP之间添加一个安全协议层(SSL或TSL),这个时候,就成了我们常说的HTTPS。

默认HTTP的端口号为80,HTTPS的端口号为443。

为什么HTTPS安全

因为网络请求需要中间有很多的服务器路由器的转发。中间的节点都可能篡改信息,而如果使用HTTPS,密钥在你和终点站才有。https之所以比http安全,是因为他利用ssl/tls协议传输。它包含证书,卸载,流量转发,负载均衡,页面适配,浏览器适配,refer传递等。保障了传输过程的安全性

什么是Etag?

当发送一个服务器请求时,浏览器首先会进行缓存过期判断。浏览器根据缓存过期时间判断缓存文件是否过期。

情景一:若没有过期,则不向服务器发送请求,直接使用缓存中的结果,此时我们在浏览器控制台中可以看到 200 OK(from cache) ,此时的情况就是完全使用缓存,浏览器和服务器没有任何交互的。

情景二:若已过期,则向服务器发送请求,此时请求中会带上①中设置的文件修改时间,和Etag

然后,进行资源更新判断。服务器根据浏览器传过来的文件修改时间,判断自浏览器上一次请求之后,文件是不是没有被修改过;根据Etag,判断文件内容自上一次请求之后,有没有发生变化

情形一:若两种判断的结论都是文件没有被修改过,则服务器就不给浏览器发index.html的内容了,直接告诉它,文件没有被修改过,你用你那边的缓存吧—— 304 Not Modified,此时浏览器就会从本地缓存中获取index.html的内容。此时的情况叫协议缓存,浏览器和服务器之间有一次请求交互。

情形二:若修改时间和文件内容判断有任意一个没有通过,则服务器会受理此次请求,之后的操作同①

① 只有get请求会被缓存,post请求不会

Expires和Cache-Control

Expires要求客户端和服务端的时钟严格同步。HTTP1.1引入Cache-Control来克服Expires头的限制。如果max-age和Expires同时出现,则max-age有更高的优先级。

Cache-Control:no-cache, private, max-age=0
ETag: abcde
Expires: Thu, 15 Apr 201420:00:00 GMT
Pragma: private
Last-Modified:$now //RFC1123 format

说说TCP传输的三次握手四次挥手策略

为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送 后的情况置之不理,它一定会向对方确认是否成功送达。
三次握手?
1.建立连接时,客户端发送syn包到服务器,等待服务器确认;
2.服务器收到syn包,必须确认客户的SYN,同时自己也发送SYN+ACK包返回给客户端;
3.户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,完成三次握手。

四次挥手?

1)客户端释放报文后停止发送数据。其中释放数据报文首部( FIN报文段即使不携带数据,也要消耗一个序号。)
2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当服务器端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我器端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

TCP和UDP的区别
TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来;
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去!
UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。
https://www.cnblogs.com/xiaomayizoe/p/5258754.html

ETag应用:
Etag由服务器端生成,客户端通过If-Match或者说If-None-Match这个条件判断请求来验证资源是否修改。常见的是使用If-None-Match。请求一个文件的流程可能如下:

第一次请求
1.客户端发起 HTTP GET 请求一个文件;
2.服务器处理请求,返回文件内容和一堆Header,当然包括Etag(例如"2e681a-6-5d044840")(假设服务器支持Etag生成和已经开启了Etag).状态码200

第二次请求
客户端发起 HTTP GET 请求一个文件,注意这个时候客户端同时发送一个If-None-Match头,这个头的内容就是第一次请求时服务器返回的Etag:2e681a-6-5d0448402.服务器判断发送过来的Etag和计算出来的Etag匹配,因此If-None-Match为False,不返回200,返回304,客户端继续使用本地缓存;流程很简单,问题是,如果服务器又设置了Cache-Control:max-age和Expires呢,怎么办

答案是同时使用,也就是说在完全匹配If-Modified-Since和If-None-Match即检查完修改时间和Etag之后,

服务器才能返回304.(不要陷入到底使用谁的问题怪圈)

为什么使用Etag请求头?

Etag 主要为了解决 Last-Modified 无法解决的一些问题。

常见web安全及防护原理
sql注入原理
就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
(一般简单的请求是不会有的,一般是动态查询会发生)
总的来说有以下几点:
1.永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。
2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4.不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。
https://www.cnblogs.com/csguo/p/7499171.html

** XSS原理及防范**
Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者javascript代码。比如:攻击者在论坛中放一个看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。

XSS防范方法

首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。

首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。

其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。

如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。

尽量采用POST 而非GET 提交表单

XSS与CSRF有什么区别吗?

XSS是获取信息,不需要提前知道其他用户页面的代码和数据包。CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。

要完成一次CSRF攻击,受害者必须依次完成两个步骤:

登录受信任网站A,并在本地生成Cookie。

在不退出A的情况下,访问危险网站B。

CSRF的防御
丶 服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。
丶通过验证码的方法

** 关于Http 2.0 你知道多少?**

HTTP/2引入了“服务端推(server push)”的概念,它允许服务端在客户端需要数据之前就主动地将数据发送到客户端缓存中,从而提高性能。
HTTP/2提供更多的加密支持
HTTP/2使用多路技术,允许多个消息在一个连接上同时交差。
它增加了头压缩(header compression),因此即使非常小的请求,其请求和响应的header都只会占用很小比例的带宽。

常见的浏览器内核有哪些?
chrome blink webkit / FireFox Gecko/IE trident/safari webkit

浏览器是如何渲染UI的?
1.浏览器收到Html后,会把它解析成DOM tree
2.与此同时把css解析成style rules
3.再把上面合并成render tree
4.然后进入布局阶段,也就是把把每个节点分配到相对应的屏幕位置上
5.随后调用GPU进行绘制(paint),再遍历render tree节点,将元素呈现出来

vue部分

an1:.那你能讲一讲MVVM吗?
MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。

an2:简单说一下Vue2.x响应式数据原理
Vue在初始化数据时,会使用Object.defineProperty重新定义data中的所有属性,当页面使用对应属性时,首先会进行依赖收集(收集当前组件的watcher)如果属性发生变化会通知相关依赖进行更新操作(发布订阅)。

an3:那你知道Vue3.x响应式数据原理吗?
Vue3.x改用Proxy替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
Proxy只会代理对象的第一层,那么Vue3又是怎样处理这个问题的呢?
判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?
我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。

an4:再说一下vue2.x中如何监测数组变化
使用了函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。

an5: nextTick知道吗,实现原理是什么?
DOM更新完毕之后执行一个回调。nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用
PromiseMutationObserversetImmediate如果以上都不行则采用setTimeout
定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。
(关于宏任务和微任务以及事件循环可以参考我的另两篇专栏)

an6:.说一下Vue的生命周期
beforeCreate是new Vue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。
created在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$ nextTick来访问Dom。
beforeMount发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。
mounted在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
beforeUpdate发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。
updated发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
activited keep-alive组件被激活时调用
deactivated keep-alive组件被销毁时调用
beforeDestroy发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。
destroyed发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。

an7:.你的接口请求一般放在哪个生命周期中?
接口请求一般放在mounted中,但需要注意的是服务端渲染时不支持mounted,需要放到created中。
an8:.再说一下Computed和Watch
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

an9:.说一下v-if和v-show的区别
当条件不成立时,v-if不会渲染DOM元素,v-show操作的是样式(display),切换当前DOM的显示和隐藏。

an10:.组件中的data为什么是一个函数?
一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。如果data是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。

an11: 说一下v-model的原理
v-model本质就是一个语法糖,可以看成是value + input方法的语法糖。
可以通过model属性的prop和event属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性。

an12: Vue事件绑定原理说一下
原生事件绑定是通过addEventListener绑定给真实元素的,组件事件绑定是通过Vue自定义的$on实现的。

an13: Vue模版编译原理知道吗,能简单说一下吗?
简单说,Vue的编译过程就是将template转化为render函数的过程。会经历以下阶段:
生成AST树优化codegen
首先解析模版,生成AST语法树(一种用JavaScript对象的形式来描述整个模板)。
使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。
Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的DOM也不会变化。那么优化过程就是深度遍历AST树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。
编译的最后一步是将优化后的AST树转换为可执行的代码。

an14: Vue2.x和Vue3.x渲染器的diff算法分别说一下
Vue的diff算法是基于snabbdom改造过来的,仅在同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。因为跨层级的操作是非常少的,忽略不计,这样时间复杂度就从O(n3)变成O(n)。
diff 算法包括几个步骤:
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把所记录的差异应用到所构建的真正的DOM树上,视图就更新了

an15: 再说一下虚拟Dom以及key属性的作用
Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。

key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度。
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key的作用让每个item有一个唯一的识别身份,可以下标值index或者id, 主要是为了vue精准的追踪到每一个元素,高效的更新虚拟DOM。

an16: keep-alive了解吗
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:
一般结合路由和动态组件一起使用,用于缓存组件;
提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

an17: Vue中组件生命周期调用顺序说一下
加载渲染过程
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
子组件更新过程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
父组件更新过程
父 beforeUpdate -> 父 updated
销毁过程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

an18: Vue2.x组件通信有哪些方式?
父子组件通信
父->子props,子->父 $ on、$ emit
获取父子组件实例 $ parent、 $ children
Ref 获取实例的方式调用组件的属性或者方法
Provide、inject 官方不推荐使用,但是写组件库时很常用
兄弟组件通信
Event Bus 实现跨组件通信 Vue.prototype.$bus = new Vue
Vuex
跨级组件通信
Vuex
$attrs、 $ listeners
Provide、inject

an19: SSR了解吗?
SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把html直接返回给客户端。
SSR有着更好的SEO、并且首屏加载速度更快等优点。不过它也有一些缺点,比如我们的开发条件会受到限制,服务器端渲染只支持beforeCreate和created两个钩子,当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境。还有就是服务器会有更大的负载需求。

an20: 你都做过哪些Vue的性能优化?
编码阶段
尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcherv-if和v-for不能连用如果需要使用v-for给每项元素绑定事件时使用事件代理SPA 页面采用keep-alive缓存组件在更多的情况下,使用v-if替代v-showkey保证唯一使用路由懒加载、异步组件防抖、节流第三方模块按需导入长列表滚动到可视区域动态加载图片懒加载
SEO优化
预渲染服务端渲染SSR
打包优化
压缩代码Tree Shaking/Scope Hoisting使用cdn加载第三方模块多线程打包happypacksplitChunks抽离公共文件sourceMap优化
用户体验
骨架屏PWA
还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

an21: hash路由和history路由实现原理说一下
location.hash的值实际就是URL中#后面的东西。
history实际采用了HTML5中提供的API来实现,主要有history.pushState()和history.replaceState()。

an22:v-for与v-if一起使用的问题
由于v-for的优先级比v-if高,所以导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。
解决办法:在v-for的外层或内层包裹一个元素来使用v-if
an23: 说说你对 SPA 单页面的理解,它的优缺点分别是什么?
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
优点:
用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
基于上面一点,SPA 相对对服务器压力小;
前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
an24: 直接给一个数组项赋值,Vue 能检测到变化吗?
由于 JavaScript 的限制,Vue 不能检测到以下数组的变动:

当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength

为了解决第一个问题,Vue 提供了以下操作方法:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$ set,Vue.set的一个别名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
为了解决第二个问题,Vue 提供了以下操作方法:
// Array.prototype.splice
vm.items.splice(newLength)
an25: 在哪个生命周期内调用异步请求?
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
能更快获取到服务端数据,减少页面 loading 时间;
ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;

an26: 在什么阶段才能访问操作DOM?
在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM。
an27: 父组件可以监听到子组件的生命周期吗?
比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:
// Parent.vue
<Child @mounted=“doSomething”/>

// Child.vue
mounted() {
this.$emit(“mounted”);
}
以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:
// Parent.vue
<Child @hook:mounted=“doSomething” ></ Child>

doSomething() {
console.log(‘父组件监听到 mounted 钩子函数 …’);
},

// Child.vue
mounted(){
console.log(‘子组件触发 mounted 钩子函数 …’);
},

// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 …
// 父组件监听到 mounted 钩子函数 …
复制代码当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。
an28: 组件中 data 为什么是一个函数?

为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?

// data
data() {
return {
message: “子组件”,
childName:this.name
}
}

// new Vue
new Vue({
el: ‘#app’,
router,
template: ‘< App/>’,
components: {App}
})

复制代码因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
an29: v-model 的原理?
我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

text 和 textarea 元素使用 value 属性和 input 事件;
checkbox 和 radio 使用 checked 属性和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件。

以 input 表单元素为例:

相当于

< input v-bind:value=“something” v-on:input=“something = $event.target.value”>
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
父组件:< ModelChild v-model=“message”></ ModelChild>

子组件:
< div>{{value}}</ div>
props:{
value: String
},
methods: {
test1(){
this.$emit(‘input’, ‘小红’)
},
},
an30: Vue 组件间通信有哪几种方式?
Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
(1)props / $emit 适用 父子组件通信
这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
(2)ref 与 $parent / $children 适用 父子组件通信
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
$parent / $children:访问父 / 子实例
(3)EventBus ( $ emit / $on) 适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
(4) $ attrs/ $listeners 适用于 隔代组件通信
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind=" $attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=" $listeners" 传入内部组件
(5)provide / inject 适用于 隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
(6)Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

an31: 你使用过 Vuex 吗?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
(1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
(2)改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
主要包括以下几个模块:

State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

an32: 使用过 Vue SSR 吗?说说 SSR?
Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
即:SSR大致的意思就是vue在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。

服务端渲染 SSR 的优缺点如下:
(1)服务端渲染的优点:
更好的 SEO: 因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
更快的内容到达时间(首屏加载更快): SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
(2) 服务端渲染的缺点:
更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。

如果没有 SSR 开发经验的同学,可以参考本文作者的另一篇 SSR 的实践文章《Vue SSR 踩坑之旅》,里面 SSR 项目搭建以及附有项目源码。
an33: vue-router 路由模式有几种?
vue-router 有 3 种路由模式:hash、history、abstract,对应的源码如下所示:
switch (mode) {
case ‘history’:
this.history = new HTML5History(this, options.base)
break
case ‘hash’:
this.history = new HashHistory(this, options.base, this.fallback)
break
case ‘abstract’:
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== ‘production’) {
assert(false, invalid mode: ${mode})
}
}
其中,3 种路由模式的说明如下:
hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.

an34: 能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?
(1)hash 模式的实现原理
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 ‘#search’:
https://www.word.com#search
复制代码hash 路由模式的实现主要是基于下面几个特性:

URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;
可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。

(2)history 模式的实现原理
HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
复制代码history 路由模式的实现主要基于存在下面几个特性:

pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;
我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。
an35: Vue 是如何实现数据双向绑定的?
Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据,如下图所示:

输入框内容变化时,Data 中的数据同步变化。即 View => Data 的变化。
Data 中的数据变化时,文本节点的内容同步变化。即 Data => View 的变化。

其中,View 变化更新 Data ,可以通过事件监听的方式来实现,所以 Vue 的数据双向绑定的工作主要是如何根据 Data 变化更新 View。
Vue 主要通过以下 4 个步骤来实现数据双向绑定的:
实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
36.说说$set实现原理?
受现代 JavaScript 的限制 (而且 Object.observe 也已经被废弃),Vue 无法检测到对象属性的添加或删除。
由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。
那么 Vue 内部是如何解决对象新增属性不能响应的问题的呢?

1.如果目标是数组,使用 vue 实现的变异方法 splice 实现响应式
2.如果目标是对象,判断属性存在,即为响应式,直接赋值
3.如果 target 本身就不是响应式,直接赋值
4.如果属性不是响应式,则调用 defineReactive 方法进行响应式处理

说一下Vue-Router的实现原理
Vue-Router初始化是发生在什么时候
Vue中key的作用

37.vue渲染过程
调用 compile 函数,生成 render 函数字符串 ,编译过程如下:
parse 函数解析 template,生成 ast(抽象语法树)
optimize 函数优化静态节点 (标记不需要每次都更新的内容,diff 算法会直接跳过静态节点,从而减少比较的过程,优化了 patch 的性能)
generate 函数生成 render 函数字符串
调用 new Watcher 函数,监听数据的变化,当数据发生变化时,Render 函数执行生成 vnode 对象
调用 patch 方法,对比新旧 vnode 对象,通过 DOM diff 算法,添加、修改、删除真正的 DOM 元素

38.骨架屏
骨架屏可以理解为是当数据还未加载进来前,页面的一个空白版本,在页面完全渲染完成之前,用户会看到一个样式简单,描绘了当前页面的大致框架的骨架屏页面,然后骨架屏中各个占位部分被实际资源完全替换,这个过程中用户会觉得内容正在逐渐加载即将呈现,降低了用户的焦躁情绪,使得加载过程主观上变得流畅

39.性能优化
webpack优化
1.外部引入模块(CDN)
2.按需引入
3.tree-shaking
4.开启gzip
其余的优化
1.前面提到的强制缓存与协商缓存
2.减少不必要的http请求 对资源进行合并
3.使用骨架屏提升用户体验
4.前面提到的defer延迟加载
5.懒加载

/deep/ 深度选择器
在vue中,我们为了避免父组件的样式影响到子组件的样式,会在style中加
scoped 之后样式会自动添加一个hash值,既不影响到别的地方,又能修改子组件在当前的样式。
< style scoped> /deep/ .title{ color: #ff0; } </ style>
当然了把 /deep/ 换成 >>>,也可以达到同样的效果。

vue3.0
更快
1、virtual DOM 完全重写,mounting & patching 提速 100%;
  2、更多编译时 (compile-time)提醒以减少 runtime 开销;
  3、基于 Proxy 观察者机制以满足全语言覆盖以及更好的性能;
  4、放弃 Object.defineProperty ,使用更快的原生 Proxy;
  5、组件实例初始化速度提高 100%;
  6、提速一倍/内存使用降低一半;

更小
  1、Tree-shaking 更友好;
  2、新的 core runtime:~ 10kb gzipped;
3.0启动npm run serve
2.0启动npm run dev
3.0 新加入了 TypeScript 以及 PWA 的支持

小程序总结:
1.在小程序中常用有那些缓存形式
wx.setStorage({key: ‘sessionId’, data: phoneNumber,})
wx.getStorageSync(‘sessionId’)
wx.removeStorage({key: ‘sessionId’,})
2.授权实现:使用wx.getSetting// 查看是否授权
返回的数据 res.authSetting[‘scope.userInfo’]为空说明没有授权登录(授权了scope.userInfo返回为true)
授权
//判断小程序的API,回调,参数,组件等是否在当前版本可用。
canIUse: wx.canIUse(‘button.open-type.getUserInfo’),
< button class=‘bottom’ type=‘primary’ open-type=“getUserInfo” lang=“zh_CN” bindgetuserinfo=“bindGetUserInfo”>
授权登录
</ button>
bindGetUserInfo
3.小程序支付流程
4.扫码
5.获取宽高
6.路由有那些
7.上传图片wx.uploadFile
8.wx.previewImage({
current: ‘’, // 当前显示图片的http链接
urls: [] // 需要预览的图片http链接列表
})
9. onShareAppMessage(转发跟分享)
10.小程序登录机制:
写过3种:1.因为微信现在限制不能一进去小程序就强迫用户登录,否则审核不通过,后面想的解决办法是,把首页放在json中的第一位,当点击信息为空的时候,去授权,再到登录页,登录账号的时候,通过wx.login会获取code,通过code获取,然后在后台给的登录接口加上这个code,获取到登录成功的信息里面会返回一个重要的东西就是sessionId,出于安全性可以把sessionid当作token,当每次接口请求的时候,都要加到请求头上。

题外话:学习东西的时候多长点心眼,我认为这是件好事

THE END
< <上一篇
下一篇>>