Web前端面试题宝典【精编版】

过了八月,马上到了金九银十的跳槽季,找工作的时候曾一周面试了十多家公司,从一开始被面试官虐的体无完肤到最后对答如流,过程中经历了多少挑灯夜战的晚上,恶补前端知识,努力的付出终会有回报,最终获得了心仪的offer,所有的一切都是值得的。整理一下面试过程中遇到的高频题目,这些都是精简后的真题,就算不能百分百涵盖,至少也囊括7、8%了,如果面试过程中的你能回答出百分之八十左右的题目,不出大的纰漏offer基本上也就稳收囊中了,剩下的百分之二十是面试官测试你的技术极限在哪里,也会因此来决定你的薪资。

不要觉得面试题目多,难度大,很多知识掌握不了就抱着侥幸心态觉得问不到,我想说的是任何行业都适用二八定律,努力成为你自己行业那百分之二十拔尖的人。总而言之就是发展自己才是硬道理,发展的好会遭人嫉妒,这是正常的,否则你得多普通呀。不仅要在专业方面需要进步,当专业发展遇到天花板时说明需要扩维了。学习不要太功利,往往看似无用的东西反而在某个时候会有大用处。只有感到痛苦我们才能成长,舒适区只有两个: 妈妈的子宫和死去的状态,前一个地方我们回不去,后一个地方我们不想去,只能挣扎着克服一切克服的了抑或克服不了的事儿,坚持坚持就过去了,等回头看时都是p大的事儿,大家一起加油!

H5/C3/ES6/JS

Html5

h5、css3、es6的新属性

h5新特性:

  1. 拖拽释放(Drag and drop) API
  2. 语义化更好的内容标签(header,nav,footer,aside,article,section)
  3. 音频、视频API(audio,video)
  4. 画布(Canvas) API
  5. 地理(Geolocation) API
  6. 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
  7. sessionStorage 的数据在浏览器关闭后自动删除
  8. 表单控件:calendar、date、time、email、url、search
  9. 新的技术:webworker, websocket, Geolocation

移除的元素:

  1. 纯表现的元素:basefont,big,center,font, s,strike,tt,u;
  2. 对可用性产生负面影响的元素:frame,frameset,noframes

H5的特点

  1. 更简洁,但是在实际开发中要注意书写规范
  2. 标签的语义化
  3. 语法更宽松

BFC

BFC(块级格式化上下文),一个创建了新的BFC的盒子是独立布局的,盒子内元素的布局不会影响盒子外面的元素,在同一个BFC中的两个相邻的盒子在垂直方向发生margin重叠的问题。

BFC是指浏览器中创建了一个独立的渲染区域,该区域内所有元素的布局不会影响到区域外元素的布局,这个渲染区域只对块级元素起作用。

触发BFC的方式(以下任意一条就可以)

  1. html根元素
  2. float属性不为none
  3. position不为visible,为absolute或fixed。
  4. display为inline-block, table-cell, table-caption, flex, inline-flex
  5. overflow不为visible

BFC布局与普通文档流布局区别

(注:字体加粗的触发BFC的条件都支持)

普通文档流布局规则

1. 浮动的元素是不会被父级计算高度

2. 非浮动元素会覆盖浮动元素的位置(兄弟关系的元素)。

3. 给子级添加margin-top会传递给父级

4.两个相邻元素上下margin会重叠

BFC布局规则

1.浮动的元素会被父级计算高度(高度坍塌)

(1)给父级添加浮动float

(2)给父级添加overflow:hidden; 触发了BFC(子元素浮动,父元素没有高度时使用,该方法不能和定位一起使用)

(3)给父级添加display:inline-block;

(4)给父级添加定位position:absolute/fixed;

2. 非浮动元素不会覆盖浮动元素位置

(1)非浮动元素float的值不为none

(2)非浮动元素加overflow:hidden触发了BFC。

(3)非浮动元素display:inline-block;

(4)给非浮动元素或者浮动元素添加position定位,添加的元素会覆盖在未添加的元素上面

3. 给子级添加margin-top不会传递给父级

(1)给父级添加浮动float

(2)给父级添加overflow:hidden;

(3)给子级添加display:inline-block;

(4)给父级添加定位position:absolute/fixed;

Form中的获取数据的两个方式get和post的区别?

  1. get是从服务器上获取数据,post是向服务器传送数据。
  2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。get是显式提交,post是隐式提交。
  3. 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。
  4. get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。
  5. get安全性非常低,post安全性较高。但是执行效率却比Post方法好。

什么时候使用get什么时候使用post?

  1. get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;
  2. 在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式;

Html中form里的action方法的get与post方法区别是什么?

get:

  1. 通过GET提交数据,用户名和密码将明文出现在URL上,因为登录页面有可能被浏览器缓存,GET请求请提交的数据放置在HTTP请求协议头
  2. 或者其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-site request forgery攻击,所以不安全
  3. GET请求有长度限制

post:

  1. POST数据放在body(POST提交的数据则放在实体数据),POST请求数据不能被缓存下来
  2. POST请求参数不会被保存在浏览器历史或 web 服务器日志中。
  3. POST请求没有长度限制

参考:form里面的action和method(post和get的方法)使用

https://www.cnblogs.com/May-d...

渐进增强和优雅降级的概念以及区别

渐进增强 progressive enhancement:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。

优雅降级 graceful degradation:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

区别:优雅降级是从复杂的现状开始,并试图减少用户体验的供给,而渐进增强则是从一个非常基础的,能够起作用的版本开始,并不断扩充,以适应未来环境的需要。降级(功能衰减)意味着往回看;而渐进增强则意味着朝前看,同时保证其根基处于安全地带。

如何处理HTML5新标签的浏览器兼容问题?

支持HTML5新标签:

IE8/IE7/IE6支持通过 document.createElement 方法产生的标签,可以利用这一特性让这些浏览器支持 HTML5 新标签,浏览器支持新标签后,还需要添加标签默认的样式(当然最好的方式是直接使用成熟的框架、使用最多的是html5shim框架):

如何区分HTML和HTML5

DOCTYPE声明新增的结构元素、功能元素

文档碎片

如果我们要在一个ul中添加100li,如果不使用文档碎片,那么我们就需要使用append经常100次的追加,这会导致浏览器一直不停的渲染,是非常消耗资源的。但是如果我们使用文档碎片了,我们可以先将100li添加到文档碎片中,然后直接把这个文档碎片追加到ul中即可。所以文档碎片其实就是一个临时的仓库。

如下代码在document.body中添加5span

for(var i=0;i<5;i++)
{ 
    var op = document.createElement("span"); 
    var oText = document.createTextNode(i); 
    op.appendChild(oText); 
    document.body.appendChild(op); 
} 

对于少量的更新,一条条循环插入的运行也还可以。但是,当要向document中添加大量数据(比如1w条),如果像上面的代码一样,逐条添加节点,整个过程会十分缓慢,性能极差。
也可以建一个新的节点,例如div,先将span添加到div上,然后再将div添加到body:

var oDiv = document.createElement("div"); 
for(var i=0;i<10000;i++)
{ 
    var op = document.createElement("span"); 
    var oText = document.createTextNode(i); 
    op.appendChild(oText); 
    oDiv.appendChild(op);  
} 
document.body.appendChild(oDiv); 

但这样会在body中多添加一个div节点。用文档碎片就不会产生这种节点,引入createDocumentFragement()方法,它的作用是创建一个文档碎片,把要插入的新节点先插入它里面,然后再一次性地添加到document中。
代码如下:

//先创建文档碎片
var oFragmeng = document.createDocumentFragment(); 
for(var i=0;i<10000;i++)
{ 
    var op = document.createElement("span"); 
    var oText = document.createTextNode(i); 
    op.appendChild(oText); 
    //先附加在文档碎片中
    oFragmeng.appendChild(op);  
} 
//最后一次性添加到document中
document.body.appendChild(oFragmeng); 

浏览器内核

IE: trident内核

Firefox:gecko内核

Safari:webkit内核

Opera:以前是presto内核,Opera现已改用Google Chrome的Blink内核

Chrome:Blink(基于webkit,Google与Opera Software共同开发)

什么是viewport?

viewport是视口,在浏览器中用来显示网页的某一块区域,以合理的比例显示内容。

CSS3

css3新特性

  1. CSS3实现圆角(border-radius),盒子阴影(box-shadow),文本阴影(text-shadow)
  2. 渐变(gradient):线性渐变、径向渐变,旋转(transform)
  3. 有关制作动画的三个属性:transform、transition、animation
  4. transform:translate移动、scale缩放、rotate旋转、skew倾斜
  5. animation动画 keyframes关键帧
  6. 增加了更多的CSS选择器 多背景 rgba
  7. 在CSS3中唯一引入的伪元素是 ::selection.
  8. 媒体查询,多栏布局
  9. border-color、border-image、border-radius
  10. filter:blur()虚化图片

css3的优势

  1. 让页面效果看起来非常炫酷,用户体验更高。
  2. 有利于开发和维护,还能提高网站的性能,增加网站的可访问性,可用性。
  3. 使网站能适配更多的设备,利于seo网站优化,提高网站的搜索排名。

css sprites如何使用

将导航背景图片,按钮背景图片等有规则的合并成一张背景图,即将多张图片合为一张整图,然后用“background-position”来实现背景图片的定位技术。

CSS元素类型

块状元素,内联元素(行内元素),内联块元素(行内块元素)

空元素有哪一些

br hr

标准盒模型和怪异盒模型

CSS中Box model是分为两种:W3C标准和IE标准盒子模型

在标准模式下,一个块的总宽度 = width + margin+padding+border

在怪异模式下,一个块的总宽度=width+margin(即width已经包含了padding和border的值)

大多数浏览器采用W3C标准模型,而IE中则采用Micreosoft自己的标准。

怪异模式是"部分浏览器在支持W3C标准的同时还保留了原来的解析模式",怪异模式主要表现在IE内核的浏览器。

当不对doctype进行定义时,会触发怪异模式。

标准盒模型和怪异盒模型的转化

怪异盒模型:box-sizing:border-box

标准盒模型:box-sizing:content-box

列出display的值,说明他们的作用

display属性与属性值 (18个属性值)

属性值:block/inline/inline-block/none/list-item/table-header-group/table-footer-group/table

作用:块状元素和内联元素之间的转换。

  1. Block块状显示:类似在元素后面添加换行符,也就是说其他元素不能在其后面并列显示。或者就是让元素竖排显示。
  2. inline内联显示:在元素后面删除换行符,多个元素可以在一行内并列显示。或者就是让元素横排显示。
  3. 当元素设置了float属性后,就相当于该元素具备块状元素显示的特点;可以加宽高。
  4. Inline-block行内块元素显示:元素的内容以块状显示,行内的其他元素显示在同一行。(只有行内块这一个元素类型支持vertical-align属性)

    img,input垂直对齐方式{vertical-align:top/bottom/middle;}

  5. none 此元素不会被显示。隐藏且不占位

position的值:relative和absolute分别是相对谁进行定位的

position:定位属性,检索对象的定位方式;

定位都脱离层。

语法:position:static /absolute/relative/fixed

absoluted/relative/fixed 对定位有效,对层级属性Z-index有效。

取值:

  1. static:默认值,无特殊定位,对象遵循HTML原则;
  2. absolute:绝对定位,将对象从文档流中拖离出来,使用left/right/top/bottom等属性相对其最接近的一个并有定位设置的父元素(祖先元素|祖先元素不包含叔叔级的)进行绝对定位;如果不存在这样的父对象,则依据根元素(html)浏览器进行定位,而其层叠通过z-index属性定义,可以脱离当前的大容器,并且不占位。float脱离半层。
  3. relative :相对定位,该对象的文档流位置不动,将依据right,top,left,bottom(相对定位)等属性在正常文档流中偏移位置;其层叠通过z-index属性定义

