HTML5、CSS3
HTML5
HTML5新增特性
- 语义化标签:
header、nav、footer、section... - 媒体标签:
audio音频、video视频 - 表单类型属性:
email、number、时间控件、color颜色拾取器、placeholder、autofocus自动获取焦点... - cavas绘图
- web存储:
localStorage、sessionStorage
行内元素、块级元素有哪些
行内元素:a、span、strong、em、b、s、i、font
块级元素:div、ul、li、ol、dt、dh、li、p、h1-6
行内块元素: img 、input 、select 、textarea 、button 、label …
隐藏元素的方式
方式一: display:none
方式二: visibility:hidden 和display:none的区别在于,元素在页面消失后,其占据的空间依旧会保留着,所以它只会导致浏览器重绘而不会重排。
方式三:opacity属性表示元素的透明度,而将元素的透明度设置为0后,是一种隐藏元素的方法。 这种方法和visibility:hidden的一个共同点是元素隐藏后依旧占据着空间。
设置居中对齐的方式
文本居中
- 实现文字水平居中(使用text-align)
- 单行文本垂直居中(使用line-height),设置height的高度与line-height的高度相同
- 文本垂直居中(使用vertial-align)可以使用vertical-align实现垂直居中,不过vertical-align仅适用于内联元素和table-cell元素,因此之前需要转化。
- 图片垂直居中 (使用background-position), 例如:
.parentDiv {
background: url(xxx.jpg) #ffffff no-repeat center;
}
注:关键就在于最后的center,这个参数定义图片的位置。当然,background-position属性还可以写成“top left"或者"bottom right"等,也可以直接写数值。
div居中
首先编写一个简单的html代码,设置一个父div类名为parentDiv,再设置一个子div类名为childDiv。html代码如下:
<div class="parentDiv">
<div class="childDiv"></div>
</div>
.parentDiv {
10 width:400px;
11 height: 100px;
12 background-color: #00ff00;
13 margin: 20px;
14 }
15
16 .childDiv {
17 width: 200px;
18 height:50px;
19 background-color: #ff0000;
20 }
- 水平居中 当div.childDiv对象拥有固定宽度时,设置水平方向margin为auto,可以实现水平居中。
.parentDiv .childDiv {
margin: 0 auto;
}
- 实现水平居中(使用text-align:center) 如果给子盒子div.childDiv设置display: inline-block不影响整体布局时,可以将子盒子转化为inline-block,对父盒子设置text-align:center实现居中对齐。
/*
text-align: center;实现水平居中
需要子盒子设置为display: inline-block;
*/
.parentDiv {
text-align: center;
}
.parentDiv childDiv {
display: inline-block;
}
- table-cell元素居中 将父盒子设置为table-cell元素,可以使用text-align: center;和vertical-align: middle;实现水平、垂直居中。比较好的解决方案是利用三层结构模拟父子结构。html代码如下:
<div class="parentDiv tableCell">
<div class="id1">
<div class="childDiv">tableCell</div>
</div>
</div>
/*
table-cell实现居中
将父盒子设置为table-cell元素,设置
text-align: center; vertical-align: middle;
子盒子设置为inline-block元素
*/
.tableCell {
display: table;
}
.tableCell .id1 {
display: table-cell;
text-align: center;
vertical-align: middle;
}
.tableCell .childDiv {
display: inline-block;
}
绝对定位居中
- 绝对定位居中(利用margin实现偏移)
/*绝对定位实现居中*/
.parentDiv {
position: relative;
}
.parentDiv .childDiv {
position: absolute;
left:50%;
top:50%;
margin-left:-100px; /*利用margin实现偏移,设置为宽度和高度的一半的负值*/
margin-top:-25px;
}
- 绝对定位居中(利用transform实现偏移)
/*绝对定位实现居中,transform偏移*/
.parentDiv {
position: relative;
}
.parentDiv .childDiv {
position: absolute;
left:50%;
top:50%;
-webkit-transform: translate(-50%, -50%);
-moz-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-o-transform:translate(-50%, -50%) ;
transform:translate(-50%, -50%);
}
- 绝对定位居中(利用margin:auto实现偏移) 同样对子盒子实现绝对定位,这里使用top、right、bottom、left均为0,margin为auto实现偏移
/*绝对定位实现居中,margin:auto实现偏移*/
4 .parentDiv {
5 position: relative;
6 }
7
8 .parentDiv .childDiv {
9 position: absolute;
10 left:0; /*top、right、bottom、left均为0*/
11 top:0;
12 right: 0;
13 bottom: 0;
14 margin: auto;
15 }
flexbox居中
- Flexbox居中
.flex{
display: flex;
justify-content:center;
}
传统网页的三种布局方式(普通流,浮动,定位)
1.普通流
1.块级元素会独占一行,从上向下顺序排列
常用的元素:div,hr,p,h1-h6,ul,ol,form,table
2.行内元素会按照顺序,从左至右顺序排列,碰到父元素边缘则自动换行
常用的元素:span, a, i ,em
2.浮动(如何实现两个盒子的左右对齐)
网页布局原则: 多个快级元素纵向排列找标准流,多个块级元素横向排列找浮动。
浮动特性:
- 浮动元素会脱离标准流
- 浮动的元素一行内显示并且元素顶部对齐
- 浮动的元素会具有行内块元素的特性 注意: 浮动的盒子不在保留原先的位置,浮动元素会具有行内块元素特性。任何元素都可以浮动,不管原先是什么模型的元素,添加浮动后具有行内块元素相似的特性
如果快级盒子没有设置宽度,默认宽度和父级一样宽,但是添加浮动后,它的大小根据内容来决定。浮动的盒子中间是没有缝隙的,是紧挨着一起的,行内元素同理
为了约束浮动元素位置,我们网页布局一般采取的策略是: 先用标准流的父元素排列上下位置,之后内部子元素采取排列左右位置,符合网页布局第一准则。 当父元素不再有高度时(由于父级盒子很多情况下,不方便给高度,但是字盒子浮动又不占有位置,最后父级盒子高度为0时,就会影响下面的标准流盒子),需要清除浮动,清除浮动之后,父级就会根据浮动的子盒子自动检测高度,父级有了高度,就不会影响下面的标准流了
3.定位
定位的应用场景:
1.某个元素可以自由的在一个盒子内移动位置,并且压住其他盒子。 2.当我们滚动窗口的时候,盒子是固定屏幕某个位置的
定位 = 定位模式+边偏移
其中边偏移就是定位的盒子移动到最终位置。有top,bottom,left和right四个属性
画三角
div{
width:0;
height:0;
line-height:0;
font-size:0;
border:50px solid transparent;
border-left-color:pink;
}
iframe的优缺点
优点:
- 原封不动的吧嵌入网页展示出来
- 增加代码的可重用
- 用来加载速度较慢的内容
缺点:
- iframe阻塞onload事件加载
- 网页内容无法被搜索引擎识别,对SEO不友好
- 会产生很多页面,不利于管理
canvas和SVG的区别
canvas:通过javaScript来绘制2D图形,是逐像素进行渲染
- 依赖分辨率
- 不支持事件处理
- 能够以.png或.jpg的格式进行保存
- 适合做游戏
SVG:基于XML描述的2D图形语言,是矢量图
- 不依赖分辨率
- 支持事件处理
- 适合做大型区域渲染
回流重绘
回流: 当DOM变化影响了元素,比如元素的尺寸、布局、显示隐藏等改变了,需要重写构建。每个页面至少需要一次回流,就是在页面第一次加载的时候,这个时候一定会发生回流。
重绘: 当一个元素的外观发生变化,但是没有改变布局,重新渲染元素的外观。比如background-color、color
回流必将引起重绘,而重绘不一定会引起回流
如何避免回流重绘:
- 避免使用
table布局 - 尽可能在
DOM树的最末端改变class - 避免设置多层内联样式
- 开启GPU加速
- 将动画效果应用到
position属性为absolute或者fixed的元素上
src和href的区别
src: src指向外部资源的位置,将指向的内容嵌入到文档中当前标签所在的位置,如js脚本、img图片、iframe等
href: 用于在当前文档和引用资源之间确立联系,一般是用在link、a等元素
CSS3
CSS3新增特性
- 新增CSS选择器、伪类
- 特效:
text-shadow、box-shadow - 渐变:
gradient - 旋转过度:
transform、transtion - 动画:
animation
盒模型
盒模型都是有四部分组成:content、padding、border、margin
标准盒模型和IE盒模型的区别在于设置width和height时,对应的范围不同
- 标准盒模型的
width、height只包含了content - IE盒模型的
width、height包含了border、margin、padding
通过修改元素的box-sizing属性来改变元素的盒模型
box-sizeing: content-box表示标准盒模型(默认值)box-sizeing: border-box表示IE盒模型(怪异盒模型)
trastion和aniamtion的区别
transtion:属于过度属性,强调过度,需要一个事件进行触发(如鼠标进入、离开)类似flash的补间动画,设置一个开始帧和结束帧
aniamtion:属于动画属性,它的实现不需要触发事件,设定好后可自动执行,且可以循环播放。也是类似补间动画,但是可以设置多个关键帧
p、em、rem的区别
px固定像素单位,不能随其它元素的变化而变化em是相对于父元素的单位,会随着父元素变化而变化rem是相对于根元素html,它会随着html元素变化而变化
rem常用在移动端项目,设置根元素的fong-size,其它元素会随着根元素的变化而变化,从而达到不同手机屏幕的自适应大小。通常会配合postcss-pxtorem插件进行使用
如何解决1px问题
- 直接写
0.5px - 利用伪元素,先放大再缩小
- 使用viewport缩放来解决
什么是BFC布局,如何创建BFC布局?
BFC布局为块格式化上下文(Block Formatting Context,BFC), 是CSS布局的一个概念,里面的元素不会影响到外面的元素。
创建BFC:
- 元素设置浮动:
float有值并不为空 - 元素设置绝对定位:
position(absolute、fixed) overfilow值为:hidden、auto、scrolldisplay值为:inline-block、table-cell、table-caption、flex等
BFC作用:
- 解决
margin重叠问题:由于BFC是一个独立的区域,内部元素和外部元素互不影响,将两个元素变为BFC,就解决了margin重叠问题 - 创建自适应两栏布局:可以用来创建自适应两栏布局,左边宽高固定,右边宽度自适应。
- 解决高度塌陷问题:在子元素设置浮动后,父元素会发生高度的塌陷,也就是父元素的高度为0.解决这个问题,只需要将父元素变成一个BFC。
link和@import的区别
- link是HTML提供的标签,不仅可以加载CSS文件,还可以定义RSS、rel连接属性等
- @import是CSS提供等语法规则,只有导入样式表带作用。
- link标签引入的CSS被同时加载,而@import引入的CSS将在页面加载完毕后被加载
- @import是CSS2.1才有的语法,存在兼容性,而link作为HTML标签不存在兼容性问题
CSS选择器优先级
@important- 内联样式
- ID选择器
- 类选择器/属性选择器/伪类选择器
- 元素选择器/伪元素选择器
- 关系选择器/通配符选择器
CSS3新增伪类有那些?
:after在元素之前添加内容,也可以用来做清除浮动。:before在元素之后添加内容。:enabled已启用的表单元素。:disabled已禁用的表单元素。:checked单选框或复选框被选中。
绝对定位和相对定位的区别
position: absolute
绝对定位:是相对于元素最近的已定位的祖先元素
position: relative
相对定位:相对定位是相对于元素在文档中的初始位置
清除浮动的几种方式,各自的优缺点
父级div定义height
结尾处加空div标签clear:both
父级div定义伪类:after和zoom
父级div定义overflow:hidden
父级div也浮动,需要定义宽度
结尾处加br标签clear:both
为什么要初始化CSS样式?
- 因为浏览器的兼容问题,不同浏览器对有些标签的默认值是不同的,如果没对
CSS初始化往往会出现浏览器之间的页面显示差异。 - 当然,初始化样式会对
SEO有一定的影响,但鱼和熊掌不可兼得,但力求影响最小的情况下初始化
position有哪些值?有什么作用?
static。默认值,不脱离文档流,top,right,bottom,left等属性不生效。 relative。不脱离文档流,依据自身位置进行偏离,当子元素设置absolute,将依据它进行偏离。 absolute。脱离文档流,依据top,right,bottom,left等属性在正常文档流中偏移位置。 fixed。通过浏览器窗口进行定位,出现滚动条的时候,不会随之滚动。
知道css有个content属性吗?有什么作用?有什么应用?
css的content属性专门应用在 before/after伪元素上,用于来插入生成内容。最常见的应用是利用伪类清除浮动。
CSS在性能优化方面的实践
css压缩与合并、Gzip压缩css文件放在head里、不要用@import- 尽量用缩写、避免用滤镜、合理使用选择器
CSS3动画(简单动画的实现,如旋转等)
依靠CSS3中提出的三个属性:transition、transform、animation
transition:定义了元素在变化过程中是怎么样的,包含transition-property、
transition-duration、transition-timing-function、transition-delay。
transform:定义元素的变化结果,包含rotate、scale、skew、translate。
animation:动画定义了动作的每一帧(@keyframes)有什么效果,包括animation-
name,animation-duration、animation-timing-function、animation-delay、
animation-iteration-count、animation-direction
DOM 部分
1.DOM是什么:
DOM(Document Object Model)文档对象模型,是处理可扩展标志语言的标准编程接口。
DOM 是 W3C 的标准; [所有浏览器公共遵守的标准]
2.DOM0级和DOM2级有什么区别:
DOM0级中为某个dom元素绑定多个事件时,只有最后一个事件有效。onclick
DOM2级中可以为单个元素绑定多个事件,每个事件都可以被触发。addEventListener
3.textContent、innerText、innnerHTML、value的区别:
textContent用来获取和设置文本内容,与innerText的差别是:textContent获取到的内容包括了元素中的style标签和script标签的内容。
innerText只能获取和设置文本内容,不能获取和设置html代码
innerHTML可以获取和设置html代码
value获取的是表单元素的值
4.关于dom的api有什么:
节点创建型api:
document.createElement() document,createTextNode() parent.cloneNode(true) document.createDocumentFragment() 创建文档片段,解决大量添加节点造成的回流问题
页面修改型API:
parent.appendChild(child) parent.insertBefore(newNode,referenceNode) 将新元素添加到父元素中指定的子元素前面 parent.removeChild(child) parent.replcaeChild(newChild,oldChild)
节点查询型API:
document.getElementById()
document.getElementsByTagName() 返回的是一个即时的
HTMLCollection类型
document.getElementsByName() 根据指定的name属性获取元素,返回的是一个即时的NodeList
document.getElementsByClassName() 返回的是一个即时的HTMLCollection
document.querySelector() 获取匹配到的第一个元素,采用的是深度优先搜索。
docuemnt.querySelectorAll()
返回的是一个非即时的NodeList,也就是说结果不会随着文档树的变化而变化
节点关系型api:
父关系型:
node.parentNode()
兄弟关系型
node.previouSibling() 返回节点的前一个节点(包括元素节点,文本节点,注释节点)
node.previousElementSibling() 返回前一个元素节点
node.nextSibling() 返回下一个节点
node.nextElementSibling() 返回下一个元素节点
子关系型
parent.childNodes() 返回一个即时的NodeList,包括了文本节点和注释节点
parent.children() 一个即时的HTMLCollection,子节点都是Element
parent.firsrtNode()
parent.lastNode()
hasChildNodes()
元素属性型api:
element.setAttribute(“name”,“value”) 为元素添加属性
element.getAtrribute(“name”) 获取元素的属性
元素样式型api:
window.getComputedStyle(element) 返回一个CSSStyleDeclaration,可以从中访问元素的任意样式属性。
element.getBoundingClientRect() 返回一个DOMRect对象,里面⭐️ 包括了元素相对于可视区的位置top,left,以及元素的大小,单位为纯数字。可用于判断某元素是否出现在了可视区域。
6.说说前端中的事件流
什么叫Dom事件流?
事件发生时会在元素节点之间按照特定的顺序传播,整个过程分为捕获阶段,目标阶段和冒泡阶段,这个传播过程叫做Dom事件流。
事件冒泡:从事件源逐级向上传播到DOM最顶层节点的过程。
事件捕获:从DOM最顶层节点逐级向下传播到事件源的过程。
addEventListener用于指定事件处理程序,共接收三个参数。分别是触发事件,事件处理程序函数以及一个布尔值。第三个参数默认为false,表示在该事件的处理函数会在冒泡阶段被调用。若改为true,则表示事件处理函数会在捕获阶段被调用。 IE只支持事件冒泡。
7.如何让事件先冒泡后捕获
原本的事件流中,是先捕获再冒泡。
对于目标元素来说,如果DOM节点通过addEventListener同时绑定了两个事件监听函数,一个用于捕获,一个用于冒泡,那么两个事件的执行顺序是按照代码添加的顺序执行的。所以,先绑定冒泡的函数,再绑定捕获的函数,即可实现。
对于非目标元素来说,可以给捕获事件的处理程序添加一个定时器,将处理程序推入下一个宏任务执行。
8.说一下事件代理:
事件委托是指不在子节点单独设置事件监听器,而将事件监听器设置在父节点上,再利用冒泡原理使每一个子节点都能触发该事件。
事件委托的优点:只操作一次Dom,提高了程序的性能。
常用于:
ul和li标签的事件监听,一般采用事件委托机制将事件监听器绑定在ul上。
还适合动态元素的绑定,新添加的子元素不需单独添加事件处理程序。
9.事件类型相关:
(1)mouseover和mouseenter的区别
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移出事件是mouseout。
mouseenter:鼠标移入子元素时不会再次触发mouseenter事件,对应的移出事件是mouseleave。
(2)三种键盘事件的区别:
keyup: 松开键盘触发
keydown:按下键盘触发
keypress:不能识别功能键,比如ctrl,alt,shift,左右箭头。可以区分大小写。
在输入框中按下一个键的全过程:触发keydown/keypress事件->文字键入输入框中->触发keyup事件
按下按键后自动对焦输入框,应该使用keyup,不应该使用keydown/keypress,因为后者会使按键落入输入框中,对于回车键的话还不能使用keypress。
10.静态绑定事件与动态绑定事件的区别:
静态绑定事件是指直接在Html标签上通过οnclick="hide()"来绑定事件。
缺点:
html和js文件存在耦合,不符合结构和行为分离的原则。
可能存在引发错误,如果js代码还没加载就触发该事件则会抛出错误
动态绑定事件是指通过js动态绑定事件,element.onclick() element.addEventListener()。
11.元素的位置和大小-三大系列
① offset系列:
offsetTop(获取元素位置): 相对于带有‘定位’的父元素的偏移量
offsetHeight: content+padding+border
offsetParent: 返回带有定位的父元素
② cilent系列:
clientTop: 上边框border-top的宽度
clientHeight(获取元素宽高): content+padding,不包含border。
③ scroll系列:
scrollTop(获取滚动的距离): 向下滚动后,上面被卷去的距离,即隐藏的高度。
scrollHeight: content+padding ,其中的content包含了因为滚动被隐藏的部分。
④ document.clientWidth与document.style.width的区别:
区别1:前者可以获取任意样式表中的width样式值,包括行内样式的,内嵌样式的,外部样式的;后者只能获取行内的样式。
区别2:clientWidth获取的是数字型的,style获取的带有px后缀
区别3:clientWidth包含了padding,而style.width只包含content。
区别4:clientWidth是只读属性,所以一般用于获取元素的大小;而style.width是可读可写的,可用于获取,也可用于修改。
⑤ 特殊:
获取html元素:document.documentElement
获取body元素: document.body
获取可视区域的宽高: window.innerWeight 获取的宽度包括纵向滚动条的宽度。 document.documentElement.clientWidth 获取的是正宗的可视区域的宽度 document.body.clientWidth 获取的是body的宽度,即content+padding。 获取window向下滚动时被卷去的高度: window.pageYOffset (注意:不能使用window.scrollTop)
⑥ 判断一个元素是否已经出现在了可视区域:(此问题可应用在懒加载中)
方法一:计算比较麻烦
需满足条件: xxx.offsetTop(需要递归叠加获取)<= window.pageYOffset+document.documentElement.clientHeight
即该元素距页面顶端的距离 <= window向下滚动隐藏的距离+window的可视区域的高度。
方法二:使用element.getBoundingClientRect().top获取在可视区的位置。
window.addEventListener("scroll", function () {
let viewPortHeight = window.pageYOffset;
let offset = box2.getBoundingClientRect().top;
if (offset < viewPortHeight) {
if (offset + box2.offsetHeight < 0) {
console.log("他走了");
} else {
console.log("他来啦他来啦");
}
}
});
12.鼠标坐标
clientX,clientY: 鼠标在可视区的坐标,可视区即展示在用户面前的页面区域。
pageX,pageY: 鼠标在整个html页面的坐标。一般实际应用使用pageX和pageY。
screenX,screenY: 鼠标在电脑屏幕的坐标,即整个电脑屏幕。
13.js拖动及拖拽功能的实现
(1)拖动功能的实现: 前置条件:
1.拖动事件的三个过程:鼠标按下mousedown,鼠标移动mousemove,鼠标松开mouseup
鼠标按下后执行mousemove事件。
2.盒子采用绝对定位,通过left和top属性来修改位置。
方法一:(直接根据鼠标移动的距离确定元素移动的距离)
鼠标的坐标通过clientX,clientY获取:
盒子的定位信息:鼠标移动时候的坐标-鼠标按下去时候的坐标+元素初始情况下的offetLeft.
方法二:
鼠标的坐标通过pageX,pageY获取:
先计算鼠标在盒子中的坐标,这是不变的。然后在mousemove的时候通过pageX和pageY减去在盒子中的坐标计算出盒子边缘应该修改为的偏移量。
(2)拖拽功能的实现: 使用html5提供的拖拽API(Drag 和 drop)
拖拽功能涉及的基本事件:
dragstart:在开始拖放元素时触发。(事件源:被拖拽的元素)
这一步需要做的是获取被拖拽元素的id。拖拽事件对象中的dataTransfer属性是专门用来存储拖动过程中的数据的。ev.dataTransfer.setData("key",value) dragover:在被拖放在某元素内移动时触发。(事件源:目标元素)
阻止dragover的默认事件(不允许被拖拽) drop:目标元素完全接受被拖放元素时触发。(事件源:目标元素)
阻止drop的默认事件(以链接的形式打开),然后获取之前保存的元素的id ev.dataTransfer.getData("key"),然后将该元素添加到目标元素中。 ———————————————— 版权声明:本文为CSDN博主「qq_41800649」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/qq_41800649…
BOM部分
BOM 浏览器对象 DOM 内容结点
BOM(Browser Object Model)是指浏览器对象模型,可以对浏览器窗口进行访问和操作。常用的BOM属性:
1.location对象
2.history对象
history.back()后退一页;history.forward()前进一页
3.Navigator对象
navigator.userAgent -- 返回用户代理头的字符串表示(就是包括浏览器版本信息等的字符串)
navigator.cookieEnabled -- 返回浏览器是否支持(启用)cookie
1.实用的BOM属性对象方法:
location对象
location.href : 返回或设置当前文档的URL
location.search : 返回URL中的查询字符串部分。例如 www.dreamdu.com/dreamdu.php… 返回包括(?)后面的内容?id=5&name=dreamdu
location.hash : 返回URL#后面的内容,如果没有#,返回空
location: 返回URL中的域名部分,例如www.dreamdu.com
location.hostname : 返回URL中的主域名部分,例如dreamdu.com
location.pathname :返回URL的域名后的部分。例如 www.dreamdu.com/xhtml/ 返回/xhtml/
location.port : – 返回URL中的端口部分。例如 www.dreamdu.com:8080/xhtml/ 返回8080
location.protocol : 返回URL中的协议部分。例如 www.dreamdu.com:8080/xhtml/ 返回(//)前面的内容http:
location.assign() : 重定向页面,与location.href一样,会记录历史,能后退页面
location.replace() : 设置当前文档的URL,不记录历史,不能后退页面
location.reload() : 重载当前页面,相当于F5。添加参数true则表示强制刷新,直接从服务器获取数据,不从浏览器缓存中取数据,相当于Ctrl+F5
history对象
history.go(n) : 前进或后退指定的页面数;
history.back() : 后退一页
history.forward() : 前进一页
navigator对象
navigator :包含了用户浏览器的信息
navigator.userAgent : 返回用户代理头的字符串表示(就是包括浏览器版本信息等的字符串)
navigator.cookieEnabled : 返回浏览器是否支持(启用)cookie
2. setTimeout(fn,100);100毫秒是如何权衡的:
100ms指的是将回调函数加入到任务队列所花的时间。至于具体什么时候执行,需要看主线程的执行栈中是否还有任务在执行。
3.定时器实现动画的最佳时间:16.6ms
大多数电脑显示器的刷新频率是60HZ,大概相当于每秒钟重绘60次。因此,最平滑的动画效的最佳循环间隔是1000ms/60,约等于16.6ms
4.setInterval存在的问题:
定时器的代码执行部分不断的被调入任务队列中,如果定时器的执行时间比间隔时间长,最终可能导致定时器堆叠在一起执行。
js引擎为了解决这个问题,采用的方式是若任务队列中存在这个定期器,则不会将新的定时器放入任务队列,这样做的弊端是可能导致某些间隔被跳过。
解决方法:
循环调用setTimeout来实现setInterval:(即用setTimeout来实现setInterval)
setTimeout(function fn(){
...
setTimeout(fn,delay)
},delay)
5.requestAnimationFrame:
js动画的要求:
一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化。
用定时器实现js动画存在的问题:
定时器回调函数执行的时机不精确。定时器中的延时指的是将回调函数加入到任务队列所需花的时间,如果主线程中还有任务在执行,就不能确保回调函数在放入队列后马上执行,这就造成了执行时机的不精确。
requestAnimationFrame:
特点:requestAnimationFrame采用系统时间间隔,保证了最佳的绘制效率。
使用方法:requestAnimationFrame接收一个回调函数,这个回调函数会在下一次浏览器重绘之前调用。
6.分别用setInterval,setTimeout,requestAnimationFrame制作有个简单的进度条效果:
setInterval:
<div
style="width: 0; height: 20px; background-color: orange"
id="div"
></div>
<script>
let timer = setInterval(() => {
if (parseInt(div.style.width) >= 500) {
return clearInterval(timer);
}
console.log(div.style.width);
div.style.width = parseInt(div.style.width) + 5 + "px";
div.innerHTML = parseInt(div.style.width) / 5 + "%";
}, 16);
</script>
setTimeout:
<div
style="width: 0; height: 20px; background-color: orange"
id="div"
></div>
<script>
let timer = setTimeout(function fn() {
if (parseInt(div.style.width) < 500) {
div.style.width = parseInt(div.style.width) + 5 + "px";
div.innerHTML = parseInt(div.style.width) / 5 + "%";
timer = setTimeout(fn, 16);
} else {
clearTimeout(timer);
}
}, 16);
</script>
requestAnimationFrame:类似于setTimeout,需要一次次的调用
<div
style="width: 0; height: 20px; background-color: orange"
id="div"
></div>
<script>
let timer = requestAnimationFrame(function fn() {
if (parseInt(div.style.width) < 500) {
div.style.width = parseInt(div.style.width) + 5 + "px";
div.innerHTML = parseInt(div.style.width) / 5 + "%";
requestAnimationFrame(fn);
} else {
cancelAnimationFrame(timer);
}
});
</script>
7.js中的轮播实现原理?假如一个页面上有两个轮播,你会怎么实现?
1.让图片存在一个数组中,然后将最后一张图片重复添加在数组的头部,将第一张图片重复添加在数组的最后。
2.然后准备一个只能显示一张图片的盒子,对盒子做溢出隐藏处理。
3.通过定时器增减索引,显示对应的图片,实现轮播功能。
如果有两个轮播,可封装一个轮播组件,将需要轮播的图片作为参数传递。
———————————————— 版权声明:本文为CSDN博主「qq_41800649」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/qq_41800649…
dom与bom
javacsript是通过访问BOM(Browser Object Model)对象来访问、控制、修改客户端(浏览器),由于BOM的window包含了document,window对象的属性和方法是直接可以使用而且被感知的,因此可以直接使用window对象的document属性,通过document属性就可以访问、检索、修改XHTML文档内容与结构。因为document对象又是DOM(Document Object Model)模型的根节点。可以说,BOM包含了DOM(对象),浏览器提供出来给予访问的是BOM对象,从BOM对象再访问到DOM对象,从而js可以操作浏览器以及浏览器读取到的文档。其中 DOM包含:window
JS基础
基础数据类型
string、number、boolean、object、function、undefined、null、symbol
null和undefined的区别:null表示对是一个空的对象(object)、undefined是申明了但没赋值,在使用typeof检测类型时,null为object,undefined为undefined
null == undefined // true
null === undefined //false
复制代码
值类型(基本数据类型):string、number、boolean、undefined、null、symbol
引用类型:object、function、array
值类型和引用类型区别:
- 值类型保存在栈中,占用空间固定,当一个方法执行时,每个方法都会创建自己的内存栈,方法中定义的变量会存放在这个内存栈中。当方法执行结束时,这个内存栈也会被销毁。所以,栈中存储的是基础变量。而引用变量存储在栈中是指向堆中的数组或对象的引用地址。这也就是为何修改引用类型总会影响到其它指向这个地址的引用变量。
- 值类型可以使用
typeof进行数据类型检测 - 引用类型保存在堆中,占用空间不固定。创建对象会保存到堆内存中,这个内存不回随着方法结束而销毁,因为这个对象还可能被另外一个变量所引用,只有当一个对象没有被任何变量引用时,系统的垃圾回收机制会将它回收。
- 引用类型使用
instanceof检测数据类型 - 使用new()方法构造出来的对象是引用类型
闭包
闭包就是能够读取到其它函数内部变量的函数,创建一个最简单的闭包,就是在一个函数内部创建另外一个函数,创建的函数可以访问到当前函数的局部变量。
闭包优点:
- 创建全局私有变量,避免变量全局污染
- 可以实现封装、缓存等
闭包缺点:
- 创建的变量不能被回收,容易小号内存,使用不当会导致内存溢出
变量提升、作用域、作用域链
变量提升
js代码在解析的时候,会将所有的变量函数,提升到代码的最上面。变量提升,提升的只是变量申明,并不会吧变量赋值提升上来
console.log(i) // 4
var i = 4
复制代码
作用域
作用域是一个变量或函数的可访问范围,作用域控制着变量或函数的可见性和生命周期
-
全局作用域:可以全局访问
- 最外层函数和最外层定义的变量拥有全局作用域
- window上的对象属性方法拥有全局作用域
- 为定义直接复制的变量自动申明拥有全局作用域
- 过多的全局作用域变量会导致变量全局污染,命名冲突
-
函数作用域:只能在函数中访问使用哦
- 在函数中定义的变量,都只能在内部使用,外部无法访问
- 内层作用域可以访问外层,外层不能访问内存作用域
-
ES6中的块级作用域:只在代码块中访问使用
-
使用ES6中新增的
let、const什么的变量,具备块级作用域,块级作用域可以在函数中创建(由{}包裹的代码都是块级作用域) -
let、const申明的变量不会变量提升,const也不能重复申明 -
块级作用域主要用来解决由变量提升导致的变量覆盖问题
var i = 3 function fnc() { console.log(i); var i = 6; } fnc() // undefined 复制代码
-
作用域链
变量在指定的作用域中没有找到,会依次向上一层作用域进行查找,直到全局作用域。这个查找的过程被称为作用域链。
call、apply、bind区别
- 都可以用作改变
this指向 - call和apply的区别在于传参,call、bind都是传入对象。apply传入一个数组。
- call、apply改变this指向后会立即执行函数,bind在改变this后返回一个函数,不会立即执行函数,需要手动调用。
new操作符干了什么操作
- 创建一个空对象
- 设置原型,将对象的原型设置到函数的
prototype对象上 - 改变this指向,将this指向该对象,并执行构造函数。
- 判断函数的返回类型,如果是值类型,返回创建的对象。如果是引用类型,返回这个引用类型的对象。
原型、原型链
- 原型: 每个对象在内部初始化的时候,都会初始化一个原型属性 ,而对象的
_proto_属性指向它的原型对象。 - 原型链: 当我们访问实例对象属性时,如果这个属性不存在,那么就会去它的原型对象上进行查找,而这个原型对象又会有自己的原型,会这样一直查找,知道找到顶级对象object为止。这个查找的过程被称为原型对象。
继承
原型链继承
利用对象的原型链,将子类Son.prototype指向父类的构造函数创建出来的实例对象new Person()
🌰
function Person() {
...
};
function Son() {
....
};
// 关键代码
Son.prototype = new Person();
复制代码
优点:
- 子类可以继承父类构造函数、原型上的属性方法
缺点:
- 父类引用类型的实例对象被共享,容易造成修改的混乱。
- 创建子类的时候不能向父类传参
构造函数继承
利用.call()或者.apply()方法,在子类中借用父类的构造函数,初始化父类构造函数。
🌰
function Person(name) {
...
};
function Son(name) {
// 关键代码
Person.call(this, name)
...
}
复制代码
优点:
- 子类在继承父类时,可以向父类构造函数中传参。
- 不会造成子类势力之间引用属性共享。
缺点:
- 只能继承父类构造函数中的属性方法,无法访问原型上的方法。
- 每个子类都会创建一个父类副本
组合继承
组合继承,将原型链继承和构造函数继承融合在一起。
🌰
function Person(name) {
...
};
function Son(name) {
// 借用构造函数继承关键代码
Person.call(this, name);
};
// 原型链式继承关键代码
Son.prototype = new Person();
// 将子类的构造函数指向自己
Son.prototype.constructon = Son;
复制代码
优点:
- 结合前面两种继承方式的优点,子类的实例可以访问到父类原型上的属性方法
- 子类的实例之间不会被共享
缺点:
- 调用了两次父类构造函数
原型式继承
用函数包装一个对象,返回这个函数的调用(也就是ES5的Object.create的模拟实现),将传入的对象作为创建对象的原型
🌰
function create(obj) {
// 创建一个空的的构造函数
function F() {};
// 将空的构造函数原型指向传递进来的对象
F.prototype = obj;
// 返回一个实例对象
return new F();
}
const obj = {
name: 'zs',
...
};
const newObj1 = create(obj);
const newObj2 = create(obj);
复制代码
优缺点和原型链式继承一样,引用类型还是会被共享。
寄生式继承
在原型式继承基础上,在函数内部来做增强函数,返回对象。
🌰
function createObj(obj){
// 获取继承的子类对象,也就是上面的create方法实现
const newObj = Object.create(obj);
// 函数增强
newObj.say = function() {
...
}
// 返回对象
return newObj;
}
复制代码
类似原型式继承,但在原有的基础上可以自定义属性方法,依旧没有解决引用值被共享问题。(跟借用构造函数模式一样,每次创建对象都会创建一遍方法。)
寄生组合式继承
结合寄生继承和组合式继承优缺点,组合式继承缺点为调用了两次父类构造函数,优点为解决了引用值被共享问题。而寄生式继承缺点为没有解决引用值被共享问题,只要将两者结合就得到完美的继承方式。
🌰
function createObj(son, parent) {
// 寄生式继承,利用父类构造函数的原型对象创建出一个新的对象,解决组合式继承创建两个父类问题
const newObj = Objcet.create(parent.prototype);
// 将新对象的constructor构造函数执行子类
newObj.constructor = son;
// 将子类构造函数的原型指向心的 原型式继承
son.protoytype = newObj;
}
function Person(name) {
...
};
function Son(name) {
// 构造函数继承
Person.call(this, name);
...
};
// 实现继承
createObj(Son, Person)
// 更简洁的方式 在组合式继承的基础上 直接将son的原型通过API指向person的原型
Son.prototype = Object.create(Person.prototype);
const obj1 = new Son('zx');
const obj2 = new Son('lw');
复制代码
深拷贝、浅拷贝
浅拷贝
浅拷贝只是复制对象的值类型,通过Object.assign或者扩展运算符即可实现
深拷贝
通过递归实现
function deepClone(obj) {
// 判断是否为对象
if (!obj || typeof obj !=='object') return obj;
// 根据obj类型创建数组还是对象
let newObj = obj instanceof Object ? {} : [];
// 循环遍历obj,处理子元素为对象,递归拷贝
for (key in obj) {
if (obj.hasOwnProperty(key)) {
newOb[key] = deepClone(obj[key])
};
};
return newObj;
};
复制代码
事件循环机制EventLoop(浏览器)
栈、队列理解
栈(Stack)中的任务后进先出,js中的执行栈是一个存储函数的栈结构,栈中的任务执行遵循先进后出的原则依次执行。队列(Queue)中的任务先进先出,js运行时创建一个任务队列,用来处理列表(事件)和待执行的回调函数
宏观任务、微观任务
js中的任务分为两种:宏观任务(MacroTask|Task)、微观任务(MicorTask)。
- 宏任务:
script全部代码、setTimeout、setInterval、I/O、UI Rendering - 微任务:
Promise.then、Process.nexTick(Node独有)、MutationObserver
同步任务、异步任务
js有一个主线程和一个执行栈(调用栈) ,所有的任务都会放到执行栈中等待被主线程执行。
js代码在执行时,所有函数都会压入执行栈中。同步任务会按照后进先出原则依次执行,直到执行栈清空。异步任务会在异步任务有了结果后,将注册的回掉函数放入异步任务队列中,等待主线程空闲后(执行栈中的同步任务执行完毕) 。
异步队列中的任务又分为宏观任务和微观任务,当当前执行栈清空后,处理异步队列中的任务时,首先会判断是否有微观任务可执行,如果有就将微观任务压入执行栈中执行。当微观队列中的任务在执行栈被执行完毕,再来异步队列中将宏观任务放入执行栈。
简单的来说:
- 执行同步代码,这属于宏观任务
- 所有代码执行完毕,执行栈清空,执行异步队列中任务
- 异步队列中,先执行微观任务
- 微观任务执行完毕,再执行宏观任务
节流防抖
节流(throttle):在n秒内只允许执行一次,
防抖(debounce):在n秒内多次触发但不执行,而是在n秒后执行,如果n秒内触发则重新计算。
事件冒泡、事件委托
事件发生的三个阶段:捕获阶段、目标阶段、冒泡阶段
-
事件冒泡:在一个对象上触发某类事件,如果此对象绑定了事件,就会触发事件,如果没有,就会向这个对象的父级对象传播,最终父级对象触发事件。
-
如何阻止:
- 普通浏览器:
event.stopPropagation() - IE浏览器:
event.cancelBubble = true;
- 普通浏览器:
-
-
事件委托:利用浏览器事件冒泡机制。事件在冒泡的过程中会传到父节点,并且父节点可以通过事件对象获取到目标节点,可以吧子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件
- 事件委托可以不必要为每一个子节点都绑定监听事件,减少内存上的消耗。
- 使用事件委托还可以实现事件的动态绑定,比如新增一个子节点,并不需要为此单独增加一个监听事件,可以由父元素中的监听函数来处理。
事件冒泡与事件捕获
事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。
<div id="outer">
<p id="inner">Click me!</p>
</div>复制代码
上面的代码当中一个div元素当中有一个p子元素,如果两个元素都有一个click的处理函数,那么我们怎么才能知道哪一个函数会首先被触发呢?
为了解决这个问题微软和网景提出了两种几乎完全相反的概念。
事件冒泡
事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。
因此上面的例子在事件冒泡的概念下发生click事件的顺序应该是****
p -> div -> body -> html -> document
事件捕获
与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
上面的例子在事件捕获的概念下发生click事件的顺序应该是:
document -> html -> body -> div -> p
事件冒泡和事件捕获过程图:
\
1-5是捕获过程,5-6是目标阶段,6-10是冒泡阶段;
事件代理
在实际的开发当中,利用事件流的特性,我们可以使用一种叫做事件代理的方法。
<ul class="color_list">
<li>red</li>
<li>orange</li>
<li>yellow</li>
<li>green</li>
<li>blue</li>
<li>purple</li>
</ul>
<div class="box"></div>复制代码
.color_list{
display: flex;
display: -webkit-flex;
}
.color_list li{
width: 100px;
height: 100px;
list-style: none;
text-align: center;
line-height: 100px;
}
//每个li加上对应的颜色,此处省略
.box{
width: 600px;
height: 150px;
background-color: #cccccc;
line-height: 150px;
text-align: center;
}
复制代码
我们想要在点击每个 li 标签时,输出li当中的颜色(innerHTML) 。常规做法是遍历每个 li ,然后在每个 li 上绑定一个点击事件:
var color_list=document.querySelector(".color_list");
var colors=color_list.getElementsByTagName("li");
var box=document.querySelector(".box");
for(var n=0;n<colors.length;n++){
colors[n].addEventListener("click",function(){
console.log(this.innerHTML)
box.innerHTML="该颜色为 "+this.innerHTML;
})
}复制代码
这种做法在 li 较少的时候可以使用,但如果有一万个 li ,那就会导致性能降低(少了遍历所有 li 节点的操作,性能上肯定更加优化)。
这时就需要事件代理出场了,利用事件流的特性,我们只绑定一个事件处理函数也可以完成:\
function colorChange(e){
var e=e||window.event;//兼容性的处理
if(e.target.nodeName.toLowerCase()==="li"){
box.innerHTML="该颜色为 "+e.target.innerHTML;
}
}
color_list.addEventListener("click",colorChange,false)复制代码
**由于事件冒泡机制,点击了 li 后会冒泡到 ul ,此时就会触发绑定在 ul 上的点击事件,再利用 target 找到事件实际发生的元素,就可以达到预期的效果。
**
**使用事件代理的好处不仅在于将多个事件处理函数减为一个,而且对于不同的元素可以有不同的处理方法。假如上述列表元素当中添加了其他的元素节点(如:a、span等),我们不必再一次循环给每一个元素绑定事件,直接修改事件代理的事件处理函数即可。****
**
(1)toLowerCase() 方法用于把字符串转换为小写。语法: stringObject.toLowerCase()
返回值: 一个新的字符串,在其中 stringObject 的所有大写字符全部被转换为了小写字符。
(2)nodeName 属性指定节点的节点名称。如果节点是元素节点,则 nodeName 属性返回标签名。如果节点是属性节点,则 nodeName 属性返回属性的名称。对于其他节点类型,nodeName 属性返回不同节点类型的不同名称。
所有主流浏览器均支持 nodeName 属性。
阻止事件冒泡
1. 给子级加 event.stopPropagation( )
$("#div1").mousedown(function(e){
var e=event||window.event;
event.stopPropagation();
});复制代码
2. 在事件处理函数中返回 false
$("#div1").mousedown(function(event){
var e=e||window.event;
return false;
});复制代码
但是这两种方式是有区别的。return false 不仅阻止了事件往上冒泡,而且阻止了事件本身(默认事件)。event.stopPropagation()则只阻止事件往上冒泡,不阻止事件本身。\
3. event.target==event.currentTarget,让触发事件的元素等于绑定事件的元素,也可以阻止事件冒泡;
\
阻止默认事件
(1)event.preventDefault( )
(2)return false
对DOM元素进行增删改查
增
document.createElement()
复制代码
删
element.removeAttribute()
element.removeChild()
复制代码
改
element.innerHTML()
element.setAttribute()
复制代码
查
getElementById()
getElementsByClassName()
getElementsByTagName()
querySelector()
querySelectorAll()
复制代码
ajax、axios、fetch区别
ajax
- 基于原生的XHR开发
- 本身针对MVC编程,不符合现在前端MVVM潮流
axios
- 从浏览器中创建
XMLHttpRequest - 支持
promise - 支持请求拦击和响应拦截
- 从node.js创建http请求
- 客服端支持防止
CSRF/XSRF
fetch
- 浏览器原生实现的请求方式,ajax的替代品
- 只对网络请求报错,对400、500都当做成功的请求,需要封装
- fetch默认不会带
cookie,需要添加配置项 - fetch不支持abort,不支持超时控制,使用
setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费 - fetch没有办法原生监测请求的进度,而XHR可以
localStorage和sessionStorage的区别
localStorage和sessionStorage一样都是用来存储客户端临时信息的对象。
sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅、是会话级别的存储。只允许同一窗口访问。
而localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。同源可以读取并修改localStorage数据。
ES6
var、let、const区别
var声明变量可以重复声明,而let不可以var是不受限于块级作用域,而let受限var存在变量提升,let和const不存在变量提升const声明的变量不可变const声明之后必须赋值,否则会报错
箭头函数和普通函数的区别
- 箭头函数是匿名函数,不能作为构造函数,不能使用
new - 箭头函数不绑定
arguments - 箭头函数没有自己的
this,将所在的上下文的this作为自己的this值 - 没有
prototype call()、applay()、bind()方法不能改变箭头函数中的this指向
forEach和map的区别
-
forEach返回值是undefined,不可以链式调用
-
map()返回一个新的数组,不改变原数组。forEach改变原数组。
-
forEach()可以做到的东西,map()也同样可以。反过来也是如此。
-
map()会分配内存空间存储新数组并返回,forEach()不会返回数据。
-
forEach()允许callback更改原始数组的元素。map()返回新的数组。
变量的解构赋值
1.数组的解构
const F4 = ['小沈阳','刘能','赵四’,‘宋小宝’]
let[xiao,liu,zhao,song] = F4
2.对象的解构
const zhao = {
name:'赵本山',
age:'不详',
xiaopin:function(){
console.log("我可以演小品");
}
};
//let{name,age,xiaopin} = zhao;
//console.log(name);
//console.log(age);
//需要同名
let{xiaopin} = zhao;
console.log(xiaopin);
xiaopin()
模板字符串
-
声明
let str = `我也是一个字符串哦` -
内容中可以直接出现换行符
var str = ` <ul>
<li>沈腾</li>
<li>玛丽</li>
<li>魏翔</li>
<li>艾伦</li>
</ul>
`
3.变量拼接
let loverst = '魏翔’
let out = ` &{lovest}是我心目中最搞笑的演员`
简化对象写法
// ES6允许在大括号里面,里面直接写入变量和函数,作为对象的属性和方法。这样的书写更加简介
let name = '尚硅谷'
let change = function(){
console.log('我们可以改变你!!');
const school = {
name,
change,
//原先函数赋值方法
improve: function()
{
console.log('我们可以提高你的技能')
}
//es6新增
improve(){
console.log("我们可以提高你的技能")
}
箭头函数
- ES6允许使用箭头定义函数
//声明一个函数
let fn = function(){
}
//使用箭头函数声明
let fn = ( a,b)=>{
return a +b;}
fn(1,2)
- this 是静态的。this始终指向函数声明时所在作用域下的this的值。
// 普通函数的this指向window
function getName() {
console.log(this.name);
}
// 全局作用域下,声明的箭头函数的this指向window
let getName2 = () => {
console.log(this.name);
}
window.name = '尚硅谷';
const school = {
name: "ATGUIGU"
}
getName();//输出尚硅谷
getName2();//输出尚硅谷
getName.call(school)//输出ATGUIGU
getName2.call(school)//输出尚硅谷
- 不能作为构造实例化对象
let person = (name, age) => {
this.name = name;
this.age = age;
}
let me = new Person('xiao', 30)
console.log(me);
//则会报错 Person is not defined
4.不能使用arguments变量
let fn = (a, b, c) => {
console.log(arguments);
}
fn(1, 2, 3)
//会出现 arguments is not defined 错误
- 箭头函数的简写
//1)省略小括号,当形参有且只有一个的时候
let add = (n) =>{
return n+n;
}
console.log(add(9));
//省略小括号
let add = n =>{
return n+n;
}
console.log(add(9));
//省略花括号
let add = n =>{
return n+n;
}
console.log(add(9));
// 省略花括号,当代码体只有一条语句的时候,此时return 必须省略,而且语句的执行结果就是函数的返回值
//省略小括号
let add = n => {
return n + n;
}
//省略花括号
let pow = (n) => n * n;
console.log(pow(8))
//都省略
let pow = n => n * n;
console.log(pow(8))
5.箭头函数的应用场景
一、定时器
let ad = document.getElementById('ad');
ad.addEventListener("click", function() {
setTimeout(() => {
this.style.background = 'pink';
})
})
二、数组的方法回调
const arr = [1, 6, 9, 100, 25];
const result = arr.filter(item => item % 2 === 0)
console.log(result)
箭头函数适合与this无关的回调:定时器,数组的方法回调 箭头函数不适合与this有关的回调:事件回调,对象的方法
{
name:'',
getName:()=>{
this.name;
}
}
参数默认值
//ES6 允许给函数参数赋值初始值
//1. 形参初始值 具有默认值的参数,一般位置要靠后(潜规则)
function add(a, c = 10, b) {
return a + b + c;
}
let result = add(1, 2);
console.log(result);
//与解构赋值结合
function connect({
host = "127.0.0.1",
username,
password,
port
}) {
console.log(host)
console.log(username)
console.log(password)
console.log(port)
}
connect({
host: 'a',
username: 'root',
password: 'root',
port: 3306
})
rest (剩余参数)
作用与 arguments 类似
function add(...args)
{
console.log(args);
}
add(1,2,3,4,5);
/** * rest 参数必须是最后一个形参 */
function minus(a,b,...args)
{
console.log(a,b,args);
}
minus(100,1,2,3,4,5,19);
rest 参数非常适合不定个数参数函数的场景
spread 扩展运算符
扩展运算符(spread)也是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包
let tfboys = ['德玛西亚之力','德玛西亚之翼','德玛西亚皇子'];
function fn()
{
console.log(arguments);
}
fn(...tfboys)
扩运算符的应用
1.数组的合并
const kuaizi = ['苹果','橘子']
const sucai =['萝卜','地瓜']
//第一种方式
//const hebing = kuaizi.concat(sucai)
//第二种方式
const hebing = [...kuaizi,...sucai]
console.log(hebing) //输出['苹果','橘子','萝卜','地瓜']
2.数组的克隆
const sanzhihua = ['E','G','M']
const sanyecao = [...sanzhihua]
console.log(sanyecao) //输出 'E','G','M'
3.将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
console.log(divs)// object
const divArr = [...divs];
symbol
迭代器
概述
一、是为各种数据结构,提供一个统一的、简便的访问接口;
二、是使得数据结构的成员能够按某种次序排列;
三、是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
特点
ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费;原生具备 iterator 接口的数据(可用 for of 遍历):
Array;
Arguments;
Set;
Map;
String;
TypedArray;
NodeLis
原理
-
创建一个指针对象,指向当前数据结构的起始位置;
-
第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员;
-
接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员;
-
每调用 next 方法返回一个包含 value 和 done 属性的对象
** 需要自定义遍历数据的时候,要想到迭代器;**
// 声明一个数组
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧'];
// 使用 for...of 遍历数组
for(let v of xiyou){
console.log(v);
}
et iterator = xiyou[Symbol.iterator]();
// 调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
// 重新初始化对象,指针也会重新回到最前面
let iterator1 = xiyou[Symbol.iterator]();
console.log(iterator1.next());
同步编程和异步编程
同步编程
简单描述: 一个线程获得了一个任务,然后去执行这个任务, 当这个任务执行完毕后,才能执行接下来的另外一个任务。
注意: 这个线程不能将当前的任务放置在一边,转而去做另外一个任务。
一个线程,相当于一个人,这个人做完一个任务,再去做下一个任务。
这个人有两个任务,第一个是吃水果,第二个是写文章。
那么这个人是先去吃一堆水果,然后再去写文章。
异步编程
它和同步的编程模型有很大的区别:
一个线程中执行一堆任务,
这个线程可以自由的保存,恢复任务的状态。
也就是,它有能力穿插的执行任务。
一个线程,相当于一个人,这个人交替的做任务。
这个人有两个任务,第一个是吃水果,第二个是写文章。
那么这个人是先去吃两个水果,然后再去写文章,过一会再去吃水果,然后再来写文章。 原文链接: 同步编程 vs 异步编程 - 简书 (jianshu.com)
异步解决方案
使用场景: ajax请求, 事件回调, nodeAPI, 定时器
异步编程解决方案有: 生成器,promise, async 和 await
关于事件循环机制的解释: (19条消息) js 事件循环执行顺序(setTimeout,async,promise多层嵌套)_ZFZ5720-CSDN博客
生成器
其中生成器是一个特殊的函数,是异步编程的一种解决方案。
function getUsers() {
setTimeout(() => {
let data = "用户数据";
iterator.next(data);
}, 5000)
}
function getOrders() {
setTimeout(() => {
let data = "订单数据";
iterator.next(data); // 这里将data传入
}, 6000);
}
function getGoods() {
setTimeout(() => {
let data = "商品数据";
iterator.next(data); // 这里将data传入
}, 1000);
}
function* gen() {
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
let iterator = gen();
iterator.next();
//最后输出用户数据,订单数据,商品数据,其中iterator.next()第二次传入的值,被第一次yield接受
promise
Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果
Promise封装读取文件:
一般写法:
const fs = require("fs");
// 2、调用方法,读取文件
fs.readFile("resources/text.txt",(err,data)=>{
// 如果失败则抛出错误
if(err) throw err;
// 如果没有出错,则输出内容
console.log(data.toString());
});
Promise封装
// 1、引入 fs 模块
const fs = require("fs");
// 2、使用Promise封装
const p = new Promise(function(resolve,data){
fs.readFile("resources/text.txt",(err,data)=>{
// 判断如果失败
if(err) reject(err);
// 如果成功
resolve(data);
});
});
p.then(function(value){
console.log(value.toString());
},function(reason){
console.log(reason); // 读取失败
})
Promise封装Ajax请求:
原生请求:
// 请求地址:https://api.apiopen.top/getJoke
// 原生请求
// 1、创建对象
const xhr = new XMLHttpRequest();
// 2、初始化\
xhr.open("GET","https://api.apiopen.top/getJoke");
// 3、发送
xhr.send();
// 4、绑定事件,处理响应结果
xhr.onreadystatechange = function(){
// 判断状态
if(xhr.readyState == 4){
// 判断响应状态码 200-299
if(xhr.status>=200 && xhr.status<=299){
// 成功
console.log(xhr.response);
}else{
// 失败
console.error(xhr.status);
}
}
}
Promise封装Ajax请求:
const p = new Promise(function(resolve,reason){
// 原生请求
// 1、创建对象
const xhr = new XMLHttpRequest();
// 2、初始化
xhr.open("GET","https://api.apiopen.top/getJoke");
// 3、发送
xhr.send();
// 4、绑定事件,处理响应结果
xhr.onreadystatechange = function(){
// 判断状态
if(xhr.readyState == 4){
// 判断响应状态码 200-299
if(xhr.status>=200 && xhr.status<=299){
// 成功
resolve(xhr.response);
}else{
// 失败
reason(xhr.status);
}
}
}
});
p.then(function(value){
console.log(value.toString());
},function(reason){
console.log(reason); // 读取失败
})
Promise.prototype.then:
调用then方法,then 方法返回的是promise对象,对象状态由回调函数的执行结果决定。
1.如果回调函数中返回的结果是非promise类型的属性,状态为成功,返回值为对象的成功的值。
// 创建 Promise 对象
const p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("用户数据");
},1000);
});
// 调用then方法,then方法的返回结果是promise对象,
// 对象的状态有回调函数的结果决定;
const result = p.then(value => {
consolr.log(value)
},reason => {
console.error(reason);
})
// 链式调用
// then里面两个函数参数,可以只写一个
p.then(value=>{},reason=>{}).then(value=>{},reason=>{});
console.log(result);
promise 封装多个文件读取
“回调地狱”方式写法
// 1、引入 fs 模块
const fs = require("fs");
// 2、调用方法,读取文件
fs.readFile("resources/text.txt",(err,data1)=>{
fs.readFile("resources/test1.txt",(err,data2)=>{
fs.readFile("resources/test2.txt",(err,data3)=>{
let result = data1 + data2 + data3;
console.log(result);
});
});
});
Promise实现
// 1、引入 fs 模块
const fs = require("fs");
// 3、使用Promise实现
const p = new Promise((resolve,reject)=>{
fs.readFile("resources/text.txt",(err,data)=>{
resolve(data);
运行结果:
Promise对象catch方法:
代码示例及相关说明:
});
});
p.then(value=>{
return new Promise((resolve,reject)=>{
fs.readFile("resources/test1.txt",(err,data)=>{
resolve([value,data]);
});
})
}).then(value=>{
return new Promise((resolve,reject)=>{
fs.readFile("resources/test2.txt",(err,data)=>{
// 存入数组
value.push(data);
resolve(value);
});
})
}).then(value=>{
console.log(value.join("\r\n"));
})
Promise对象catch方法:
// Promise对象catch方法
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
// 设置p对象的状态为失败,并设置失败的值
reject("失败啦~!");
},1000);
})
// p.then(value=>{
// console.log(value);
// },reason=>{
// console.warn(reason);
// });
p.catch(reason=>{
console.warn(reason);
});
async 和await
概述
async 和 await 两种语法结合可以让异步代码看起来像同步代码一样;简化异步函数的写法
async 函数
概述
- async 函数的返回值为 promise 对象;
代码实现:
//返回普通数据,async依然返回的是promise对象
async function fn() {
return 123;
}
console.log(fn())
实现结果:
如果抛出一个错误,那么返回的primise对象的状态就是失败,promise对象的值就是失败的值
async function fn() {
throw new Error("出错了!")
}
console.log(fn())
- promise 对象的结果由 async 函数执行的返回值决定;
//成功
async function fn() {
return new Promise((resolve, reject) => {
resolve("成功啦!")
})
}
console.log(fn())
//失败
async function fn() {
return new Promise((resolve, reject) => {
reject("失败啦!");
})
}
console.log(fn())
完整代码
async function fn() {
return new Promise((resolve, reject) => {
// reject("失败啦!");
resolve("我想成功")
})
}
console.log(fn())
result = fn()
result.then(value => {
console.log(value);
}, reson => {
console.warn(reson)
})
结果
await
概述
- await 必须写在 async 函数中;
- await 右侧的表达式一般为 promise 对象;
- await 返回的是 promise 成功的值;
- await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理;
代码实现
基本实现
const p = new Promise((resolve, reject) => {
resolve("成功啦!");
})
async function fn() {
let result = await p;
console.log(result)
}
fn()
async await实现多文件读取
const fs = new require("fs");
function readtext1() {
return new Promise((reslove, reject) => {
fs.readFile("text1.txt", (err, data) => {
if (err) reject(err);
reslove(data);
})
})
}
function readtext2() {
return new Promise((reslove, reject) => {
fs.readFile("text2.txt", (err, data) => {
if (err) reject(err);
reslove(data);
})
})
}
function readtext3() {
return new Promise((reslove, reject) => {
fs.readFile("text3.txt", (err, data) => {
if (err) reject(err);
reslove(data);
})
})
}
async function main() {
let result1 = await readtext1();
let result2 = await readtext2();
let result3 = await readtext3();
console.log(result1.toString());
console.log(result2.toString());
console.log(result3.toString());
}
main()
async await实现ajax封装
function sendAjax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status <= 299) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
})
}
//传统写法
// const result = sendAjax("https://api.apiopen.top/getJoke");
// result.then(value => {
// console.log(value)
// }, reason => {
// console.warn(reason)
// })
//async,await 写法
async function main() {
let result = await sendAjax("https://api.apiopen.top/getJoke")
console.log(result)
}
main()
node.js的单线程如何实现高并发
1、每个Node.js进程只有一个主线程在执行程序代码,形成一个执行栈(execution context stack)。
2、主线程之外,还维护了一个"事件队列"(Event queue)。当用户的网络请求或者其它的异步操作到来时,node都会把它放到Event Queue之中,此时并不会立即执行它,代码也不会被阻塞,继续往下走,直到主线程代码执行完毕。
3、主线程代码执行完毕完成后,然后通过Event Loop,也就是事件循环机制,开始到Event Queue的开头取出第一个事件,从线程池中分配一个线程去执行这个事件,接下来继续取出第二个事件,再从线程池中分配一个线程去执行,然后第三个,第四个。主线程不断的检查事件队列中是否有未执行的事件,直到事件队列中所有事件都执行完了,此后每当有新的事件加入到事件队列中,都会通知主线程按顺序取出交EventLoop处理。当有事件执行完毕后,会通知主线程,主线程执行回调,线程归还给线程池。
4、主线程不断重复上面的第三步。
async和promise的区别
async/await是基于promise实现的,他不能用于普通的回调函数
async/await使得异步代码看起来像同步代码
async/await与Promise一样,是非阻塞的。
Javascript异步编程的4种方法
Javascript异步编程的4种方法 - 阮一峰的网络日志 (ruanyifeng.com)
Set、Map的区别
Set
- 创建:
new Set([1, 1, 2, 3, 3, 4, 2]) add(value):添加某个值,返回Set结构本身。delete(value):删除某个值,返回一个布尔值,表示删除是否成功。has(value):返回一个布尔值,表示该值是否为Set的成员。clear():清除所有成员,没有返回值。
Map
set(key, val):向Map中添加新元素get(key):通过键值查找特定的数值并返回has(key):判断Map对象中是否有Key所对应的值,有返回true,否则返回falsedelete(key):通过键值从Map中移除对应的数据clear():将这个Map中的所有元素删除
区别
- Map是一种键值对的集合,和对象不同的是,键可以是任意值
- Map可以遍历,可以和各种数据格式转换
- Set是类似数组的一种的数据结构,但在Set中没有重复的值
class
概述
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已;
代码实现
// 手机 ES5写法
// function Phone(brand,price){
// this.brand = brand;
// this.price = price;
// }
// // 添加方法
// Phone.prototype.call = function(){
// console.log("我可以打电话!");
// }
// // 实例化对象
运行结果:
class静态成员:
代码实现:
// let HuaWei = new Phone("华为",5999);
// HuaWei.call();
// console.log(HuaWei);
// ES6写法
class Phone{
// 构造方法,名字是固定的
constructor(brand,price) {
this.brand = brand;
this.price = price;
}
//打电话,方法必须使用该方式写
call(){
console.log("我可以打电话!");
}
}
let HuaWei = new Phone("华为",5999);
HuaWei.call();
console.log(HuaWei);
class静态成员
// class静态成员
// ES5写法
// function Phone(){}
// Phone.name = "手机";
运行结果:
ES5构造函数实现继承:
代码实现:
// Phone.change = function(){
// console.log("我可以改变世界!");
// }
// let nokia = new Phone();
// console.log(nokia.name); // undefined
// // nokia.change();
// // 报错:Uncaught TypeError: nokia.change is not a function
// Phone.prototype.color = "黑色";
// console.log(nokia.color); // 黑色
// console.log(Phone.name);
// Phone.change();
// 注意:实例对象和函数对象的属性是不相通的
// ES6写法
class Phone{
// 静态属性
static name = "手机";
static change(){
console.log("我可以改变世界!");
}
}
let nokia = new Phone();
console.log(nokia.name);
console.log(Phone.name);
Phone.change();
运行结果 undefined 手机 我可以改变这个世界
es5构造函数实现继承
// ES5构造函数继承
// 手机
function Phone(brand,price){
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function(){
console.log("我可以打电话!");
}
/ 智能手机
function SmartPhone(brand,price,color,size){
Phone.call(this,brand,price);
this.color = color;
this.size = size;
}
/ 设置子级构造函数的原型
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone;
// 声明子类的方法
SmartPhone.prototype.photo = function(){
console.log("我可以拍照!");
}
SmartPhone.prototype.game = function(){
console.log("我可以玩游戏!");
}
const chuizi = new SmartPhone("锤子",2499,"黑色","5.5inch");
console.log(chuizi);
chuizi.call();
chuizi.photo();
chuizi.game();
运行结果
我可以打电话
我可以拍照
我可以玩游戏
ES6class类继承:
// ES6class类继承
class Phone{
constructor(brand,price) {
this.brand = brand;
this.price = price;
}
call(){
console.log("我可以打电话!");
}
}
class SmartPhone extends Phone{
// 构造函数
constructor(brand,price,color,size) {
super(brand,price); // 调用父类构造函数
this.color = color;
this.size = size;
}
photo(){
console.log("我可以拍照!");
}
game(){
console.log("我可以玩游戏!");
}
}
const chuizi = new SmartPhone("小米",1999,"黑色","5.15inch");
console.log(chuizi);
chuizi.call();
chuizi.photo();
chuizi.game();
运行结果
我可以打电话
我可以 拍照
我可以玩游戏
class Phone{
constructor(brand,price) {
this.brand = brand;
this.price = price;
}
call(){
console.log("我可以打电话!");
}
}
class SmartPhone extends Phone{
// 构造函数
constructor(brand,price,color,size) {
super(brand,price); // 调用父类构造函数
this.color = color;
this.size = size;
}
// 子类对父类方法重写
// 直接写,直接覆盖
// 注意:子类无法调用父类同名方法
call(){
console.log("我可以进行视频通话!");
}
photo(){
console.log("我可以拍照!");
}
game(){
console.log("我可以玩游戏!");
}
}
const chuizi = new SmartPhone("小米",1999,"黑色","5.15inch");
console.log(chuizi);
chuizi.call();
chuizi.photo();
chuizi.game();
class中的getter和setter设置
// class中的getter和setter设置
class Phone{
get price(){
console.log("价格属性被读取了!");
// 返回值
return 123;
}
set price(value){
console.log("价格属性被修改了!");
}
}
/ 实例化对象
let s = new Phone();
console.log(s.price); // 返回值
s.price = 2999;
Vue
基础知识
什么是vue
是一套用于构建用户界面的渐进式框架
jquery和vue的区别是什么
Vue.js是一个前端JavaScript框架,用于构建用户界面。它是开源的。当使用其他JavaScript库时,它还可以用作集成工具。它本质上是增量自适应的。Vue还用作Web应用程序框架,可以为单页面应用程序提供支持,Vue.js 专注于 MVVM 模型的 ViewModel 层。它通过双向数据绑定把 View 层和 Model 层连接了起来,通过对数据的操作就可以完成对页面视图的渲染。
jQuery语法旨在轻松导航文档,例如选择DOM元素,创建动画,处理事件和开发Ajax应用程序。它还可以用于在JavaScript库之上创建插件。
Vue 的双向数据绑定原理是什么?
vue.js 是采用数据劫持结合发布者-订阅者模式的方式, 通过 Object.defineProperty()来劫持各个属性的 setter, getter, 在数据变动时发布消息给订阅者, 触发相应的监听回调。
具体步骤:
第一步: 需要 observe 的数据对象进行递归遍历, 包括子属性对象的属性, 都加上 setter和 getter这样的话, 给这个对象的某个值赋值, 就会触发 setter, 那么就能监听到了数据变化
第二步: compile 解析模板指令, 将模板中的变量替换成数据, 然后初始化渲染页面视图,
并将每个指令对应的节点绑定更新函数, 添加监听数据的订阅者, 一旦数据有变动, 收到通知, 更新视图
第三步: Watcher 订阅者是 Observer 和Compile之间通信的桥梁, 主要做的事情是:
1、 在自身实例化时往属性订阅器(dep)里面添加自己
2、 自身必须有一个 update()方法
3、 待属性变动 dep.notice()通知时, 能调用自身的 update()方法, 并触发 Compile 中绑定的回调, 则功成身退。
第四步: MVVM 作为数据绑定的入口, 整合 Observer、Compile 和 Watcher 三者, 通过 Observer来监听自己的 model 数据变化, 通过 Compile 来解析编译模板指令, 最终利用 Watcher 搭起Observer和Compile之间的通信桥梁, 达到数据变化 -> 视图更新; 视图交互变化(input) -> 数据 model 变更的双向绑定效果。
vue的优点
低耦合。 视图(View) 可以独立于 Model 变化和修改, 一个 ViewModel 可以绑定
到不同的"View"上, 当 View 变化的时候 Model 可以不变, 当 Model 变化的时候
View 也可以不变。
可重用性。 你可以把一些视图逻辑放在一个 ViewModel 里面, 让很多 view 重用
这段视图逻辑。
独立开发。 开发人员可以专注于业务逻辑和数据的开发(ViewModel), 设计人员
可以专注于页面设计。
可测试。 界面素来是比较难于测试的, 而现在测试可以针对 ViewModel 来写。\
MVVM
MVVM是一种软件架构模式,在vue中 M 代表model层(数据模型),负责数据模型。V代表View层(视图层),VM代表ViewModel(视图模型),它是Model和View之间的桥梁,数据会绑定到viewModel层,并自动将数据渲染到页面层,视图变化时会通知viewModel更新数据
Vue生命周期
创建前后:
beforeCreated(创建前):数据观测和初始化事件还未开始,不能访问data、computed、watch、methods上的数据方法。created(创建后):实例创建完成,可以访问data、computed、watch、methods上的数据方法,但此时渲染节点还未挂在到DOM上,所以不能访问。
挂载前后:
beforeMounted(挂载前):Vue实例还未挂在到页面html上,此时可以发起服务器请求mounted(挂载后):Vue实例已经挂在完毕,可以操作DOM
更新前后:
beforeUpdate(更新前):数据更新之前调用,还未渲染页面updated(更新后):DOM重新渲染,此时数据和界面都是新的。
销毁前后:
beforeDestoryed(销毁前):实例销毁前调用,这时候能够获 取到thisdestoryed(销毁后):实例销毁后调用,实例完全被销毁。
注意:初次加载时所执行的钩子函数
beforCreate
created
beforeMount
mounted
watch和computed的区别
watch:监听属性,用来监听数据的变化,没有缓存,当监听到的数据发生变化时都会执行毁掉函数
computed:计算属性,被监听的值有缓存,只有它依赖的属性值发生变化后,下一次获取computed的值时才会重新计算computed的值。(只有依赖发生变化后才会重新计算)
watch : 侦听器就是侦听data中的数据变化,如果数据一旦发生变化就通知侦听器所绑定方法,来执行相应的操作。从这一点上,与计算属性是非常类似的.watch是允许异步操作的,并且在我们得到最终的结果前,可以设置中间状态,这些都是计算属性无法做到的
v-for中key的作用
key是为了更高效的对比虚拟DOM中的每个节点是否相同,避免页面更新时重复更新节点,它会唯一标识数组中的每一项,未来当数组中的那一项改变的时候,它会只更新那一项,好处就是提升性能。
v-if和v-show的区别
v-if元素不可见 删除dom元素
v-show元素可见 通过设置元素的display:none样式属性
-
v-show本质就是标签display设置为none,控制隐藏
- v-show只编译一次,后面其实就是控制css,而v-if不停的销毁和创建,故v-show性能更好一点。
-
v-if是动态的向DOM树内添加或者删除DOM元素
- v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件
组件中的data为什么是一个函数
因为对象是一个引用类型,如果data是一个对象的情况下,会造成多个组件共用一个data,data为一个函数,每个组件都会有自己的私有数据空间,不会干扰其他组件的运行。
Vue组件通信
父子组件
父传子
- props
- $children
- $refs
子传父
- $emit
- $parent
兄弟组件
- provied
- inject
- eventBus
- Vuex
keep-alive内置组件的作用
可以让当前组件或者路由不会经历创建和销毁,而是进行缓存,凡是被keep-alive组件包裹的组件,除了第一次以外。不会经历创建和销毁的。第一次创建后会缓存到缓存当中。
include - 字符串或正则表达式, 只有名称匹配的组件会被缓存
exclude - 字符串或正则表达式, 任何名称匹配的组件都不会被缓存
你能谈一下对Vue组件化的理解吗?
定义:组件是可复用的Vue实例,准确讲它是VueComponent的实例,继承自Vue
优点:组件化可以增加代码的复用性,可维护性和可测试性。
使用场景:什么时候使用组件?以下分类可以作为参数
第一:通用组件:实现最基本的功能,具有通用性,复用性。例如按钮组件,输入框组件,布局组件等。(Element UI组件库就是属于这种通用的组件)
第二:业务组件,用于完成具体的业务,具有一定的复用性。例如登录组件,轮播图组件。
第三:页面组件,组织应用各部分独立内容,需要时在不同页面组件间切换,例如:商品列表页,详情页组件。
Vuex的基本使用
Vuex用于vue中的数据状态管理,有五种属性:
state:Vuex的基本数据,用于存储变量getter:从state派生出来的数据,当相遇state的计算属性,在这里可以对state数据进行过滤、筛选等操作mutation:提交更新state数据的方法action:和mutation功能相似,都是用来提交更新,但action提交的是mutation,而不是直接变更数据,并且action可以包含异步操作module:模块化Vuex,每个模块都有自己的state、mutation、actoion、getter
mutation和action的区别
mutation更专注于修改state,必须是同步执行。action提交的是mutation,而不是直接更新数据,可以是异步的。action可以整合多个mutation
Vuex和localstory的区别
Vuex存储在内存中,页面关闭刷新就会消失。而localstorage存储在本地,读取内存比读取硬盘速度要快Vuex应用于组件之间的传值,localstorage主要用于不同页面之间的传递Vuex是响应式的,localstorage需要刷新
路由守卫
- 全局前置钩子:
beforeEach、beforeResolve、afterEach - 路由独享守卫:
beforeEnter - 组件内钩子:
beforeRouterEnter、beforeRouterUpdate、beforeRouterLeave
hash和history的区别
hash
hash模式是vue开发中的默认模式,地址栏URL携带#,#后为路由。
原理是通过onhashchange()事件监听路由hash的变化,这个好处就是当hash值发生变化,不需要向后端发起请求,window就可以监听事件的改变,并按照规则加载项对应的代码。除此之外,hash值的变化对应的URL都会被浏览器记录下来,这样就能实现浏览器历史页面的前进后退。
history
vue还提供history模式,在history模式下URL中没有#,相比hash模式更加好看。但是需要后台配置支持。
history的原理是利用HTML5中hostory提供的pushState、replaceState这两个API,这两个API记录了浏览器历史栈,并且当在修改URL时不会触发页面刷新和后台请求。
动态路由
定义方式
-
params传参
- 路由配置:
/index/:id - 路由跳转:
this.$router.push({name: 'index', params: {id: "zs"}}); - 路由参数获取:
this.params.id - 最后形成的路由:
/index/zs
- 路由配置:
-
query传参
- 路由配置:
/index正常的路由配置 - 路由跳转:
this.$rouetr.push({path: 'index', query:{id: "zs"}}); - 路由参数获取:
this.query.id - 最后形成的路由:
/index?id=zs
- 路由配置:
$router和$route的区别
$router是指整个路由对象,可以使用this.$router.push({name: ;index'})进行页面跳转$route时指当前页面的路由对象,可以使用this.$route.parmas.id来获取当前路由对象传递进来的参数
Vue常用的API
Vue.set
向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且会触发视图更新。
使用方法:Vue.set(target,propertyName,value)
Vue.delete
删除对象的属性,如果对象是响应式的,确保删除能触发更新视图。
聊聊你对 Vue.js 的 template 编译的理解?
简而言之, 就是先转化成 AST 树, 再得到的 render 函数返回 VNode(Vue 的虚拟 DOM 节点)
详情步骤:
首先, 通过 compile 编译器把 template 编译成 AST 语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式), compile 是 createCompiler 的返回值, createCompiler 是用以创建编译器的。 另外 compile 还负责合并 option。
然后, AST 会经过 generate(将 AST 语法树转化成 render funtion 字符串的过程) 得到 render函数, render 的返回值是 VNode, VNode 是 Vue 的虚拟 DOM 节点, 里面有(标签名、 子节点、 文本等等)
vue 等单页面应用(spa) 及其优缺点
优点: Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件, 核心
是一个响应的数据绑定系统。 MVVM、 数据驱动、 组件化、 轻量、 简洁、 高效、 快速、 模块
友好; 即第一次就将所有的东西都加载完成, 因此, 不会导致页面卡顿。
缺点: 不支持低版本的浏览器, 最低只支持到 IE9; 不利于 SEO 的优化(如果要支持 SEO,
建议通过服务端来进行渲染组件); 第一次加载首页耗时相对长一些; 不可以使用浏览器的
导航按钮需要自行实现前进、 后退。
自定义指令( v-check、 v-focus) 的方法有哪些? 它有哪些钩子函数? 还有哪些钩子函数参数?
全局定义指令: 在 vue 对象的 directive 方法里面有两个参数, 一个是指令名称, 另外一个是
函数。 组件内定义指令: directives
钩子函数: bind( 绑定事件触发)、 inserted(节点插入的时候触发)、 update( 组件内相关更
新)
钩子函数参数: el、 binding。
简述一下 Sass、 Less, 且说明区别?
他们是动态的样式语言, 是 CSS 预处理器,CSS 上的一种抽象层。 他们是一种特殊的语法语言而编译成 CSS。变量符不一样, less 是@, 而 Sass 是$;Sass 支持条件语句, 可以使用 if{}else{},for{}循环等等。 而 Less 不支持;Sass 是基于 Ruby 的, 是在服务端处理的,而 Less 是需要引入 less.js 来处理 Less 代码输出Css 到浏览器。
vue3.0升级内容
-
全部使用ts重写(响应式、vdom、模版编译等)
-
性能提升,代码量少
-
会调整api
-
proxy实现响应式
-
基本使用
-
reflect
- 和proxy的作用一一对应
- 规范化、标准化、函数化
- 代替object工具函数
-
实现响应
-
虚拟DOM
VNode 是什么? 虚拟 DOM 是什么?
包括几个步骤:
1、用 JavaScript 对象结构表示 DOM 树的结构, 然后用这个树构建一个真正的 DOM 树,插到文档当中;
2、当状态变更的时候, 重新构造一棵新的对象树, 然后用新的树和旧的树进行比较, 记录两棵树差异;
3、把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上, 视图就更新了。\
Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。 可以类比 CPU 和硬盘, 既然硬盘这么慢, 我们就在它们之间加个缓存: 既然 DOM 这么慢, 我们就在它们 JS 和 DOM 之间加个缓存。 CPU(JS) 只操作内存(Virtual DOM), 最后的时候再把变更写入硬盘(DOM)。
Vue 在 页面上渲染的节点, 及其子节点称为“虚拟节点 (Virtual Node)”, 简写为“VNode”。“虚拟 DOM” 是由 Vue 组件树建立起来的整个 VNode 树的称呼。
说下对 Virtual DOM 算法的理解
浏览器
0. 浏览器的渲染过程:
浏览器将获取的HTML文档解析成DOM树。 处理CSS标记,构成层叠样式表模型CSSOM(CSS Object Model)。 将DOM和CSSOM合并为渲染树(rendering tree),代表一系列将被渲染的对象。 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次绘制操作就可以布局所有的元素。 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting。
1. 从输入 url 到展示的过程
浏览器根据请求的 URL 交给 DNS 域名解析,找到真实 IP,向服务器发起请求;
服务器交给后台处理完成后返回数据, 浏览器接收文件(HTML、JS、CSS、图象浏览器对加载到的资源(HTML、JS、CSS 等)进行语法解析, 建立相应的内部数据结构(如HTML 的 DOM Tree);
载入解析到的资源文件,渲染页面,完成。
具体:
1.DNS 解析:将域名解析成 IP 地址
2.TCP 连接: TCP 三次握手
3.发送 HTTP 请求
4.服务器处理请求并返回 HTTP 报文
5.浏览器解析渲染页面(根据 HTML 解析出 DOM 树,根据 CSS 解析生成 CSS 规则树,结合 DOM 树和 CSS 规则树,生成渲染树,根据渲染树计算每一个节点的信息)
6.断开连接:TCP 四次挥手
2.重绘与回流
当元素的样式发生变化时,浏览器需要触发更新,重新绘制元素。这个过程中,有两种类型
的操作,即重绘与回流。
重绘(repaint): 当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时
由于只需要 UI 层面的重新像素绘制,因此损耗较少
回流(reflow): 当元素的尺寸、结构或触发某些属性时,浏览器会重新渲染页面,称为回流。
此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。
会触发回流的操作:
o 页面初次渲染
o 浏览器窗口大小改变
o 元素尺寸、 位置、 内容发生改变
o 元素字体大小变化
o 添加或者删除可见的 dom 元素
o 激活 CSS 伪类(例如: :hover)
o 查询某些属性或调用某些方法
clientWidth、 clientHeight、 clientTop、 clientLeft
offsetWidth、 offsetHeight、 offsetTop、 offsetLeft
scrollWidth、 scrollHeight、 scrollTop、 scrollLeft
getComputedStyle()
getBoundingClientRect()
scrollTo()
回流必定触发重绘,重绘不一定触发回流。重绘的开销较小,回流的代价较高。
4.如何减少重绘和回流
通过 classname 或 cssText 一次性修改样式, 而非一个一个改
离线模式: 克隆要操作的结点, 操作后再与原始结点交换, 类似于虚拟 DOM
避免频繁直接访问计算后的样式, 而是先将信息保存下来
绝对布局的 DOM, 不会造成大量 reflow
p 不要嵌套太深, 不要超过六层。
5. 内存泄露
意外的全局变量: 无法被回收
定时器: 未被正确关闭, 导致所引用的外部变量无法被释放
事件监听: 没有正确销毁 (低版本浏览器可能出现)
闭包: 会导致父级中的变量无法被释放
dom 引用: dom 元素被删除时, 内存中的引用未被正确清空
可用 chrome 中的 timeline 进行内存标记, 可视化查看内存的变化情况, 找出异常点。
6.localStorage 与 sessionStorage 与 cookie 的区别总结?
共同
点: 都保存在浏览器端, 且同源localStorage 与 sessionStorage 统称 webStorage,保存在浏览器,不参与服务器通信,大小为5M.
生命周期不同: localStorage 永久保存, sessionStorage 当前会话, 都可手动清除
作用域不同: 不同浏览器不共享 local 和 session, 不同会话不共享 session
Cookie: 设置的过期时间前一直有效, 大小 4K.有个数限制, 各浏览器不同, 一般为 20 个.携带在 HTTP 头中, 过多会有性能问题.可自己封装, 也可用原生.
7.浏览器如何阻止事件传播, 阻止默认行为?
阻止事件传播(冒泡): e.stopPropagation() 阻止默认行为: e.preventDefault()
8.浏览器事件机制中事件触发三个阶段
事件捕获阶段: 从 dom 树节点往下找到目标节点, 不会触发函数
事件目标处理函数: 到达目标节点
事件冒泡: 最后从目标节点往顶层元素传递, 通常函数在此阶段执行.
addEventListener 第三个参数默认 false(冒泡阶段执行),true(捕获阶段执行)
9.什么是跨域? 为什么浏览器要使用同源策略? 你有几种方式可以解决跨域问题? 了解预检请求嘛?
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源防止 XSS、 CSFR 等攻击, 协议+域名+端口不同jsonp; 跨域资源共享(CORS) (Access control); 服务器正向代理等。
预检请求: 需预检的请求要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
10.为什么操作 DOM 慢?
DOM 本身是一个 js 对象, 操作这个对象本身不慢, 但是操作后触发了浏览器的行为, 如repaint 和 reflow 等浏览器行为, 使其变慢。
11.什么情况会阻塞渲染?
js 脚本同步执行
css 和图片虽然是异步加载, 但 js 文件执行需依赖 css, 所以 css 也会阻塞渲染
12.浏览器内核
浏览器内核可以分成两部分: 渲染引擎(layout engineer 或者 Rendering Engine)和 JS 引擎。
浏览器的内核的不同对于网页的语法解释会有不同, 所以渲染的效果也不相同。 所有网页浏览器、 电子邮件客户端以及其它需要编辑、 显示网络内容的应用程序都需要内核
渲染引擎
渲染引擎负责取得网页的内容( HTML、 XML、 图像等等)、 整理讯息( 例如加入 CSS 等),以及计算网页的显示方式, 然后会输出至显示器或打印机。
JS 引擎
JS 引擎则是解析 Javascript 语言, 执行 javascript 语言来实现网页的动态效果。
最开始渲染引擎和 JS 引擎并没有区分的很明确, 后来 JS 引擎越来越独立, 内核就倾向于只指渲染引擎。
13. 常见的 DOCTYPE 声明有几种?
HTML中常见声明类型共有8种,分别是html5有1种,HTML 4.01和XHTML 1.0都有3种,XHTML 1.1有1种 1.HTML5 声明——》
2.HTML 4.01和XHTML 1.0
strict包含所有 HTML 元素和属性,但不包括展示性的和弃用的元素(比如 font)。不允许框架集(Framesets)。
transitional该 DTD 包含所有 HTML 元素和属性,包括展示性的和弃用的元素(比如 font)。不允许框架集(Framesets)。
framesets该 DTD 等同于 HTML 4.01 Transitional,但允许框架集内容。
3.XHTML 1.1规定了一种声明, 等同于 XHTML 1.0 Strict,但允许添加模型。
14. 请描述一下 cookie、 sessionStorage、 localStorage的区别?
存储大小: cookie 数据不能超过 4k, sessionStorage 和 localStorage, 可以达到 5M 或更大。
数据有效期: sessionStorage 关闭标签就去清除; localStorage 需要手动清除;
cookie 在设置的 cookie 过期时间之前一直有效请求数据, cookie 可以作为请求参数去请求服务器接口, 即 cookie 在浏览器和服务器间来回传递。
作用范围: cookie 只属于某个路径下, 需要设置路径, 同源窗口共享,
sessionStorage 不在不同的浏览器窗口中共享, localStorage 在所有同源窗口中都是共享的
15、你能描述一下渐进增强和优雅降级之间的不同吗
渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
优雅降级:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
16、渲染优化
1、使用CSS3代码代替JS动画(尽可能避免重绘重排以及回流)
2、页面中空的href和 src会阻塞页面其他资源的加载 (阻塞下载进程)
3、用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能
4、当需要设置的样式很多时设置className而不是直接操作style
5、少用全局变量、缓存DOM节点查找的结果。减少IO读取操作
6、图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳
17、 如何进行网站性能优化
1、减少HTTP请求:合并文件、CSS精灵、inline Image
2、将样式表放到页面顶部
3、不使用CSS表达式
4、使用不使用@import
5、将脚本放到页面底部
6、将javascript和css从外部引入
7、压缩javascript和css
网络部分
TCP、UDP协议
TCP和UDP都是在传输层定义的两种传输协议。基于UDP协议传输不能保证数据准确无误的送达,但UDP不仅可以支持一对一的传输方式,还可以支持一对一、一对多等形式。也不需要像TCP一样建立连接,所以传输速度快。
TCP的目的是提供可靠的数据,并且需要在传输前建立连接(三次握手)。只支持一对一进行传输。
区别:
TCP协议可靠,UDP协议不可靠TCP面向连接,UDP采用无连接TCP可以保证数据顺序,UDP不能TCP一对一传输,UDP可以一对多、多对一等形式
HTTP和HTTPS区别
HTTP是明文传输,不安全。HTTPS基于SSL进行加密传输,比较安全。HTTPS需要CA证书,HTTP不需要。HTTP端口为80,HTTPS端口为443
HTTP状态码
-
1XX: 请求正在处理
-
2XX:正常状态码
- 200 :请求处理成功
- 201 : 请求成功并且服务器创建了新资源
- 202 :服务器已经接收请求,但尚未处理
-
3XXX:重定向状态
- 301 :请求重定向
- 302: 临时重定向
- 303: 临时重定向,使用get请求新的url
- 304:浏览器缓存相关
-
4XX:错误状态码
- 400: 服务器无法理解请求格式,需要修改请求内容后再次发起请求
- 401: 请求未授权
- 403: 禁止访问
- 404: 服务器上无法找到请求资源
-
5XX:服务器错误
- 500: 服务端错误
- 503: 服务器暂时无法处理请求
HTTP三次握手、四次挥手
三次握手是在建立TCP连接时,客户端和服务端总共发送三个包。进行三次握手的主要目的就是为了确认双方的接受能力和发送能力都是正常的,为后面传输可靠数据做准备。
报文:
- 序号:表示发送的数据字节流,确保TCP传输有序,对每个字节编号
- 确认序号:发送方期待接收的下一序列号,接收成功后的数据字节序列号加 1。只有
ACK=1时才有效。 ACK:确认序号的标志,ACK=1表示确认号有效,ACK=0表示报文不含确认序号信息SYN:连接请求序号标志,用于建立连接,SYN=1表示请求连接FIN:结束标志,用于释放连接,FIN=1表示关闭本方数据流
三次握手
- 第一次握手:客户端给服务端发一个
SYN报文,并指明客户端的初始化序列号ISN,此时客户端处于SYN_SEND状态。 - 务器收到客户端的
SYN报文之后,会以自己的SYN报文作为应答,并且也是指定了自己的初始化序列号ISN。同时会把客户端的ISN + 1作为ACK的值,表示自己已经收到了客户端的SYN,此时服务器处于SYN_REVD的状态。 - 客户端收到
SYN报文之后,会发送一个ACK报文,当然,也是一样把服务器的ISN + 1作为ACK的值,表示已经收到了服务端的SYN报文,此时客户端处于ESTABLISHED状态。服务器收到ACK报文之后,也处于ESTABLISHED 状态,此时,双方已建立起了连接。
为什么需要三次握手?
为了防止失效的连接请求又传送到主机, 因而产生错误。如果使用的是两次握手建立连接, 假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于 TCP 的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。如果采用的是三次握手, 就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。
四次挥手
- 客户端会发送一个
FIN报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态。 - 服务端收到
FIN之后,会发送ACK报文,且把客户端的序列号值 +1 作为ACK报文的序列号值,表明已经收到客户端的报文了,此时服务端处于CLOSE_WAIT状态。 - 如果服务端也想断开连接了,和客户端的第一次挥手一样,发给
FIN报文,且指定一个序列号。此时服务端处于LAST_ACK的状态。 - 客户端收到
FIN之后,一样发送一个ACK报文作为应答,且把服务端的序列号值 +1 作为自己ACK报文的序列号值,此时客户端处于TIME_WAIT状态。需要过一阵子以确保服务端收到自己的ACK报文之后才会进入CLOSED状态,服务端收到ACK报文之后,就处于关闭连接了,处于CLOSED状态。
为什么需要四次挥手
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
HTTP缓存
强缓存
使用强制缓存策略,如果缓存资源有效,就直接使用缓存资源,不需要向服务器发送请求。强制缓存通过两种方式来设置,在request headers中的Expires属性和Cache-Contorl属性。
Expires属性,指定资源的过期时间。在过期时间以内,改资源可以被缓存使用,不需要向浏览器发送请求。这个时间依赖于服务器时间,会存在服务器时间和客户端时间不一致。
Cache-Control属性:
private: 仅浏览器可以缓存public:浏览器和代理服务器都可以缓存max-age=xxx过期时间,单位为秒no-cache不进行强缓存,但会有协商缓存no-store不强缓存,也不协商缓存
如果request header中,Cache- Control的值中有max-age=xxx,这个时候走强缓存。如果值为no-cache,表明没有命中,走协商缓存。如值为no-store,不使用缓存。
协商缓存
如果没有命中强制缓存,在设置协商缓存情况下,先向服务器发送一个请求,如果资源没有发生修改,则返回一个304的状态,让浏览器使用本地的缓存副本。如果资源发生修改,则返回修改后的资源。在request headers中的Etag属性和Last-Modified属性,来进行设置。其中,ETage优先于Last-Modified。
命中协商缓存条件:
Cache-Control: no-cachemax-age时间过期
Last-Modified(文件的修改时间):
服务器在响应头中添加Last-Modified属性,来指出资源最后一次修改时间。当浏览器发起请求时,会在request headers中添加一个If-None-Match属性,值为上一次请求的资源返回的Last-Modified值。服务端会通过这个属性和资源最后一次修改时间进行对比,以此来判断资源是否修改。如果资源没有修改,请求返回304状态,客户端使用本地缓存。如果资源有修改,则返回修改的资源。
这种方式有一个缺点,Last-Modified标记的时间只能精确到秒。
ETag(文件改动):
同样在服务器返回资源的时候,在头信息中添加ETag属性,这个属性是资源的唯一标识符。当资源改变时,这个值也会改变。在一下次请求资源时,会在request headers中添加一个If-None-Match属性,值为上一次请求的资源返回的ETag值。服务端会通过这个属性和资源最后一次修改时间进行对比,以此来判断资源是否修改。这种方式比Last-Modified更加准确。
区别
- 强缓存优先级高于协商缓存
- 强缓存不需要发请求,协商缓存需要。
- 强缓存返回的状态码为
200,协商缓存返回304 - ctrl+F5强制刷新会跳过所有缓存,而F5刷新跳过强缓存,但是会检查协商缓存。
POST和GET的区别
- 传递的参数不同,
POST传递的参数在request body中,GET传递的参数在url后拼接 POST相对GET请求安全GET请求长度有限制,POST没有GET请求会被浏览器主动缓存,POST不会,要手动设置GET请求一般用于查询,POST一般用于提交某种信息进行某些修改操作
XSS、csrf攻击
XSS(跨站脚本攻击)
Xss(cross-site scripting) 是一种代码注入攻击,攻击者往Web页面里插入恶意 html 标签或者 javascript 代码。在骗取用户点击后获取用户,获取用户信息。
避免方式:
url参数通过encodeURIComponent方法进行转义- 尽量不使用
InnerHtml插入HTML内容 - 对用户输入的地方和变量都需要仔细检查长度和对 ”<”,”>”,”;”,”’” 等字符做过滤
CSRF(跨站请求伪造)
CSRF(Cross-site request forgery)攻击者盗用你的身份信息,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
避免方式:
-
添加验证码验证
-
使用
token- 服务端给用户生成一个
token,加密后传递给用户 - 用户在提交请求时,需要携带这个
token - 服务端验证
token是否正确
- 服务端给用户生成一个
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"
复制代码
function aaa(){
var a=0;
function bbb() {
a++;
alert(a);
}
return bbb
}
var ccc=aaa();
ccc(); //结果为1
ccc(); //结果为2
var ddd=aaa();
ddd(); //结果为1
ddd(); //结果为2