一般给父层级添加relative属性值

相对于自身位置进行偏移

元素设置:margin:0 auto时:

(1)box为相对定位时(相对自身),居中

(2)box为绝对定位时(相对于根元素HTML),向左

  1. fixed:(固定定位)未支持,对象定位遵从绝对定位方式(absolute);但是要遵守一些规范(IE6浏览器不支持此定位);跟绝对定位一样的都是脱离文档流,不占位,并且永远相对于当前浏览器的可视窗口进行位置偏移。

absolute是不是一定需要设置相对定位

absolute不一定必须找到position单词修饰的祖先元素,如果找不到就相对浏览器进行定位。

解释下浮动和它的工作原理

语法:float:none/left/right;

float浮动脱离半层

float:定义网页中其它文本如何环绕该元素显示

嵌套浮动元素的父级要有高度,不然会高度塌陷,解决办法,添加overflow:hidden;(注意不能与定位一起使用)

浮动的目的:就是让竖着的东西横着来

有三个取值:

left:元素活动浮动在文本左面

right:元素浮动在右面

none:默认值,不浮动。

清除浮动的方法

  1. 在结尾处添加空div标签clear:both;
  2. 万能清除浮动法(给浮动元素的父元素添加)class名.clear,哪个需要清除就给哪个元素命名为clear ;一个元素可以取多个class名,中间用空格隔开)。
:after{content:".";clear:both;display:block;height:0;overflow:hidden;visibility:hidden;}
.clear{zoom:1}
  1. 父级div定义height
  2. 父级div定义overflow:hidden
  3. 父级div定义overflow:auto
  4. 父级div也一起浮动
  5. 父级div定义display:table
  6. 结尾处加br标签clear:both

参考:几种常用的清除浮动方法

https://www.cnblogs.com/nxl09...

为何要清除浮动

浮动框不属于文档流中的普通流,当元素浮动之后,不会影响块级元素的布局,只会影响内联元素布局。此时文档流中的普通流就会表现得该浮动框不存在一样的布局模式。当包含框的高度小于浮动框的时候,此时就会出现“高度塌陷”

CSS3动画的实现种类

CSS3属性中有关于制作动画的三个属性:

Transition(过渡),Transform(转换),Animation(动画)

animation和transition的区别

相同点:都是随着时间改变元素的属性值。

不同点:transition需要触发一个事件(hover事件target事件或click事件等)才会随时间改变其css属性; 而animation在不需要触发任何事件的情况下也可以显式的随着时间变化来改变元素css的属性值,从而达到一种动画的效果,css3的animation就需要明确的动画属性值。

transition触发后只能运动一次,animation可以设定运动的循环次数。

Animation-->在这个动画之前,先看Keyframes关键帧,支持animation动画的只有webkit内核的浏览器

如何让chrome支持小于12px的文字?(css3中的缩放形式)


haorooms博客测试10px

iframe的优缺点

优点:

  1. 解决加载缓慢的第三方内容如图标和广告等的加载问题
  2. Security sandbox
  3. 并行加载脚本

缺点:

  1. iframe会阻塞主页面的Onload事件
  2. 即时内容为空,加载也需要时间
  3. 没有语意

回流和重绘

  1. 当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。
  2. 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

注:回流必将引起重绘,而重绘不一定会引起回流

回流就相当于整个页面发生了DOM上的变化 重绘就是样式发生了改变

区别:

他们的区别很大:
回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流
当页面布局和几何属性改变时就需要回流
比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变

CSS选择符

类型选择符,id选择符,class选择符,通配符,群组选择符,包含选择符,伪类选择符(伪类选择符CSS中已经定义好的选择器,不能随便取名),伪对象选择符(设置在对象后发生的内容,用来和content属性一起使用 )

CSS选择符的权重

css中用四位数字表示权重,权重的表达方式如:0,0,0,0

类型选择符的权重为0001

class选择符的权重为0010

id选择符的权重为0100

子选择符的权重为0000

属性选择符的权重为0010

伪类选择符的权重为0010

伪元素选择符的权重为0001

包含选择符的权重:为包含选择符的权重之和

群组选择符的权重:为各自的权重

内联样式的权重为1000

继承样式的权重为0000

*通配符选择器权重为0

垂直水平居中

CSS2有定位

让一个元素实现垂直水平居中:

(1)给当前元素后面添加空标签,代码不换行,必须写在一行上。

(2)给当前元素和span添加display:inline-block;转换为内联块元素。

(3)给span添加height属性,与父级同高;

(4)给当前元素和span添加vertical-align:middle;实现垂直居中。

(5)给父元素添加text-align:center;实现水平居中。

让一个图片实现垂直水平居中:

(1)给当前元素后面添加空标签,代码不换行,必须写在一行上。

(2)给span添加display:inline-block;转换为内联块元素。(图片本身就是内联块元素,不需要再转换)

(3)给span添加height属性,与父级同高;

(4)给当前元素和span添加vertical-align:middle;实现垂直居中。

(5)给父元素添加text-align:center;实现水平居中。

水平居中:text-align:center:(1)对文本有效;(2)对内联元素、内联块元素有效。

当前元素为有宽度的块元素时,可以用margin:0 auto;实现水平居中。

垂直居中:vertical-align:middle;只对内联块元素有效。

用定位让元素或图片实现垂直水平居中:

(1)给父元素定位:position:relative;

(2)给子元素加绝对定位:position:absolute;

(3)给子元素添加坐标属性:top:0;left:0;right:0;bottom:0;margin:auto;

相当于给元素提供了四个参照范围

让一个元素在当前浏览器窗口垂直水平居中:

第一种方法:(1)给元素添加固定定位:position:fixed;

(2)给元素添加坐标属性:top:0;left:0;right:0;bottom:0;margin:auto;

第二种方法:(1)给元素添加固定定位position:fixed;

(2)top:50%;left:50%;

(3)margin:-元素高度/2px 0 0 -元素宽度/2px;

中心点的移动:从左上移到中心点

c3有弹性盒布局

给父元素添加:

display:flex;

justify-content: center;

align-items: center;

利用位移(定位加位移)

父元素:

positive:relative;

子元素:

position: absolute;

top:50%;

left:50%;

transform: translate(-元素宽度/2px,-元素高度/2px);

两种引入外部样式表link和import之间的区别

差别1:本质的差别:link属于XHTML标签,而@import完全是CSS提供的一种方式。

差别2:加载顺序的差别:当一个页面被加载的时候(就是被浏览者浏览的时候),link引用的CSS会同时被加载,而@import引用的CSS会等到页面全部被下载完再被加载。所以有时候浏览@import加载CSS的页面时开始会没有样式(就是闪烁),网速慢的时候还挺明显。

差别3:兼容性的差别:@import是CSS2.1提出的,所以老的浏览器不支持,@import只有在IE5以上的才能识别,而link标签无此问题。

差别4:使用dom(document object model文档对象模型 )控制样式时的差别:当使用javascript控制dom去改变样式的时候,只能使用link标签,因为@import不是dom可以控制的。

简述src与href的区别

src用于替换当前元素,href用于在当前文档和引用资源之间确立联系。

src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。

当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。这也是为什么将js脚本放在底部而不是头部。

href是Hypertext Reference的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,如果我们在文档中添加

那么浏览器会识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用link方式来加载css,而不是使用@import方式。

伪类选择器

1、结构性伪类选择器

①:first-child选择某个元素的第一个子元素;IE6不支持:first-child选择器

②:last-child选择某个元素的最后一个子元素;

③:nth-child()选择某个元素的一个或多个特定的子元素;

④:nth-last-child()选择某个元素的一个或多个特定的元素,从这个元素的最后一个元素开始算;

⑤:nth-of-type()选择指定类型的元素;

nth-of-type类似于:nth-child,不同的是他只计算选择指定的元素类型。

⑥:nth-last-of-type()选择指定的元素,从元素的最后一个开始计算;

⑦:first-of-type选择一个上级元素下的第一个同类元素;

⑧:last-of-type选择一个上级元素的最后一个同类元素;

⑨:only-child选择的元素是它的父元素的唯一 一个子元素;

⑩:only-of-type选择一个元素是它的上级元素的唯一一个相同类型的子元素;

伪元素选择器

::first-line ::first-letter ::before ::after

CSS3新增:::selection 用来改变浏览网页选中文本的默认效果

CSS3中主要用两个"::"和一个":"来区分伪类和伪元素

用CSS写一个三角形

aside{

width:1px;

height:1px;

border:50px solid #fff;

border-bottom-color:red;

}

设置宽高为0或者1像素,需要哪个方向的三角形,就给哪个方向的边框单独加颜色。

font属性的简写

说明:font的属性值应按以下次序书写(各个属性之间用空格隔开)

顺序: font-style font-weight font-size / line-height font-family

css实现文字溢出显示省略号

  1. 容器宽度:width:value;
  2. 强制文本在一行内显示:white-space:nowrap;
  3. 溢出内容为隐藏:overflow:hidden;
  4. 溢出文本显示省略号:text-overflow:ellipsis;

圣杯布局

left:左浮动;

right:右浮动;

center:overflow:hidden;

顺序:左右中,左浮右浮。

自适应两栏布局

html,body{height:100%;}高度写百分比时,需要声明body和html的高度为100%。

布局:一左一右,左半部分浮动,非浮动元素会覆盖浮动元素的位置。

给右半部分添加overflow:hidden;

自适应,非浮动元素不加宽度。

只适用于pc端,比较简单的布局。

CSS3兼容

各主流浏览器都定义了私有属性,以便让用户体验 CSS3 的新特性。例如,

  • Webkit 类型浏览器(如 Safari、Chrome)的私有属性是以-webkit-前缀开始,
  • Gecko 类型的浏览器(如 Firefox)的私有属性是以-moz-前缀开始,
  • Konqueror 类型的浏览器的私有属性是以-khtml-前缀开始,
  • Opera 浏览器的私有属性是以-o-前缀开始,
  • 而 Internet Explorer 浏览器的私有属性是以-ms-前缀开始(目前只有 IE 8+ 支持-ms-前缀)。

什么是Css Hack?ie6,7,8的hack分别是什么?

针对不同的浏览器写不同的CSS code的过程,就是CSS hack。

#test       {   
        width:300px;   
        height:300px;   
        background-color:blue;      /*firefox*/
        background-color:red\9;      /*all ie*/
        background-color:yellow;    /*ie8*/
        +background-color:pink;        /*ie7*/
        _background-color:orange;       /*ie6*/    }  
        :root #test { background-color:purple\9; }  /*ie9*/
    @media all and (min-width:0px){ #test {background-color:black;} }  /*opera*/
    @media screen and (-webkit-min-device-pixel-ratio:0){ #test {background-color:gray;} }       /*chrome and safari*/

ES6

ES6的新特性

(1)let关键字:let关键字声明变量,只要遇到大括号,就形成作用域。

(2)const:const声明的变量,一旦被声明,就没有办法被修改。

(3)箭头函数

(4)变量的解构赋值

(5)字符串模板:使用反引号`表示,使用${变量|函数}嵌入代码

(6)数组的一些方法:

  • Array.from():将伪数组转成真数组。(极少数的地方会用)
  • copyWithin()了解:复制指定内容覆盖指定内容(指定数组的下标6、7替换下标2及以后)
  • find() 功能:查找符合条件的第一个元素。
  • findIndex(); 功能:查找符合条件的第一个元素的下标。

(7)Object.assign 合并对象

(8)第七种数据类型Symbol

(9)Set和Map集合

(10)Promise对象

(11)class定义类

箭头函数中this的指向

箭头函数:箭头函数本身是没有this和arguments的,在箭头函数中引用this实际上是调用的是定义时的上一层作用域的this。 这里强调的是上一层作用域,是因为对象是不能形成独立的作用域的。

例如:

    var obj = {
        say: function() {
            var f1 = ()=>{
                console.log("1111",this);    
            }
            f1();
        }
    }
    var o = obj.say;

o();//f1执行时,say函数指向window,所以f1中的this指向window
obj.say();//f1执行时,say函数指向obj,所以f1中的this指向obj;

    var ojb = {
        pro: {
            getPro: ()=>{
                console.log(this);
            }
        }
    }

obj.pro.getPro();//this指向的是window,因为箭头函数定义时,getPro的上一级是pro,是一个对象,不能形成单独的作用域,故指向window。

参考:箭头函数中的this指向

https://blog.csdn.net/sinat_3...

ES6新增了那些关于数组的方法

  1. Array.from 这个东西就是把一些集合,或者长的像数组的伪数组转换成真的数组,比如arguments,js选择器找到dom集合,还有对象模拟的数组。

    var obj = {'0' : 1,length : 1}
    Array.from(obj / arguments / 伪数组) //返回的是一个数组

    Array.from还有第二个参数,是一个回调函数,功能类似map Array.from( [1, 2, 3], item => item * 2 )

  2. Array.of

    Array.of(1, 2, 3, 4) //把参数合并成一个数组返回,如果参数为空,则返回一个空数组
  3. find/findIndex

    //find 返回数组中第一个符合条件的元素, findIndex返回索引
    
     [1, 2, 3, 4, 5].find(function(item){ return item > 3 }
  4. fill

    数组中的每一个元素替换成指定值
    
    let arr = [1, 2, 3, 4] arr.fill(5) //arr全变成了5
    
    指定范围替换
    
    arr.fill(6, 1, 3)  //arr=[1,6,6,4]
  5. entries/keys/values

    let arr=['a', 'b', 'c'] for(let key of arr.keys()){} //取键 
    
    for(let value of arr.values()){} //取值
    
    for(let [key, value] of arr.entries()){} //都取
  6. includes

    var a = function(){} 
    
    [1, 2, 3, 4, a].includes(a) //true

var和let的区别

let关键字声明变量,只要遇到大括号,就形成作用域。

我们把let关键字,形成的作用域,叫做块级作用域。

let命令的特性

(1)let不存在变量提升

var命令会发生”变量提升“现象,即变量可以在声明之前使用,值为undefined。

let命令不存在变量提升的行为,它所声明的变量一定要在声明后使用,否则报错。

(2)暂时性死区

只要块级作用域内存在let命令声明变量之前,该变量都是不可用的。

这在语法上,称为“暂时性死区” temporal dead zone,简称 TDZ。

(3)不允许重复声明

let不允许在相同作用域内,重复声明同一个变量。

const命令

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const声明的常量可以更改值吗

const a = [1, 2, 3];
        a.push(4); 
        console.log(a)// 输出[1,2,3,4]

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。

参考:let 和 const 命令

http://es6.ruanyifeng.com/#do...

let const var 的区别?

1、let是es6新增的声明变量的方式 ,其特点是:
(1)作用域是块级作用域(在ES6之前,js只存在函数作用域以及全局作用域)

 if(1){
  let a=1;
  console.log(a)
}

(2)不存在变量声明提前;

 console.log(b); //ReferenceError: b is not defined

 let b=2;

(3) 不能重复定义

 let a=1;
 let a=2;

console.log(a);//Identifier 'a' has already been declared

(4)存在暂时性死区:可以这样来理解

var a=1;
if(1){
 console.log(a); 
  let a=2;
}

① 在一个块级作用域中,变量唯一存在,一旦在块级作用域中用let声明了一个变量,那么这个变量就唯一属于这个块级作用域,不受外部变量的影响;

② 无论在块中的任何地方声明了一个变量,那么在这个块级作用域中,任何使用这个名字的变量都是指这个变量,无论外部是否有其他同名的全局变量;

③ 暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

④ 暂时性死区的意义:让我们标准化代码。将所有的变量的声明放在作用域的最开始。

2、 const一般用来声明常量,且声明的常量是不允许改变的,只读属性,因此就要在声明的同时赋值。const与let一样,都是块级作用域,存在暂时性死区,不存在变量声明提前,不允许重复定义

const A=1;(重新给常量A赋值会报错 )A=3;// Uncaught TypeError: Assignment to constant variable.(错误:赋值给常量)

3、 var 是es6之前 js用来声明变量的方法,其特性是:
(1)var的作用域是函数作用域,在一个函数内利用var声明一个变量,则这个变量只在这个函数内有效

function test(){
 var a=1;

console.log(a);//函数未调用 输出的是undefined  函数调用输出的是1 

}

console.log(a);//ReferenceError:a is not defined

(2)存在变量声明提前(虽然变量声明提前,但变量的赋值并没有提前,因此下面的程序不会报错,但a的值为undefined)

function test(){
 console.log(a);//undefined
 var a=3/a=3(隐式声明)

}

参考:let,const以及var三者的区别,面试经常会问哦!

https://blog.csdn.net/lydia11...

第七种数据类型Symbol

https://www.jianshu.com/p/f40...

Symbol函数会生成一个唯一的值

可以理解为Symbol类型跟字符串是接近的

但每次生成唯一的值,也就是每次都不相等,

至于它等于多少,并不重要,这对于一些字典变量,比较有用

每个Symbol实例都是唯一的。因此,当你比较两个Symbol实例的时候,将总会返回false

应用场景:可以使用Symbol来作为对象属性名(key)

在这之前,我们通常定义或访问对象的属性时都是使用字符串,而现在,Symbol可同样用于对对象属性的定义和访问,当使用了Symbol作为对象的属性key后,Symbol类型的key是不能通过Object.keys()或者for…in来枚举的,它被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。也正因为这样一个特性,当使用JSON.stringfy()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外,我们可以利用这一特点来更好的设计我们的数据对象,让"对内操作"和"对外选择性输出"变得更加优雅。

数组的解构赋值

请实现该缺少的内部代码,交换两个数的值

ES5:交换

        var res = (function(x,y){
            let temp = x;
            x= y;
            y = temp;
            return {
                x:x,
                y:y
            }
        })(1,2)
        console.log(res.x)
        console.log(res.y)

ES6:数组解构赋值

        var res = (function (x, y) {
                [y,x] = [x,y]
                return {
                    x: x,
                    y: y
                }
            })(1, 2)
        console.log(res.x)
        console.log(res.y)

参考:数组的扩展

http://es6.ruanyifeng.com/?se...

ES6中构造函数内super关键字的使用

super关键字用于访问和调用一个对象的父对象上的函数,是用来继承属性的 ES6 初始化对象的属性,在构造函数中使用时,super关键字将单独出现,并且必须在使用this关键字之前使用。

JavaScript

TypeScript

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。

const hello : string = "Hello World!"
console.log(hello)

让js语言更加的严谨

JS里面新建函数的方法?

  1. 通过new
 var func = new Function("参数","函数体")

 var func = new Function(“num1”,“num2”,“return num1 + num2”)

 var result =  func(1,1)

console.log(result)//2
  1. 通过函数表达式
var sum = function(num1,num2){

         return num1 + num2

}

var result = sum(10,20)

console.log(result)
  1. 通过函数声明
function sum(num1,num2){

 //上面的逻辑

} 

DOM流

什么是事件流

事件流:描述的就是从页面中接受事件的顺序。

分有事件冒泡与事件捕获两种。

DOM事件流的三个阶段

  1. 事件捕获阶段
  2. 处于目标阶段
  3. 事件冒泡阶段

参考:DOM事件流

https://www.cnblogs.com/cmyou...

阻止事件冒泡和默认行为的方法

event.preventDefault() 阻止默认行为

event.stopPropagation() 阻止冒泡

非W3C标准:

e.cancelBubble=true 阻止事件冒泡

return false 阻止默认行为

return false 代表即阻止事件冒泡,又阻止了默认行为

事件委托的实现流程

  1. 找到要添加事件的节点:父节点、或者祖父节点
  2. 把事件添加在找到的父节点上
  3. 通过target触发对象,找到复合条件的元素节点,进行后续操作

IE:window.event.srcElement
标准:event.targe

window.onload = function(){ 
    var oUl = document.getElementById('ull');
     var aLi = document.getElementsByTagName('li');

  oUl.onmouseover = function(ev){ 
     var event = ev||window.event;  // 获取event对象
     var target = ev.target || ev.srcElement; // 获取触发事件的目标对象
    
     if(target.nodeName.toLowerCase() == 'li'){  //判断目标对象是不是li
         target.style.background = 'red';
     }

  }
代码中加了一个标签名的判断,主要原因是如果不加判断,当你鼠标移入到父级oUL上面的时候,整个列表就会变红,这不是我们想要的结果,所以要判断一下。

target.nodeName 弹出的名字是大写的,所以需要转换大小写再比较。

事件委托的实现是利用事件冒泡的机制

事件冒泡:由里向外逐渐触发

事件捕获:由外向里逐渐触发

事件监听器

  1. 能够给一个事件,添加多个函数
  2. 精确地删除某一个事件某一个函数(通过函数名)
  3. 会对称重复添加的函数,自动去重

*事件监听器是用来解决传统事件绑定的问题,一般情况下(99%)用传统的事件绑定

数据类型

Javascript的基本数据类型

Number:数字(整数,浮点数float)

Array:数组

Object:对象

布尔类型:Boolean a==b

特殊类型:Null、Undefined、NaN

console.log(1+”2"+"2") //122
console.log(“1"-'2'+'2') //-12
console.log(‘A'+"B"+"2") //ab2
console.log(“A”-"B"+2)//NaN

JavaScript创建对象的几种方式

工厂方式,构造函数方式,原型模式,混合构造函数原型模式,动态原型方式

null和undefined的区别

null == undefined //true

null !==undefined

undefined:

(1)变量被声明了,但没有赋值时,就等于undefined。

(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

(3)对象没有赋值的属性,该属性的值为undefined。

(4)函数没有返回值时,默认返回undefined。

null是空值,解除一段关系,把对象赋值成一个空指针,避免内存泄漏,不用的DOM对象一般指向为空,例如:arr = null,不占用内存。

数据类型不同:

typeof (undefined):undefined

typeof(null):object

补充:0和null的区别

0是一个数值,是自然数,有大小

0不等于null

call和apply和bind的区别

三者都是强制改变this的指向

在说区别之前还是先总结一下三者的相似之处:
1、都是用来改变函数的this对象的指向的。
2、第一个参数都是this要指向的对象。
3、都可以利用后续参数传参。

传参数的区别:

参数形式不同,call(obj, pra, pra)后面是单个参数。apply(obj, [args])后面是数组。

Object.call(this,obj1,obj2,obj3) call需要一个一个传参

Object.apply(this,arguments) apply可以传递数组

call与apply都属于Function.prototype的一个方法,所以每个function实例都会有call/apply属性。fn.call,fn是函数,函数下面有一个call方法,fn调用。两者传递参数不同,call函数第一个参数都是要传入当前对象的对象,apply不是,apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入。Call传入则是直接的参数列表,Call方法可以将一个函数的对象上下文从初始的上下文改成由thisObj指定的新对象。

call和apply立即修改this,Bind绑定的时候不会立马去执行,当函数调用的时候bind才会执行。

参考:javascript中apply、call和bind的区别

https://www.cnblogs.com/cosir...

break和continue的区别

break 终止当前循环的

continue 跳过这次循环,直接进入下一次循环。

声明提升

程序运行之前,有一个预编译。

预编译,会将代码从头看到尾,将需要的资源(内存)准备好。

然后才开始运动。

代码运行结果

var a;
alert(typeof a);
alert(b); //报错
var c = null; 
alert(typeof c) 

运行结果:Undefined/报错:b is not defined

var a;
alert(typeof a);
alert(typeof b); //undefined 
var c = null; 
alert(typeof c) 

运行结果:Undefined/undefined/object

typeof:即使你没有定义这个变量也不会报错 只会弹出undefined

alert(typeof typeof undefined) // string

运行结果:string

var bb = 1;
        function aa(bb) {
            bb = 2;
            alert(bb);
        }
        aa(bb); //2
        alert(bb);//1 没有更改全局变量

运行结果:2 1

var bb = 1;
        function aa() {
            bb = 2;
            alert(bb);
        }
        aa(bb); //2
        alert(bb);//1 没有更改全局变量

运行结果:2 2

如果外部不传入bb,相当于你又声明了一个bb,所以结果是2 2

function Foo() {
            var i = 0;
            return function () {
                console.log(i++);
            }
        }
        var f1 = Foo();
        f2 = Foo();
        f1();//0 f1执行 打印i的值 i++是后+
        f1();//1 f1维护自己的i
        f2();//0 f2是一个新的函数 拿到的是新的i变量

运行结果:0 1 0

已知前端页面中,有如下table。请添加用户交互效果,当用户点击该表格的某一行的时候,该行背景色设置为红色的代码。


        ...
        

用jq来实现:让当前点击的对象改变背景颜色

        var trDoms = document.getElementsByTagName("tr")
        for (var i = 0; i < trDoms.length; i++) {
            trDoms[i].onclick = function () {
                this.style.background = "red"
                console.log(i)
            }
        }

运行结果:3 3 3

想实现打印结果为:0 1 2,应如何操作?

第一种方法:获取对应索引

        var trDoms = document.getElementsByTagName("tr")
        for (var i = 0; i < trDoms.length; i++) {
            trDoms[i].index = i;
            trDoms[i].onclick = function () {
                this.style.background = "red"
                console.log(this.index)
            }
        }

第二种方法:闭包

        var trDoms = document.getElementsByTagName("tr")
        for (var i = 0; i < trDoms.length; i++) {
            trDoms[i].onclick = function (i) {
                return function(){
                    console.log(i)
                }
            }(i)
        }

第三种方法:var改成let作用域

                var trDoms = document.getElementsByTagName("tr")
        for (let i = 0; i < trDoms.length; i++) {
            trDoms[i].onclick = function () {
                console.log(i)
            }
        }

请问以下结果会输出什么

        for (var i = 0; i < 5; i++) {
            console.log(i);
        }// 0 1 2 3 4

        for (var i = 0; i < 5; i++) {
            setTimeout(function () {
                console.log(i);
            }, 1000 * i);
        } // 异步打印5个5

        for (var i = 0; i < 5; i++) {
            (function (i) {
                setTimeout(function () {
                    console.log(i);
                }, 1000 * i);
            })(i);
        } // 异步打印 0 1 2 3 4 (闭包写法)

        for (var i = 0; i < 5; i++) {
            (function () {
                setTimeout(function () {
                    console.log(i);
                }, 1000 * i);
            })(i);
        } // 打印5个5(function不传i,i就是全局的i)

        for (var i = 0; i < 5; i++) {
            setTimeout((function () {
                console.log(i);
            })(i), 1000 * i);
        } // 0 1 2 3 4
        var myObject = {
            foo: "bar",
            func: function () {
                var self = this;
                console.log("outer func:",this.foo);//bar
                console.log("outer func:",self.foo);//bar
                (function () {
                    console.log("inner func",this.foo); //undefined 即时函数中this指向window
                    console.log("inner func",self.foo);    //bar        
                })();
            }
        }
        myObject.func();
        (function () {
            var a = b = 1; //声明局部变量 外部访问不到局部变量
        })();
        console.log(typeof a); //undefined 外部访问不到内部声明的局部变量
        console.log(typeof b); //number
function DateDemo() {
            for (var i = 0, j = 0; i < 10, j < 6; i++ , j++) {
                k = i + j; 
            }
            return k;
        }
        var t = DateDemo()
        console.log(t)//10:5+5

设计模式

设计模式的分类

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其实还有两类:并发型模式和线程池模式。

闭包

闭包是什么

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。

闭包的特性

  1. 函数内再嵌套函数
  2. 内部函数可以引用外层的参数和变量
  3. 参数和变量不会被垃圾回收机制回收

闭包的作用

闭包用的多的两个作用:读取函数内部的变量值;让这些变量值始终保存着(在内存中)。

同时需要注意的是:闭包慎用,不滥用,不乱用,由于函数内部的变量都被保存在内存中,会导致内存消耗大。

闭包的优点

  1. 既不声明全局变量
  2. 又能进行累加
  3. 希望一个变量常驻在内存当中,避免全局变量污染
  4. 可以声明私有成员

闭包的缺点

滥用闭包函数会造成内存泄露,因为闭包中引用到的包裹函数中定义的变量都永远不会被释放,所以我们应该在必要的时候,及时释放这个闭包函数。

用一个案例解释闭包的作用

执行say667()后,say667()闭包内部变量会存在,而闭包内部函数的内部变量不会存在。使得Javascript的垃圾回收机制GC不会收回say667()所占用的资源,因为say667()的内部函数的执行需要依赖say667()中的变量。这是对闭包作用的非常直白的描述。

  function say667() {
    // Local variable that ends up within closure
     var num = 666;
     var sayAlert = function() { alert(num);
    }
     num++;
     return sayAlert;
  }
 var sayAlert = say667();
 sayAlert() //执行结果应该弹出的667  

参考:

什么是闭包(closure),为什么要用它?

https://www.cnblogs.com/lqzwe...

什么是闭包?写一个简单的闭包?

答:我的理解是,闭包就是能够读取其他函数内部变量的函数。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁.

闭包的主要应用场景

  1. 匿名自执行函数
  2. 缓存
  3. 封装
  4. 实现类和继承

参考; javascript中的闭包主要在哪些地方用到

https://zhidao.baidu.com/ques...

为什么要使用匿名函数自执行函数,好处是什么

定义变量时需要加上var,否则会默认添加到全局对象的属性上,或者别的函数可能误用这些变量;或者造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的), 实际中有的函数只调用一次使用自执行函数也是很好的。

我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。

哪些操作会造成内存泄漏

内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。

垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。

  1. setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
  2. 闭包
  3. 控制台日志
  4. 循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)

参考:

Js内存泄漏及解决方案

https://www.cnblogs.com/carek...

JS哪些操作会造成内存泄漏?

https://www.jianshu.com/p/763...

Javascript中的垃圾回收机制

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么 函数a执行后不会被回收的原因。

JS 垃圾回收机制的原理

内存泄漏:不再用到的内存,没有及时释放,就叫做内存泄漏。解决内存的泄露,垃圾回收机制会定期找出那些不再用到的内存(变量),然后释放其内存。

现在各大浏览器通常采用的垃圾回收机制有两种方法:标记清除,引用计数。

  1. 标记清除:js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在一个函数中声明一个变量,就将这个变量标记为"进入环境",从逻辑上讲,永远不能释放进入环境变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为"离开环境"。

    function test(){
    var a = 10; *//*被标记*"*进入环境*"*
    var b = "hello"; *//*被标记*"*进入环境*"*
    test(); *//*执行完毕后之后,*a*和*b*又被标记*"*离开环境*"*,被回收
  2. 引用计数:语言引擎有一张"引用表",保存了内存里面所有资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。

    const arr = [1,2,3,4];
    console.log("hello world")

上面的代码中,数组[1,2,3,4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它是会持续占用内存。

如果增加一行代码,解除arr对[1,2,3,4]引用,这块内存就可以被垃圾回收机制释放了。

let arr = [1,2,3,4];

console.log("hello world");

arr = null;

上面代码中,arr重置为null,就解除了对[1,2,3,4]的引用,引用次数变成了0,内存就可以释放出来了。

因此,并不是说有了垃圾回收机制,程序员就轻松了。你还是需要关注内存占用:那些很占空间的值,一旦不再用到,你必须检查是否还存在对它们的引用。如果是的话,就必须手动解除引用

JavaScript this指针、闭包、作用域

this:指向调用上下文

闭包:内层作用域可以访问外层作用域的变量

作用域:定义一个函数就开辟了一个局部作用域,整个js执行环境有一个全局作用域

原型链

JavaScript原型,原型链 ? 有什么特点?

* 原型对象也是普通的对象,是对象一个自带隐式的 proto 属性,原型也有可能有自己的原型,如果一个原型对象的原型不为null的话,我们就称之为原型链。

* 原型链是由一些用来继承和共享属性的对象组成的(有限的)对象链。

* JavaScript的数据对象有那些属性值?

  writable:这个属性的值是否可以改。

  configurable:这个属性的配置是否可以删除,修改。

  enumerable:这个属性是否能在for…in循环中遍历出来或在Object.keys中列举出来。

  value:属性值。

* 当我们需要一个属性的时,Javascript引擎会先看当前对象中是否有这个属性, 如果没有的话,就会查找他的Prototype对象是否有这个属性。

 function clone(proto) {

  function Dummy() { }

  Dummy.prototype = proto;

  Dummy.prototype.constructor = Dummy;

  return new Dummy(); //等价于Object.create(Person);

 } 

        function object(old) {

         function F() {};

         F.prototype = old;

         return new F();

        }

    var newObj = object(oldObject);

Javascript如何实现继承?

原型链继承,借用构造函数继承,组合继承,寄生式继承,寄生组合继承

数组

for-of和for-in的区别

遍历数组的时候:

for-of通过key直接拿数组里面的值

for-in通过key拿数组的索引

遍历对象的时候:

for-in通过key拿属性值

for-of不可以遍历对象

遍历对象的方法,for of为什么不能遍历对象?

for-of可以遍历的:arr/map/string,因为他们中实现了Symbol.iterator属性

for of如何遍历对象

注意:for-of目前js实现的对象有array,string,argument以及后面更高级的set,Map

当我们遍历对象的时候可以使用for-in,不过这种遍历方式会把原型上的属性和方法也给遍历出来,当然我们可以通过hasOwnProperty来过滤掉除了实例对象的数据,但是for-of在object对象上暂时没有实现,但是我们可以通过Symbol.iterator给对象添加这个属性,我们就可以使用for-of了,代码如下:

var p = {
            name:'kevin',
            age:2,
            sex:'male'
        }
        Object.defineProperty(p,Symbol.iterator,{
            enumberable:false,
            configurable:false,
            writable:false,
            value:function(){
                var _this = this;
                var nowIndex = -1;
                var key = Object.keys(_this);
                return {
                    next:function(){
                        nowIndex++;
                        return {
                            value:_this[key[nowIndex]],
                            done:(nowIndex+1>key.length)
                        }
                    }
                }
            }
        })

        //这样的话就可以直接通过for-of来遍历对象了
        for(var i of p){
            console.log(i) //kevin,2,male
        }

for-in遍历数组存在的问题(for-in更适合遍历对象)

var myArray = [1,2,4,5,6,7]
        myArray.name = "杀马特"
        Array.prototype.method = function(){
            console.log("length",this.length)
        }
        for(var index in myArray){
            console.log("myArray",myArray[index])
        }

使用for in 也可以遍历数组,但是会存在以下问题:

index索引为字符串型数字,不能直接进行几何运算
遍历顺序有可能不是按照实际数组的内部顺序
使用for in会遍历数组所有的可枚举属性,包括原型。例如上栗的原型方法method和name属性 所以for in更适合遍历对象,不要使用for in遍历数组。

使用for-of遍历数组:

//for-of遍历数组
        var myArray = [1, 2, 4, 5, 6, 7]
            myArray.name = "杀马特"
            Array.prototype.method = function () {
                console.log("length", this.length)
            }
            for (var index of myArray) {//这里的index输出的是value而不是索引
                console.log("index", index)
            }

for循环和forEach循环 哪个可以停止

for循环可以通过break停止,forEach一般是没有办法终止跳出。

如何实现forEach的终止跳出?

思路1 break 不能用,不可行,执行后会报错!

var arr = ['a','b','c']

arr.forEach((item,index) => {

if(item === 'b') break

console.log(item)

})

思路2 return false 会跳出当前的遍历执行==》a,c

var arr = ['a','b','c']

arr.forEach((item,index) => {

if(item === 'b') return false

console.log(item)

})

思路3 try...catch 语句跳出异常 //a exit done

try {

  arr.forEach((item,index) => {

    if(item === 'b') throw new Error('exist')

    console.log(item)

  })

} catch (e) {

  if(e.message=='exist'){

console.log(e.message)

throw e

} 

} finally {

  console.log('done')

}

程序最后可以终止退出循环,所以使用try...catch通过抛出异常的方式来终止程序继续执行是可行。

arr.map arr.filter arr.reduce的作用分别是?

【arr.map】

遍历数组通常使用for循环,ES5的话也可以使用forEach,ES5具有遍历数组功能的还有map、filter、some、every、reduce、reduceRight等,只不过他们的返回结果不一样。但是使用foreach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数

arr.map循环遍历,并且将符合条件的元素放入一个新数组返回

var arr = [1,2,3]
let b = [];
//map循环遍历,并且返回一个新数组
let a = arr.map(item=>{
    if(item>2){
        b.push(item)
        return b
    }
})
console.log(b) //返回符合条件的新数据[3]

因为react中没有v-for指令,所以循环渲染的时候需要用到arr.map方法来渲染视图

【arr.filter】

arr.filter 过滤器 可以过滤掉不符合要求的元素,内部返回false就直接过滤掉了,将符合条件的数据返回,也是不影响原数组。

//过滤器
arr = arr.filter(item=>{
    return item>2
})
console.log(arr) //过滤出来符合条件的数据[3]

【arr.reduce】

reduce为数组中每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接收四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用reduce的数组。reduce方法可以搞定的东西for循环或者forEach方法有时候也可以搞定。

var newArr = [4,5,6,7]
newArr.reduce((res,currentValue,index,arr)=>{//第一个参数为一个回调函数
    console.log(res,currentValue,index) //4,5,1
})
//res:默认为第一个值(4),currentValue:当前值(5),index:当前值的索引(1)
let result = newArr.reduce((res,currentValue,index,arr)=>{
    console.log(res,currentValue) //第一次:4,5;第二次:9,6;第三次:15,7
    return res+currentValue//第一次:9;第二次:15;第三次:22;最后结果为22
},10)//第二个参数为一个初始值,这里给它赋值为10
console.log(result)//现在res就变成了10,currentValue变成了4,依次累加,10+22 初始值+累加值

Filter、forEach、map、reduce之间的区别联系?

作用

every():对数组中的每一项运行给定函数,如果该函数对每一项都返回 true ,则返回 true。
some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true ,则返回 true
filter():对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
forEach():对数组中的每一项运行给定函数。这个方法没有返回值。

map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。

相同点

他们的参数都一样:

  • 在每一项上运行的函数(该函数有三个参数)

    • 函数第一个参数:数组项的值
    • 函数第二个参数:数组项的索引
    • 函数第三个参数:数组对象本身
  • 运行该函数的作用域对象——影响this的值(可选)

区别

filter()、forEach()、map()、some()、every()都是对数组的每一项调用函数进行处理。

区别:
– some()、every()的返回值 :true / false
– filter()、map()的返回值 :一个新数组
– forEach()无返回值。

使用filter()、forEach()、map()、some()、every()都不改变原数组。

参考:js小记:filter()、forEach()、map()、reduce()、reduceRight()的区别

https://blog.csdn.net/b954960...

splice()

splice(index,len,[item]) 注释:该方法会改变原始数组。

//删除起始下标为1,长度为1的一个值(len设置1,如果为0,则数组不变)
var arr = ['a','b','c','d'];
arr.splice(1,1);
console.log(arr); //['a','c','d']; 

//替换起始下标为1,长度为1的一个值为‘ttt’,len设置的1
var arr = ['a','b','c','d'];
arr.splice(1,1,'ttt');
console.log(arr); //['a','ttt','c','d'] 

var arr = ['a','b','c','d'];
arr.splice(1,0,'ttt');
console.log(arr); //['a','ttt','b','c','d'] 表示在下标为1处添加一项'ttt'

slice()

slice()方法可以基于当前数组获取指定区域元素 [start, end)

格式:数组.slice(start, end);

参数:start和end都是下标,[start, end),获取指定范围内的元素,生成新数组。(原数组是不会改变的。

返回值:提取出来元素组成的新数组。

split()和join()的区别

join函数获取一批字符串,然后用分隔符字符串将它们连接起来,从而返回一个字符串。
split()函数获取一个字符串,然后在分隔符处将其断开,从而返回一批字符串。
但是,这两个函数之间的区别在于join可以使用任何分割字符串将多个字符串连接起来,而split()只能使用一个字符分隔符将字符串断开。
简单地说,如果你用split(),是把一串字符串(根据某个分隔符)分成若干个元素存放在一个数组里。而join是把数组中的字符串连接成一个长串,可以大体上认为是split的逆操作。

push、pop、shift、unshift

  • push()方法可以在数组的末属添加一个或多个元素
  • shift()方法把数组中的第一个元素删除
  • unshift()方法可以在数组的前端添加一个或多个元素
  • pop()方法把数组中的最后一个元素删除

参考:数组的push()、pop()、shift()和unshift()方法

https://blog.csdn.net/qwe5027...

数组的方法

解析链接www.qq.com?name=jack&age=18&id=100,获取其中的查询参数,并格式为js对象的代码:如: {name:”jack”,age:18,id:100}

var str = "www.qq.com?name=jack&age=18";
var str2 = str.substring(str.lastIndexOf(“?")+1); //索引从后往前找
var arr = str2.split(“&”);//通过&符号去切割 切割成如下形式[‘name = jack’,’age=18’,'id=100']
var json = {};
for(var i=0;i

下列代码的运行结果是:

var array1 = [1,2]; 
var array2 = array1; // 数组指向的是同一个地址
array1[0] = array2[1]; // 把array[1]拿出来放在array1[0]的位置变成2,2
array2.push(3);
console.log(array1);// [2,2,3]
console.log(array2);// [2,2,3]
        var arr = [1,2]
        arr2 = arr;//arr2和arr1指向的是同一块内存空间
        arr2.push(3)
        console.log(arr) //[1,2,3]
        var arr = [1,2]
        arr2 = arr
        arr = [1,2,3] //arr    指向了新的内存空间
        arr2.push(4)
        console.log(arr)//[1,2,3]
        console.log(arr2)//[1,2,4]
        var a = {n:1}
        var b = a;
        a.x = a = {n:2}
        console.log(a); //a赋值了一个新的地址 输出{n:2}
        console.log(b); //b指向的是a原来的地址 输出{n:1,x:{n:2}}

JS数组去重

  1. 利用splice方法
var arr = [10, 20, 30, 40, 50, 40, 30, 20, 20, 20, 20, 10];
    function norepeat(arr){
        for(var i = 0; i < arr.length - 1; i++){
            for(var j = i + 1; j < arr.length; j++){
                if(arr[i] === arr[j]){
                    arr.splice(j,1);
                    j--
                }
            }
        }
    }
    alert(arr);//10,20,30,40,50,40,30,20,20,20,20,10
    norepeat(arr);
    alert(arr);//10,20,30,40,50
  1. 倒序删除
//倒序删除
    function norepeat(arr){
        for(var i = arr.length -1; i > 0; i--){
            for(var j = i - 1;j >= 0; j--){
                if(arr[i] === arr[j]){
                    arr.splice(j,1);
                }
            }
        }
    }
    alert(arr);//10,20,30,40,50,40,30,20,20,20,20,10
    norepeat(arr);
    arr.reverse();
    alert(arr);//10,20,30,40,50
  1. 利用对象的属性不会重复这一特性,校验数组元素是否重复
function unique(arr){
         var obj = {};
         var result = [];
         for(var i=0;i
  1. ES6中新增了数据类型set,set的一个最大的特点就是数据不重复。Set函数可以接受一个数组(或类数组对象)作为参数来初始化,利用该特性也能做到给数组去重。

    Set集合是默认去重复的,但前提是两个添加的元素严格相等,所以5和"5"不相等,两个new出来的字符串不相等。

下面展示了一种极为精巧的数组去重的方法

var newarr = [...new Set(array)];

//拓展 固定写法
    alert(arr);//10,20,30,40,50,40,30,20,20,20,20,10
    //下述写法的值,就是去重以后的数组。
    arr = [...new Set(arr)];
    alert(arr);//10,20,30,40,50

跨域

什么是跨域

由于 Javascript 同源策略的存在使得一个源中加载来自其它源中资源的行为受到了限制。即会出现跨域请求禁止。

通俗一点说就是如果存在协议、域名、端口或者子域名不同服务端,或一者为IP地址,一者为域名地址(在跨域问题上,域仅仅是通过“ url的首部 ”来识别而不会去尝试判断相同的IP地址对应着两个域或者两个域是否同属同一个IP),之中任意服务端旗下的客户端发起请求其它服务端资源的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。

同源策略

  1. 同协议
  2. 同域名
  3. 同端口号

解决跨域问题的主流方案是什么

  1. Jsonp 利用script标签发起get请求不会出现跨域禁止的特点实现
  2. window.name+iframe 借助中介属性window.name实现
  3. html5的 postMessage 主要侧重于前端通讯,不同域下页面之间的数据传递
  4. Cors需要服务器设置header:Access-Control-Allow-Origin
  5. Nginx反向代理 可以不需要目标服务器配合,不过需要Nginx中转服务器,用于转发请求(服务端之间的资源请求不会有跨域限制)

参考:Nginx反向代理、CORS、JSONP等跨域请求解决方法总结

https://blog.csdn.net/diudiu5...

cors实现请求跨域

https://blog.csdn.net/badmoon...

JQuery跨域方式

  1. 修改ajax的请求头(尽量不要用)

改成"Access-control-Allow-Origin"

  1. 通过PHP文件作为中转站,进行间接跨源(爬虫)
  2. JSONP跨域

JSONP原理

动态创建script标签,src属性连接接口地址,callback参数就是服务器返回给我们的数据。

缺点:jsonp只支持get请求方式,post方式不支持。

JSONP跨域的流程

  1. 在资源加载进来之前定义好一个函数,这个函数接收一个参数(数据),函数里面利用这个参数做一些事情。
  2. 然后需要的时候通过script标签加载对应的远程文件资源。
  3. 当远程文件加载进来的时候,就会去执行我们前面定义好的函数,并且把数据当做这个函数中的参数传进去。

JSONP跨域的缺点

  1. 它只支持GET请求而不支持POST等其它类型的HTTP请求;
  2. 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题
  3. json文件不能用jsonp方法。

AJAX

什么是AJAX

AJAX是一种用于快速创建动态网页的技术,通过在后台与服务器进行少量数据交换,AJAX可以实现网页实现异步更新,这意味着可以在不加载整个网页的情况下,对网页的某部分进行更新。

传统的网页(不使用AJAX)如果需要更新内容,必须重载整个网页。

AJAX的原理

思路:先解释异步,再解释ajax如何使用

Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。要清楚这个过程和原理,我们必须对 XMLHttpRequest有所了解。

XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是javascript可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。

AJAX的优点

AJAX不是新的编程语言,而是一种使用现有标准的新方法。

AJAX最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。

AJAX不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上运行。

AJAX的缺点

  1. AJAX干掉了Back和History功能,即对浏览器机制的破坏。 在动态更新页面的情况下,用户无法回到前一个页面状态,因为浏览器仅能记忆历史记录中的静态页面。一个被完整读入的页面与一个已经被动态修改过的页面之间的差别非常微妙;用户通常会希望单击后退按钮能够取消他们的前一次操作,但是在Ajax应用程序中,这将无法实现。
  2. 安全问题技术同时也对IT企业带来了新的安全威胁,ajax技术就如同对企业数据建立了一个直接通道。这使得开发者在不经意间会暴露比以前更多的数据和服务器逻辑。ajax的逻辑可以对客户端的安全扫描技术隐藏起来,允许黑客从远端服务器上建立新的攻击。还有ajax也难以避免一些已知的安全弱点,诸如跨站点脚步攻击、SQL注入攻击和基于credentials的安全漏洞等。
  3. 对搜索引擎的支持比较弱。如果使用不当,AJAX会增大网络数据的流量,从而降低整个系统的性能。

AJAX状态码说明

1**:请求收到,继续处理

2**:操作成功收到,分析、接受

3**:完成此请求必须进一步处理

4**:请求包含一个错误语法或不能完成

5**:服务器执行一个完全有效请求失败

AJAX的流程

  1. 声明Ajax对象xhr,创建 XMLHttpRequest 实例

IE8以后才有如下声明的方式

var xhr = new XMLHttpRequest();

XMLHttpRequest()是AJAX的原生对象

  1. 一旦新建实例,就可以使用open()方法发出 HTTP 请求。

填写请求信息,发出 HTTP 请求

第一个参数:请求方式:get/post

第二个参数:url(统一资源定位符)

第三个参数:true/false true异步,false同步

xhr.open('GET', 'http://www.example.com/page.php', true);
  1. 向服务器发送请求

xhr.send(),正在发送请求

  1. 等待数据响应,接收服务器传回的数据

xhr.onreadystatechange = function(){}

在回调函数中进行请求状态readyState的监控

当 readyState 等于 4 且状态为 200 时,表示响应内容解析完成,可以在客户端调用了。

返回的内容:

responseText:返回以文本形式存放的内容

responseXML:返回XML形式的内容

  1. 更新网页数据

AJAX中的GET和POST请求

get一般用来进行查询操作,url地址有长度限制,请求的参数都暴露在url地址当中,如果传递中文参数,需要自己进行编码操作,安全性较低。

post请求方式主要用来提交数据,没有数据长度的限制,提交的数据内容存在于http请求体中,数据不会暴漏在url地址中。

GET 还是 POST?

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。

然而,在以下情况中,请使用 POST 请求:

  • 无法使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据(POST 没有数据量限制)
  • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

AJAX如何实现跨域

理解跨域的概念:协议、域名、端口都相同才同域,否则都是跨域。

出于安全考虑,服务器不允许ajax跨域获取数据,但是可以跨域获取文件内容,所以基于这一点,可以动态创建script标签,使用标签的src属性访问js文件的形式获取js脚本,并且这个js脚本中的内容是函数调用,该函数调用的参数是服务器返回的数据,为了获取这里的参数数据,需要事先在页面中定义回调函数,在回调函数中处理服务器返回的数据,这就是解决跨域问题的主流解决方案。

一个页面从输入URL到页面加载显示完成,这个过程中都发生了什么?

分为4个步骤:

  1. 当发送一个 URL 请求时,不管这个 URL 是 Web 页面的 URL 还是 Web 页面上每个资源的 URL,浏览器都会开启一个线程来处理这个请求,同时在远程 DNS 服务器上启动一个 DNS 查询。这能使浏览器获得请求对应的 IP 地址。
  2. 浏览器与远程 Web 服务器通过 TCP 三次握手协商来建立一个 TCP/IP 连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在 浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,而后服务器应答并接受客户端的请求,最后由客户端发出该请求已经被接受的报文。
  3. 一旦 TCP/IP 连接建立,浏览器会通过该连接向远程服务器发送 HTTP 的 GET 请求。远程服务器找到资源并使用 HTTP 响应返回该资源,值为 200 的 HTTP 响应状态表示一个正确的响应。
  4. 此时,Web 服务器提供资源服务,客户端开始下载资源。

从用户输入URL,到浏览器呈现给用户页面,经过了什么过程?

用户输入URL,浏览器获取到URL

浏览器(应用层)进行DNS解析(如果输入的是IP地址,此步骤省略)

根据解析出的IP地址+端口,浏览器(应用层)发起HTTP请求,请求中携带(请求头header(也可细分为请求行和请求头)、请求体body),

header包含:

请求的方法(get、post、put..)

协议(http、https、ftp、sftp...)

目标url(具体的请求路径已经文件名)

一些必要信息(缓存、cookie之类)

body包含:

请求的内容

请求到达传输层,tcp协议为传输报文提供可靠的字节流传输服务,它通过三次握手等手段来保证传输过程中的安全可靠。通过对大块数据的分割成一个个报文段的方式提供给大量数据的便携传输。

到网络层, 网络层通过ARP寻址得到接收方的Mac地址,IP协议把在传输层被分割成一个个数据包传送接收方。

数据到达数据链路层,请求阶段完成

接收方在数据链路层收到数据包之后,层层传递到应用层,接收方应用程序就获得到请求报文。

接收方收到发送方的HTTP请求之后,进行请求文件资源(如HTML页面)的寻找并响应报文。

发送方收到响应报文后,如果报文中的状态码表示请求成功,则接受返回的资源(如HTML文件),进行页面渲染。

fetch

什么是fetch?

fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象

try {
  let response = await fetch(url);
  let data = response.json();
  console.log(data);
} catch(e) {
  console.log("Oops, error", e);
}

fetch的优点

  1. 符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里。
  2. 更好更方便的写法

fetch的优势

  1. 语法简洁,更加语义化
  2. 基于标准 Promise 实现,支持 async/await
  3. 同构方便,使用 isomorphic-fetch
  4. 更加底层,提供的API丰富(request, response)
  5. 脱离了XHR,是ES规范里新的实现方式

fetch的劣势

  1. fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
  2. fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: 'include'})
  3. fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
  4. fetch没有办法原生监测请求的进度,而XHR可以

axios

axios是什么

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中,主要是用于向后台发起请求的。

axios的用途

(1)向后台发送ajax请求数据。

(2) axios可以支持高并发请求,可以同时请求多个接口。

(3) axios可以防止CSRF/XSRF(跨站请求伪造)钓鱼网站。

(4)axios提供了拦截器、catch捕获。

封装axios

Get

import axios from 'axios'

export default ({url,data})=>{
    return axios.get(url,{
        params:data
    })
}

Post

import axios from 'axios'
import qs from 'querystring'

export default ({url,data})=>{
    return axios.post(url,qs.stringify(data))
}

利用axios实现数据请求

引入axios之后就可以在实例/组件上挂载一个$http属性,用这个就可以进行数据交互了。

提供了一个get方法,执行this.$http.get().then()可以异步执行,拿到promise对象的值

axios如何防止CSRF(跨站攻击)

在a页面登录注册成功后时候跳转到b页面,b页面可以获取你的cookie信息,把你的用户信息拦截到,然后恶意向别的网站散发一些邮件或者其他的东西,当你使用axios之后,就可以防止这种攻击,a页面存储的时候往cookie添加一个唯一个key,b页面如果是别的域名是获取不到这个key的,钓鱼网站就是把b页面这个域名篡改到别的位置去了,所以它无法获取到key值,也就拿不到用户信息,这样可以防止钓鱼网站的威胁。

如何在项目中通过路由守卫来实现登录拦截?讲一个在项目中使用路由守卫的案例?

拦截器的工作流程:

第一步:路由拦截

首先在定义路由的时候就需要多添加一个自定义字段requireAuth,用于判断该路由的访问是否需要登录。如果用户已经登录,则顺利进入路由, 否则就进入登录页面。

const routes = [
    {
        path: '/',
        name: '/',
        component: Index
    },
    {
        path: '/repository',
        name: 'repository',
        meta: {
            requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
        },
        component: Repository
    },
    {
        path: '/login',
        name: 'login',
        component: Login
    }
];

定义完路由后,我们主要是利用vue-router提供的钩子函数beforeEach()对路由进行判断。

router.beforeEach((to, from, next) => {
    if (to.meta.requireAuth) {  // 判断该路由是否需要登录权限
        if (store.state.token) {  // 通过vuex state获取当前的token是否存在
            next();
        }
        else {
            next({
                path: '/login',
                query: {redirect: to.fullPath}  // 将跳转的路由path作为参数,登录成功后跳转到该路由
            })
        }
    }
    else {
        next();
    }
})

其中,to.meta中是我们自定义的数据,其中就包括我们刚刚定义的requireAuth字段。通过这个字段来判断该路由是否需要登录权限。需要的话,同时当前应用不存在token,则跳转到登录页面,进行登录。登录成功后跳转到目标路由。

登录拦截到这里就结束了吗?并没有。这种方式只是简单的前端路由控制,并不能真正阻止用户访问需要登录权限的路由。还有一种情况便是:当前token失效了,但是token依然保存在本地。这时候你去访问需要登录权限的路由时,实际上应该让用户重新登录。
这时候就需要结合 http 拦截器 + 后端接口返回的http 状态码来判断。(具体方法见下一题目)

axios+vue如何在前端实现登录拦截?(路由拦截、http拦截)

登录流程控制中,根据本地是否存在token判断用户的登录情况,但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token,后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码,而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。

// 先导入vuex,因为我们要使用到里面的状态对象
// vuex的路径根据自己的路径去写
import store from '@/store/index';

// 请求拦截器axios.interceptors.request.use(  //axios的一个方法,用于请求之前拦截  
    config => {        
        // 每次发送请求之前判断vuex中是否存在token        
        // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
        // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 
        const token = store.state.token;        
        token && (config.headers.Authorization = token); (Authorization是后端自定义的名字) // 判断vuex中是否存在token,如果存在的话,则在每个http请求时header都加上token       
        return config;  //再把这个请求发送给我们的后台  
    },    
    error => {        
        return Promise.error(error); //如果有错误直接返回报错的相关内容   
})

如果后台传递过来的状态码是失效状态,就需要在response响应拦截器中清除本地token和清空vuex中token对象,通过commit触发mutations方法来同步更改vuex中的状态,清空token之后跳转到登录页面,让用户重新登录。

// 清除token
                    localStorage.removeItem('token');
                    store.commit('loginSuccess', null);
                    // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面 
                    setTimeout(() => {                        
                        router.replace({                            
                            path: '/login',                            
                            query: { 
                                redirect: router.currentRoute.fullPath 
                            }                        
                        });                    
                    }, 1000);

完整方法见:vue中Axios的封装和API接口的管理

https://juejin.im/post/5b55c1...

vue+axios 前端实现登录拦截(路由拦截、http拦截)

https://www.cnblogs.com/guoxi...

一个axios的简单教程

https://www.jianshu.com/p/13c...

axios与fetch、ajax区别?

(1)fetch和axios差不多,都是基于promise的,fetch没有封装xhr(ajax的原生对象),是新的语法,默认不传cookie,也监听不到请求的进度,axios可以监听到请求的进度。

(2)axios返回一个promise对象,拿到对象之后再去.then可以拿到resolve之后的内容。

axios.get(url,{params:{}}).then(res=>{}).catch(err=>{}); 
axios.post(url,{}).then(res=>).catch(err=>{});

(3)ajax主要是利用callback回调函数的形式。

JQuery中的ajax:$.ajax({url,data,success(){}}) 在回调函数获取数据

axios 和 ajax 的使用方法基本一样,只有个别参数不同;

axios({
            url: 'http://jsonplaceholder.typicode.com/users',
            method: 'get',
            responseType: 'json', // 默认的
            data: {
                //'a': 1,
                //'b': 2,
            }
        }).then(function (response) {
            console.log(response);
            console.log(response.data);
        }).catch(function (error) {
            console.log(error);
        })
$.ajax({
            url: 'http://jsonplaceholder.typicode.com/users',
            type: 'get',
            dataType: 'json',
            data: {
                //'a': 1,
                //'b': 2,
            },
            success: function (response) {
                console.log(response);
            }
        })

参考:ajax和axios、fetch的区别

https://www.jianshu.com/p/8bc...

JSON

什么是 JSON ?

  • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
  • JSON 是轻量级的文本数据交换格式
  • JSON 独立于语言:JSON 使用 Javascript语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态(PHP,JSP,.NET)编程语言都支持JSON。
  • JSON 具有自我描述性,更易理解

与 XML 相同之处

  • JSON 是纯文本
  • JSON 具有"自我描述性"(人类可读)
  • JSON 具有层级结构(值中存在值)
  • JSON 可通过 JavaScript 进行解析
  • JSON 数据可使用 AJAX 进行传输

与 XML 不同之处

  • 没有结束标签
  • 更短
  • 读写的速度更快
  • 能够使用内建的 JavaScript eval() 方法进行解析
  • 使用数组
  • 不使用保留字

为什么使用 JSON?

对于 AJAX 应用程序来说,JSON 比 XML 更快更易使用:

使用 XML

  • 读取 XML 文档
  • 使用 XML DOM 来循环遍历文档
  • 读取值并存储在变量中

使用 JSON

  • 读取 JSON 字符串
  • 用 eval() 处理 JSON 字符串

js中想要把json字符串转化为js对象的方式

  1. JSON.parse()
  2. eval()

假设我们有一个json字符串

          var str = '{"friends":[{"name":"梅梅","age":29,"sex":"女"},' +
                '{"name":"李华","age":18,"sex":"男"}]' +
                '}';

使用 JSON.parse()方法来转化

     var str = '{"friends":[{"name":"梅梅","age":29,"sex":"女"},' +
            '{"name":"李华","age":18,"sex":"男"}]' +
            '}';
    JSON.parse(str);

使用 eval()方法传化:

    var str = '{"friends":[{"name":"梅梅","age":29,"sex":"女"},' +
            '{"name":"李华","age":18,"sex":"男"}]' +
            '}';
    eval('('+str+')');
注意点: 通过 eval 来转化,如果返回的字符串内容是一个数组,可以直接转化,如果返回的字符串内容是一个对象,必须在字符串的前后加上()

当字符串内容是一个数组时 ,eval()的转化方式:

    var arr = '[{"name":"唐老鸭","sex":"男"},{"name":"红太狼","sex":"女"}]';
    eval(arr);

#### JSON.parse() 和 eval() 的区别

  1. eval方法不会去检查给的字符串是否符合json的格式,而JSON.parse解析不满足json格式的字符串时,会报错。
  2. 如果给的字符串中存在js代码eval也会一并执行,比如下面的代码段:

            var str1 = '{"log":alert("我被会执行的")}';
            eval("("+str1+")");

    执行该代码片段,会将 alert 语句作为js代码来执行,如果我们在开发中建议使用JSON.parse来转化,这样可以避免很多隐患,比如,我们访问第三方提供的接口,返回的串中包含 window.location.href这样的内容,那么执行了就会跳转到不明网站,好危险,所以最好还是使用JSON.parse()来解析。

JSON.parse()

JSON 通常用于与服务端交换数据。

在接收服务器数据时一般是字符串。

我们可以使用 JSON.parse() 方法将数据转换为 JavaScript 对象。

JSON.parse(text[, reviver])

JSON.stringify()

JSON 通常用于与服务端交换数据。

在向服务器发送数据时一般是字符串。

我们可以使用 JSON.stringify() 方法将 JavaScript 对象转换为字符串。

JSON.stringify(value[, replacer[, space]])

Promise

同步异步函数的区别

同步:阻塞,当前程序必须等前面一个程序执行完毕以后,才能够执行。

异步:非阻塞,前一个程序是否执行完毕,不影响后面程序的执行。

promise、async、await

async函数返回的是一个 Promise 对象,可以使用 then 方法添加回调函数,async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

await只能用在异步函数中,使用await必须要先使用async,命令后面返回的是Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。

Promise的概念

我理解的Promise就是一套为处理异步情况的方法。先创建一个promise对象来注册一个委托,其中包括委托成功及失败后的处理函数。然后基于这种表述方式,来将promise应用到各种异步处理的情况中。

promise写法案例

var promise = getAsyncPromise('fileA.txt');
promise.then( function(result){
      // 成功时的处理办法
}).catch( function(error){
      // 失败时的处理办法
})

// 返回一个promise对象

promise三个状态

pending(进行中)、fulfilled(已成功)和rejected(已失败)

Promise 实际就是一个对象, 从它可以获得异步操作的消息,Promise 对象有三种状态,pending(进行中)、fulfilled(已成功)和rejected(已失败)。Promise 的状态一旦改变之后,就不会在发生任何变化,将回调函数变成了链式调用。

Promise的设计思想是,所有异步任务都返回一个Promise实例。Promise实例有一个then方法,用来指定下一步的回调函数。

总的来说,传统的回调函数写法使得代码混成一团,变得横向发展而不是向下发展。Promise就是解决这个问题,使得异步流程可以写成同步流程。

Promise构造函数

JavaScript 提供原生的Promise构造函数,用来生成 Promise 实例。

var promise = new Promise(function (resolve, reject) {
  // ...

  if (/* 异步操作成功 */){
    resolve(value);
  } else { /* 异步操作失败 */
    reject(new Error());
  }
});

上面代码中,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己实现。

resolve函数的作用是,将Promise实例的状态从"未完成"变为"成功"(即从pending变为fulfilled),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。reject函数的作用是,将Promise实例的状态从"未完成"变为"失败"(即从pending变为rejected),在异步失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise的优缺点

Promise 的优点在于,让回调函数变成了规范的链式写法,程序流程可以看得很清楚。它有一整套接口,可以实现许多强大的功能,比如同时执行多个异步操作,等到它们的状态都改变以后,再执行一个回调函数;再比如,为多个回调函数中抛出的错误,统一指定处理方法等等。

而且,Promise 还有一个传统写法没有的好处:它的状态一旦改变,无论何时查询,都能得到这个状态。这意味着,无论何时为 Promise 实例添加回调函数,该函数都能正确执行。所以,你不用担心是否错过了某个事件或信号。如果是传统写法,通过监听事件来执行回调函数,一旦错过了事件,再添加回调函数是不会执行的。

Promise 的缺点是,编写的难度比传统写法高,而且阅读代码也不是一眼可以看懂。你只会看到一堆then,必须自己在then的回调函数里面理清逻辑。

Promise的优点

promise优点主要解决回调地狱问题,使得原本的多层级的嵌套代码,变成了链式调用,让代码更清晰,减少嵌套数。

Promise的缺点

首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

Promise的all方法

all方法是promise是类上自带的方法,并发读取,失败一个都失败了,时间只是一个读取的时间

第一个参数 传递的是数组,数组装的是一个个promise对象

调用后会再次返回一个promise实例

最好的写法

Promise.all([read('./name.txt'),read('./age.txt')]).then(([name,age])=>{
//data就是promise执行成功的结果类型是数组
    console.log({name,age});
}).catch((err)=>{
    console.log(err)
})

Promise的race方法

Promise.race ([promise1,promise2..])

当参数里的任意一个promise成功或失败后,该函数就会返回,并使用这个promise对象的值进行resolve或reject

    let promise1 = ajax({method:'GET',url:'/x.json'});
    let promise2 = ajax({method:'GET',url:'/y.json'});
    Promise.all([promise1,promise2]).then(function(){
        console.log('两个promise都执行完成了')
    });
    Promise.race([promise1,promise2]).then(function(){
        console.log('有一个promise先执行完成了')
    })

参考:简单理解Promise

https://www.jianshu.com/p/c8f...

存储

本地存储(Local Storage )和cookies(储存在用户本地终端上的数据)之间的区别是什么?

Cookies:服务器和客户端都可以访问;大小只有4KB左右;有有效期,过期后将会删除;

本地存储:只有本地浏览器端可访问数据,服务器不能访问本地存储,直到故意通过POST或者GET的通道发送到服务器;每个域5MB;没有过期数据,它将保留直到用户从浏览器清除或者使用Javascript代码移除。

cookie  4kb 随http请求发送到服务端 后端可以帮助前端设置cookie 
session 放在服务端  一般存放用户比较重要的信息
        (token 令牌  token ==> cookie /localstorage)  vuex

localStorage 本地存储(h5的新特性 draggable canvas svg)
             5M     纯粹在本地客户端  多个标签页共享数据
             sessionStorage  会话级别的存储
             往本地页面中存值的方法(localStorage.setItem(key,value))

讲一下cookie、sessionstorage、localstorage

相同点:都存储在客户端

不同点:

1.存储大小

  • cookie数据大小不能超过4k。
  • sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。

2.有效时间

  • localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;
  • sessionStorage 数据在当前浏览器窗口关闭后自动删除。
  • cookie 设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭

3.数据与服务器之间的交互方式

  • cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端
  • sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。

定义一个对象,里面包含用户名、电话,然后将其存入localStorage的代码

var json = {username:"张三",phone:17650246248}
for(var key in json){
    localStorage.setItem(key,json[key]);
}

cookie和session的区别

  1. 保持状态:cookie保存在浏览器端,session保存在服务器端
  2. 使用方式:如果不在浏览器中设置过期时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消失。
  3. 存储内容:cookie只能保存字符串类型,以文本的方式;session通过类似与Hashtable的数据结构来保存,能支持任何类型的对象(session中可含有多个对象)
  4. 存储的大小:cookie:单个cookie保存的数据不能超过4kb;session大小没有限制。
  5. 安全性:cookie:针对cookie所存在的攻击:Cookie欺骗,Cookie截获;session的安全性大于cookie。
  6. 应用场景:

cookie:(1)判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。

    (2)保存上次登录的时间等信息。

    (3)保存上次查看的页面

    (4)浏览计数

session:Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。

  (1)网上商城中的购物车

  (2)保存用户登录信息

  (3)将某些数据放入session中,供同一用户的不同页面使用

  (4)防止用户非法登录

  1. 缺点:

    cookie:

    (1)大小受限

    (2)用户可以操作(禁用)cookie,使功能受限

    (3)安全性较低

    (4)有些状态不可能保存在客户端。

    (5)每次访问都要传送cookie给服务器,浪费带宽。

    (6)cookie数据有路径(path)的概念,可以限制cookie只属于某个路径。

    session:

    (1)Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大。

    (2)依赖于cookie(sessionID保存在cookie),如果禁用cookie,则要使用URL重写,不安全

    (3)创建Session变量有很大的随意性,可随时调用,不需要开发者做精确地处理,所以,过度使用session变量将会导致代码不可读而且不好维护。

WebStorage

WebStorage的目的是克服由cookie所带来的一些限制,当数据需要被严格控制在客户端时,不需要持续的将数据发回服务器。

WebStorage两个主要目标:

(1)提供一种在cookie之外存储会话数据的路径。

(2)提供一种存储大量可以跨会话存在的数据的机制。

Localstroage与SessionStorage存储的区别

HTML5的WebStorage提供了两种API:localStorage(本地存储)和sessionStorage(会话存储)。

  1. 生命周期:localStorage:localStorage的生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。

localStorage除非主动删除数据,否则数据永远不会消失。

sessionStorage的生命周期是在仅在当前会话下有效。sessionStorage引入了一个“浏览器窗口”的概念,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage也是不一样的。

  1. 存储大小:localStorage和sessionStorage的存储数据大小一般都是:5MB
  2. 存储位置:localStorage和sessionStorage都保存在客户端,不与服务器进行交互通信。
  3. 存储内容类型:localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理
  4. 获取方式:localStorage:window.localStorage;;sessionStorage:window.sessionStorage;。
  5. 应用场景:localStoragese:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据。sessionStorage:敏感账号一次性登录;

WebStorage的优点

  1. 存储空间更大:cookie为4KB,而WebStorage是5MB;
  2. 节省网络流量:WebStorage不会传送到服务器,存储在本地的数据可以直接获取,也不会像cookie一样美词请求都会传送到服务器,所以减少了客户端和服务器端的交互,节省了网络流量;
  3. 对于那种只需要在用户浏览一组页面期间保存而关闭浏览器后就可以丢弃的数据,sessionStorage会非常方便;
  4. 快速显示:有的数据存储在WebStorage上,再加上浏览器本身的缓存。获取数据时可以从本地获取会比从服务器端获取快得多,所以速度更快;
  5. 安全性:WebStorage不会随着HTTP header发送到服务器端,所以安全性相对于cookie来说比较高一些,不会担心截获,但是仍然存在伪造问题;
  6. WebStorage提供了一些方法,数据操作比cookie方便;

WebStorage的方法

setItem (key, value) —— 保存数据,以键值对的方式储存信息。

getItem (key) —— 获取数据,将键值传入,即可获取到对应的value值。

removeItem (key) —— 删除单个数据,根据键值移除对应的信息。

clear () —— 删除所有的数据

key (index) —— 获取某个索引的key

Token

token是"令牌"的意思,服务端生成一串字符串,作为客户端请求的一段标识。用户登录的时候生成一个token,并将token返回客户端,客户端将收到的token放在cookie里,下次用户向服务端发送请求的时候,服务端只需要对比token。

作用:进行身份验证,避免表单重复提交。

在ajax请求后台时token添加到哪里

在ajax请求的标头中加Token

 1 function GetDateForServiceCustomer(userId) {
 2     $.ajax({
 3         url: 'http://*******/api/orders',
 4         data: {
 5             currUserId: userId,
 6             type: 1
 7         },
 8         beforeSend: function(request) {
 9             request.setRequestHeader("Authorization", token);
10         },
11         dataType: 'JSON',
12         async: false,//请求是否异步,默认为异步
13         type: 'GET',
14         success: function (list) {
15         },
16         error: function () {
17         }
18     });
19 }

参考:在ajax请求后台时在请求标头RequestHeader加token

https://www.cnblogs.com/zfdcp...

协议

HTTPS与HTTP的一些区别

  • HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。
  • HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
  • HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  • HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。

HTTP1.0和HTTP2.0有什么区别

新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。

多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。

header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。

服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。

参考:HTTP1.0、HTTP1.1 和 HTTP2.0 的区别

https://www.cnblogs.com/helua...

Websocket

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

WebSocket特点

(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

scoket套接字编程

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
如果将http比作轿车的话,那么socket就相当于发动机。

websocket跟 socket 的区别

软件通信有七层结构,下三层结构偏向与数据通信,上三层更偏向于数据处理,中间的传输层则是连接上三层与下三层之间的桥梁,每一层都做不同的工作,上层协议依赖与下层协议。基于这个通信结构的概念。

Socket 其实并不是一个协议,是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口。当两台主机通信时,让 Socket 去组织数据,以符合指定的协议。TCP 连接则更依靠于底层的 IP 协议,IP 协议的连接则依赖于链路层等更低层次。

WebSocket 则是一个典型的应用层协议。

总的来说:Socket 是传输控制层协议,WebSocket 是应用层协议。

webScoket如何兼容低浏览器

  1. Adobe Flash Socket
  2. ActiveX HTMLFile (IE)
  3. 基于 multipart 编码发送 XHR
  4. 基于长轮询的 XHR

参考:WebSocket解释及如何兼容低版本浏览器

https://www.cnblogs.com/pengc...

模块化开发

模块、函数、组件分别是什么?

模块:在webpack中,通过import引入的文件叫做模块。(js/css/png)
函数:是一些功能的合集。
组件:指的是页面的某一部分。

类是什么?类被编译成什么?

类:

class Banner extends React.Component{ //es6写法
    

}

ES5:通过模块定义类 ES6中定义的类也被编译成这样

function  Banner(){ //构造函数
    
}

组件化与模块化的区别,或者说怎么实现组件化开发,模块化开发

组件化:针对的是页面中的整个完整的功能模块,划分成浏览器可以识别的每个模块,例如头部Header、底部Footer、Banner。优点:代码复用、便于维护。

模块化:就是系统功能分离或独立的功能部分的方法,一般指的是单一的某个东西,例如:js、css

模块化开发的规范

(1)commonJS:自上而下同步进行 会阻塞 用在服务端

使用模块:const 变量名 = require("包名字")

声明模块:module.exports = {

对外的名字:函数

}

(2)AMD:异步加载文件 不会阻塞 用在浏览器端

define方法用于定义模块,RequireJS要求每个模块放在一个单独的文件里。

使用模块:require("模块名",function(模块对象)){

return 模块对象.函数();//回调函数

}

(3)CMD规范 中国人发明的,ECMA6来了就废弃了。

amd和cmd的区别

amd是require.js上的一个规范,cmd是sea.js的一个规范。

其实CMD与AMD规范并没什么本质的区别,区别在于他们对依赖模块的执行时机处理不同。虽然两者都是异步加载模块,但是AMD依赖前置,js可以方便知道依赖模块是谁,要依赖什么js那就先加载进来,至于你要依赖这些js来干吗得先等着,等我加载完了资源再商量;而CMD就近依赖,需要使用这个依赖模块时,我再加载进来用。

这就好比什么呢?就好像我今晚要看5集三国演义。AMD是先打开五个窗口,分别是1~5集,都缓冲着先,反正等下每集我都要看的;CMD则是先打开第一集的窗口,等到我第一集看完了,想看第二集了,就再跳转到第二集。

现在使用频率最高的,也是大家公认的好的模块化规范,是CommonJS。 后端(node.js)

require.js

实现了AMD规范的JavaScript工具库

RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。

require.js的诞生,就是为了解决这两个问题:

(1)实现了js文件的异步加载,避免网页失去响应

(2)管理模块之间的依赖性,便于代码的编写和维护

common.js

CommonJS就很简单了,一个js文件要输出去,只需使用module.export={xxx:你要输出的内容},而在另外一个js中,你要引用什么,就通过var xxxx=require("xxxx")引用进来就行了,这玩意并不是异步加载模块,而是同步一次性加载出来。

common.js与require.js与sea.js的区别:

配置模块化开发

<1>在头部引入

main.js管理当前页面的js文件,每一个页面都有自己的不同名字的main.js文件.

路径以dist文件为主

在外部修改文件,不允许修改dist中的文件,gulp会自动同步修改.

<2>在main中通过require.config配置当前页面需要的模块路径

配置模块的依赖路径:

shim:{

//由于jquery-cookie是基于jquery库封装的,所以要先引入jquery再引入jquery-cookie

"jquery-cookie":['jquery'],

//声明一下,不是AMD规范的模块

"parabola":{

exports:"_"

}

}

<3>编写CSS样式/JS代码

编写JS代码:

  1. 先封装成函数
  2. 再通过对象的形式对外暴露
define(["jquery"],function($){

function slide(){ 

$(function(){

function show(){ 

console.log("hello world");

}

return{

slide:slide,

show:show

}

})

<4>在main.js中加载

//配置当前整个项目所有模块的路径

require.config({

paths:{

"slide":"slide"

},



require(["slide"],function(slide){

slide.show();

slide.slide();

})

前端构建集成工具(打包)

什么是前端集成解决方案?

FIS(Front-end Integrated Solution)是专为解决前端开发中自动化工具、性能优化、模块化框架、开发规范、代码部署、开发流程等问题的工具框架。

前端集成解决方案解决了前端哪些问题?

  1. 开发团队代码风格不统一,如何强制开发规范。
  2. 前期开发的组件库如何维护和使用
  3. 如何模块化前端项目
  4. 服务器部署前必须压缩,检查流程如何简化,流程如何完善。

你使用过哪些前端构建集成?你用过什么打包工具?

  1. Gulp

gulp是工具链、构建工具,可以配合各种插件做js压缩,css压缩,less编译,替代手工实现自动化工作

(1)构建工具

(2)自动化

(3)提高效率用

  1. webpack

webpack是文件打包工具,可以把项目的各种js文、css文件等打包合并成一个或多个文件,主要用于模块化方案,预编译模块的方案

(1)打包工具

(2)模块化识别

(3)编译模块代码方案用

webpack打包

一款模块化打包工具,webpack是基于配置的,通过配置一些选项来让webpack执行打包任务

webpack在打包的时候,依靠依赖关系图,在打包的时候要告知webpack两个概念:入口和出口

plugins:在webpack编译用的是loader,但是有一些loader无法完成的任务,交由插件(plugin)来完成,插件的时候需要在配置项中配置plugins选项,值是数组,可以放入多个插件使用,而一般的插件都是一个构造器,我们,我们只需在plugins数组中放入该插件的实例即可。

loader:在webpack中专门有一些东西用来编译文件、处理文件,这些东西就叫loader。

webpack都用过哪些loader?

url-loader 可以将css中引入的图片(背景图)、js中生成的img图片处理一下,生成到打包目录里

url-loader/file-loader 将图片转成base64

html-withimg-loader 可以将html中img标签引入的img图片打包到打包目录

css-loader 可以将引入到js中的css代码给抽离出来

style-loader 可以将抽离出来的css代码放入到style标签中

sass-loader/less-loader sass/less预编译

postcss-loader 兼容前缀

babel-loader 将es6转成es5转成大部分浏览器可以识别的语法

vue-loader 把vue组件转换成js模块

为何要转译此模块?

可以动态的渲染一些数据,对三个标签做了优化