–前端面试知识总结
概念
区别:
https工作原理:
这个过程中,会出现中间人攻击,就是第一次服务器向客户端返回公钥的时候,在中间被攻击者截获,生成【伪公钥】给了客户端,之后客户端生成的秘钥发送给服务器时,在中间又被攻击者截获,攻击者就可以通过自己的私钥获得真的秘钥,然后又伪造假的秘钥给服务器,这样客户端和服务器端的通信就完全暴露给攻击者了,因为双方的加密过程都被攻击者知道的一清二楚;所以第一次服务器给客户端返回公钥时,必须一同返回CA证书。
比较:
应用场景:
三次握手: 客户端和服务端都需要直到各自可收发
第三次握手是为了防止失效的连接请求又传送到了服务器建立了连接,浪费网络资源
四次挥手
等待2MSL:保证客户端最后一个确认的报文可以到达服务器, 保证tcp连接能够进行可靠的关闭 (假如客户端发送ack之后直接关闭了,然后ack又丢失了,服务器端就会一直处于等待关闭的状态,等待一定时间后会重复向客户端发送释放连接请求,但是客户端已经关闭了)
且防止已经失效的连接请求出现在连接中( 客户端在发送最后一个ACK之后,再经过经过2MSL,就可以使本链接持续时间内所产生的所有报文段都从网络中消失。从保证在关闭连接后不会有还在网络中滞留的报文段去骚扰服务器 )。
2MSL: Maximum Segment Life,这是TCP 对TCP Segment 生存时间的限制 : 客户端发出ACK,等待ACK到达对方的超时时间 MSL,等待FIN的超时重传,也是MSL,所以如果2MSL时间内没有收到FIN,说明对方安全收到FIN
总结:建立连接:服务器要确认客户端的连接请求,客户端对服务器的确认进行确认。
https://www.cnblogs.com/daijinxue/p/6640153.html
重传计时器:
tcp发送报文时候启动重传计时器,在计时器截止时间之前收到了对这个发送的报文的ack,就撤销计时器;
在时间到之后还没收到ack,就重传报文,并将计时器复位,重新开始计时。
持久计时器:
当发送tcp收到窗口大小为0的确认时,就启动持久计时器,时间到了之后,会发送特殊的探测报文,收不到探测报文的确认之后就会将持久计时器复位且时间加倍,之后时间到了继续发送探测报文… …直到计时器时间到达60s,之后计时器时间不会再加倍,发送端就会60s发送一次探测报文,直到接受端确认可以发送数据了(窗口大于0了)。
保活计时器:
防止两个TCP之间出现长时间的空闲。
一般是服务器收到客户端的信息之后就会在服务器上设置保活计时器(2h)。
比如,客户端故障了,服务器收不到客户端的信息,等待2h之后,就会发送探测报文(每75s发送一个),发送10个之后还没有响应,就直接判定客户端故障,断开连接。(硬性中断)。
时间等待计时器:四次挥手中的2MSL.
post和get本质上没有任何区别,都是http协议中的请求方法,本质都是tcp连接;
为了对不同请求的类型做出区分,才有了getpostdeleteput这几种,然后制定了一些使用上的规定:
应用场景:
post :
无法使用缓存文件,要更新服务器上的文件或者数据库时
发送大量数据时
发送包含未知字符的数据时
session比cookie安全可靠些,且可存储的数据量无限制(cookie只允许4k),但是session中信息过多开销很大。
1XX:信息类状态码,表示信息正在处理。
100 continue:到目前为止正常,客户端可以继续发送请求。post请求第一次tcpl连接就是返回100.
2XX:成功类,表示请求处理成功。
200 ok : 请求成功。用于post/get请求
3XX:重定向状态码,需要进一步操作完成请求。
301:永久重定向
302:临时重定向
303:使用get方法临时重定向
304:如果请求报文首部包含一些条件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。自从上次请求后,请求的网页未修改过。
305:临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。
4XX:客户端错误,表示客户端请求出错或无法完成请求
400 Bad Request:请求报文中存在语法错误, 前端提交数据的字段名称和字段类型与后台的实体没有保持一致 (常见的就是后台要求接收json字符串,但是前端没有处理好)
401:发送的请求需要有认证信息
403 Forbidden:请求被拒绝
404 Not Found:服务器无法根据客户端的请求找到资源
5XX:服务器错误,服务器在处理请求过程中发生了错误
500 Internal Server Error:服务器内部错误
503 Service Unavailable:由于超载或系统维护,服务器暂时无法处理客户端请求
HTTP报文是用于HTTP协议交互的信息,本身是多行数据构成的字符串文本;可以分为请求报文和响应报文。
请求报文
请求行:请求方法,url,HTTP版本
请求首部字段,通用首部字段,实体首部字段
请求内容实体
响应报文
状态行:HTTP版本,状态码
响应首部字段,通用首部字段,实体首部字段
响应内容实体
请求首部字段
Host:请求资源所在服务器
Accept:可接受的媒体类型
Accept-Charset:可接受的字符集
Accept-Encoding:可接受的内容编码
Accept-Language:可接受的自然语言
响应首部字段
Accept-Ranges:可接受的字节范围
Location:令客户端重定向到的url
Server:HTTP服务器的安装信息
通用首部字段
Date:创建报文的时间
Connection:连接管理
Cache-Control:缓存控制
Transfer-Encoding:报文主体的传输编码方式
实体首部字段
Allow:资源可支持的HTTP方法
Content-Type:实体主体适用的编码方式
Content-Language:实体主体的自然语言
Content-Length:实体主体的字节数
Content-Range:实体主体的位置范文,一般是发出请求时使用
http 1.0
特点
无状态:服务器不跟踪不记录请求的过程
无连接:浏览器每次请求都要建立tcpl连接
缺点
无法复用请求:每次发起请求都要进行tcp连接,网络利用率低
队头阻塞:在前一个请求响应到达之后才能发起新的请求。请求1 > 响应1 请求2 > 响应2 请求3 > 响应3
http 1.1
特点
长连接:新增Connection字段设置keep-alive保持tcp连接不断开,继续使用这个通道传输数据
管道化:请求可以不等响应,继续发送;请求1 --> 请求2 --> 请求3 > 响应1 --> 响应2 --> 响应3
缓存处理:新增Cache-Control,请求资源时如果有缓存就直接取,没有缓存再发送请求
断点传输:上传下载时,可以从断点处开始
缺点
管道化中虽然请求可以持续发送,但是人不能解决队头阻塞。
http 2.0
特点
二进制分帧:对所有传输的信息使用二进制编码,分割成更小的消息和帧。解决了数据过多和串行传输问题。
多路复用:同时发送请求和接受响应。同一个连接并发处理多个请求
头部压缩:压缩header,体积更小,传输更快
服务器推送:服务器可额外向客户端推送资源,适合加载静态资源。
http 1.0 —> http 1.1 长连接
http 1.1 —> http 2.0 多路复用
代理ip就是代理服务器,是一种重要的安全功能,主要在开放系统互联模型的会话层起到防火墙的作用。分为正向代理和反向代理,一把直接说代理是指正向代理。
正向代理
反向代理
反向代理也是处于客户端和目标服务器之间,负责转发请求和响应的。但是客户端是不知代理存在情况下,直接访问目标服务器站点。
总结:一个是用户向代理服务器发起请求,一个是用户向目标服务器发起请求。虽然都是由代理进行转发,但是从用户的角度是不一样的,正向中用户知道代理的存在,但是反向中用户不知代理的存在。
ngnix是一个高性能的http服务器和反向代理web服务器
将代理服务器接收到的请求按照规则分发的过程,称为负载均衡。(负载指反向代理接收到的请求数量)
短轮询
客户端使用定时器向服务器发送Ajax请求
浪费带宽和服务器资源
长轮询
客户端向服务器发送请求,只有当数据符合要求时才会响应(如果超时没有响应,就关闭连接),客户端收到响应或连接关闭后,可再次发起新的请求
服务器维护长连接增加开销
长连接
页面中插入iframe或script向服务器发起请求(减少对主页面的影响)
服务器维护长连接增加开销,iframe可能涉及跨域。
WebSocket
是HTML5开始提供的一种在单个tcp连接上进行全双工通讯的协议。
特点:
function myWebSocket(){
if('WebSocket' in window){
//ws指http wss指https
let ws = new WebSocket('ws://localhost:3000');
//连接建立时触发
ws.onopen = () => {
ws.send('data');
}
//客户端收到服务器数据时触发
ws.onmessage = (e) => {
console.log(e.data)
}
//通信发生错误时触发
ws.error = (e) => {
console.log(e)
}
//连接关闭时触发
ws.onclose = () => {
console.log('ws close')
}
}
else{
console.log('浏览器不支持websocket')
}
}
利用用户对指定网站的信任
利用网站对浏览器的信任
国内的浏览器大多数时双核驱动(Trident+Webkit / Trident+Blink)
移动端:iphone,ipad使用webkit,android 4.4以下采用webkit,以上使用blink
头部 + 主体
申明:下面的文档使用html5的规范进行解析
<html>
<head>
<meta charset="utf-8">
<meta name="keywords" content="">
<mate name="description" content="">
<title>title>
<link rel="stylesheet" href="外部样式">
<style>放置样式style>
head>
<body>
...
<script>脚本代码script>
body>
html>
块级元素:独占一行,宽高可控,没有设置宽度时默认铺满。可以包含块级和行内元素(但是p,hx,dt标签里面不可以再包含块级元素) display:block/table/list-item
div p ul ol pre table adress… …
行内元素:不会独占一行,会与相邻行内一行,直到该行满就自动转换。宽高不可控(设置width和height无效,除非转为块级),只能包含行内元素 display:inline/inline-block/inline-table/table-cell
span a i em strong b sub sup img td… …
两个元素可以通过display进行转换。
img时行级块标签,相当于display:inline-block
该标准由结构(html)、表现(css) 和 行为(js) 组成
用来将多个网页文件组合成一个文件,常用的属性:name src scrolling width height frameborder marginheight marginwidth
可以使用iframe实现tab选项卡的演示
但是实际开发中不要使用iframe,因为它破坏前进后退的功能且不利于seo抓取关键字。
介绍:层叠样式表,或者串样式表,由w3c定义和维护的标准,一种用于为结构化文档添加样式的计算机语言
使用:让结构和表现分离。css只负责表现
基本语法:选择器 { 属性 : 属性值 }
引用方式:
link和@import的区别:link可以加载多种资源,且是在页面载入的时候加载的,由于时xhtml标签因此没有兼容问题,支持js控制区更改样式。@import只可以加载css,要等网页加载完毕之后才会加载,对于低版本浏览器不支持,也不支持js控制
优先级:内联 > 内部 > 外部
!important 10000> 内联 1000> id 100> 类、伪类选择器 10> 标签,伪元素 1 > 通用选择器* 子选择> 相邻选择 + 同胞选择~ 0.1
每个元素都是一个盒子, 标准盒模型:一个块的总宽度=width+margin(左右)+padding(左右)+border(左右)
height = line-height + vertical-align
列表:ul ol dl(dt --> dd)
list-style: list-style-image list-style-position(inside/outside) list-style-type
定位是设定元素在文档的位置,会将元素转换为块级
static静态定位,默认值,没有定位,不能设置偏移值,占用标准文档流
fixed固定定位,脱离文档流,相对浏览器窗口左上角做偏移,与父级没有关系,一般用于做固定导航等
relative相对定位,占用标准文档流,可以设置偏移值修改位置,偏移的相对原点是该元素原来位置的左上角处(并不是屏幕的左上角)
absolute绝对定位,脱离文档流,相对body做偏移
当绝对定位与相对定位结合使用,它相对的是relative父级元素做偏移
z-index在static和relative上无效,可以为负值,值越大层级越上。默认值为0
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<style>
.container{
width: 100%;
overflow: hidden;
}
.clo{
height: 200px;
float: left;
}
.left{
width: 300px;
background-color: blue;
margin-left: -100%;
}
.center{
width: 100%;
background-color: green;
}
.content{
margin: 0 200px;
}
.right{
width: 300px;
background-color: red;
margin-left: -300px;
}
style>
head>
<body>
<div class="container">
<div class="clo center"><div class="content">center-contentdiv>div>
<div class="clo left">leftdiv>
<div class="clo right">rightdiv>
div>
body>
html>
圣杯布局:一样两边固定,中间自适应,但是与上面不同的是增加了相对定位和偏移设置
<style>
*{
margin:0;
padding: 0;
}
div#header, div#footer{
height: 200px;
width: 100%;
background-color: #eee;
}
.container{
padding: 0px 200px;
overflow: hidden;
}
.col{
float: left;
height: 200px;
position: relative;
}
.left{
width: 200px;
background-color: blue;
margin-left: -100%;
left: -200px;
}
.right{
width: 200px;
background-color: green;
margin-left: -200px;
right: -200px;
}
.center{
width: 100%;
background-color: red;
}
style>
head>
<body>
<div id="header">div>
<div class="container">
<div class="col center">cdiv>
<div class="col left">ldiv>
<div class="col right">rdiv>
div>
<div id="footer">div>
body>
侧边栏固定布局
FC:formatting context ,格式化上下文,是css2.1规范中的一个概念,是页面中的一块渲染区域,决定了子元素的定位,以及和其他元素之间的相互作用。分为bfc和ifc
BFC:block …块级格式化上下文
形成条件:浮动元素(float除none的值),定位元素(position:absolute/fixed),display:inline-block/table-cell/table-caption,overflow:hidden/auto/scroll
特性:
作用
IFC:inline …格式化上下文
形成条件:font-size, line-height, height, vertical-align
特性:
注意:line-height是以该行的基线为准
水平居中
垂直居中
对于行内元素:
单行:设置上下padding相等,或者设置line-height和height相等
多行:设置上下padding相等,或者设置display:table-cell 和 vertical-align:middle; 或者设置flex布局;或者使用为元素
对于块级元素:
已知高度:子元素使用绝对布局top:50%;在使用transform:translate(-50%) / 或者margin-top:负的自身高度的一半
未知高度:子元素使用绝对布局position:absolute;top:50%;transform: translateY(-50%); 或者flex布局 justify-content: center;
水平垂直居中
定高定宽:
方案一:设置父元素为相对定位,给子元素设置绝对定位,top: 0; right: 0; bottom: 0; left: 0; margin: auto;
方案二:设置父元素为相对定位,给子元素设置绝对定位,left: 50%; top: 50%; margin-left: --元素宽度的一半px; margin-top: --元素高度的一半px;
高度和宽度未知:
先用绝对布局 top: 50%; left: 50%;,再设置 transform: translate(-50%, -50%);
使用 Flexbox:justify-content: center; align-items: center
前端html语义化: 根据内容的结构(内容语义化),选择合适的标签(代码语义化)便于开发者阅读和写出更优雅的代码,同时让浏览器的爬虫和机器很好地解析。
好处:用户体验友好,且利于seo,方便开发和维护。
input的属性:autocomplete autofocus step mutiple pattern plceholder required
formaction : 修改action数据提交
formenctype:修改表单请求的类型
formmethod:修改数据提交的方法
form:设置表单元素属于哪个表单
novalidate: 不验证
css3是css2.1的升级版,不同的浏览器使用的时候会有兼容性的问题,需要做兼容处理
[选择器](#5.3 css选择器)
阴影
形状转换(2d – > 3d) transform
变形
过渡动画,帧动画 transition animation
边框 border/border-radiaus/box-shadow/border-image
背景
多重背景:background:背景色 图片 平铺方式 位置 ,背景色 图片 平铺方式 位置,… …eg:background:url(),url(),url()可以将多张图拼在一起
background-size background-clip background-origin
渐变背景 background-image
线性渐变 line-gradient(方向,颜色1 百分比,颜色2 百分比, … … )
径向渐变 radial-gradient(形状 渐变大小 at 位置,颜色1, 颜色2 , … …)
反射 -webkit-box-reflect
[文本](#5.4 文本)
颜色(rgba/hsl/hsla)
颜色(rgba/hsl/hsla)
滤镜(filter)
弹性布局 flex
栅格布局 grid
多列布局
盒模型 允许使用某种方式定义某些元素,以适应指定的区域。box-sizing: content-box/ border-box
web字体
媒体查询 就在监听屏幕尺寸的变化,在不同尺寸的时候显示不同的样式
[主流浏览器内核](#3.4 浏览器内核)
添加厂商前缀
IE:-ms- Chrome&Safari: -webkit- Firefox: -moz- Opera: -o-
属性选择器 p[class=content]
结构性伪类 :root body first-child … …
目标伪类 :target
ui元素状态伪类 :enabled :checked ::selection(用户选中的时候) :disabled
否定伪类 :not()
通用兄弟选择器 E ~ F 匹配e之后的f元素
文本阴影text-shadow:水平偏移 垂直偏移 模糊 阴影尺寸 颜色
文本自动换行word-wrap:normal | break-word
单词拆分word-break:normal | break-all | keep-all
文本拆分text-break:
文本溢出
单文本溢出text-overflow : ellipsis | clip | string
多文本溢出
display:-webkit-box;
-webkit-box-orient:vertical;
-webkit-line-clamp:行数;
pverflow:hidden;
文字渐变色
background-image:liner-gradient();
background-clip:text;
text-fill-color:transparent;
transform方法 详解transform (注意x,y,z轴的定义,rotateY和Z是怎么旋转的)
transform-origin更改基点,一般与旋转配合使用
perspective 让子元素获得透视效果,单位是长度;主流浏览器都不支持,谷歌加上-webkit-前缀
transform-style 在3d空间呈现被嵌套的元素,与transform一同使用,flat(默认值) | preserve-3d
过渡动画
transition : property duration timing-function delay; 默认值 all 0 ease 0
property :可以使用的属性有
颜色:color bgc-color border-color outline-color
位置:bgc-position left right top bottom
数字:height width line-height text-indent vertical-align outline-offset font-size margin padding border-spacing letter-spacing word-spacing opacity visibility z-index font-weight zoom
组合:text-shadow transform box-shadow clip
其他:gradient
timing-function:动画函数 linear ease ease-in(先慢后快) ease-out ease-in-out cubic-bezier
关键帧动画
步骤:
1.设置关键帧
@keyframes 动画名称 {
0%{样式} ===from{}
100%{样式} ===to{}
}
@keyframes 动画名称 {
0%{}
25%{}
...
100%{}
}
2.实施动画
常规用法 animation:name(就是上面@keyframes后面的动画名称) duration timing-function delay iteration-count(循环次数num/infinite) direction(播放完毕是否反向播放normal/alternate)
animation-play-state(动画播放或者停止paused/running)
column-count 规定分成几列 number | auto
cllumn-width 设置列的宽度 length | auto
column : width | count
简写模式
column-gap 设置栏间距 number | auto
column-rule 列之间的分隔线设置,width | style | color 与border的设置很像,可以分开指定
弹性布局是当页面需要适应不同屏幕大小和设备时使用
display:box;
box-orient: horizontal | vertical | inherit; 该属性定义父元素的子元素是如何排列的。
box-pack: start | end | center | justify; 该属性定义父元素的子元素是如何排列的。但是注意这种排列是沿box-orient方向的
box-align: baseline | center | end | start | stretch
box-flex: 0 | 任意数字; 该属性让子容器针对父容器的宽度按一定规则进行划分
display: flex ; 需要处理浏览器兼容问题
flex-direction 指定了弹性子元素在父容器上的位置,排列的规则。row(横向排) row-reserve column column-reverse
justify-content 把弹性元素沿着弹性容器主轴线对齐 。flex-start(紧凑左对齐) flex-end(紧凑右对齐) center(紧凑居中对齐) space-between(首尾贴边,其余等分) space-around(等分)
align-items 子元素在纵轴上的对齐方式 flex-start(顶端对齐) flex-end(底端对齐) center(居中对齐) baseline(以文字的基线对齐) stretch(纵向拉伸对齐,适用于没有指定高度时)
flex-grow 应用于子元素上,表示指定元素的放大比例。number 适用于没有指定宽度的时候,对屏幕进行划分。
flex 用于指定子元素空间分配 auto(=1 1 auto) initial(=0 1 auto) none(=0 0 auto) inherit
flex == flex-grow(放大比例) flex-shrink(收缩比例) flex-basis(基本宽度)
responsive design,在不同分辨率屏幕上的展示方式,通过响应式设计,使得网页在不同设备上的阅读体验好。
响应式和自适应区别:(面试点)
响应式只开发一套代码,针对不同的设备对代码进行处理,展现不同的布局和内容。但是自适应是开发多套代码界面,通过检测窗口的分辨率判断设备,然后请求返回不同的页面。
响应式布局等同于流动网格布局,但是自适应是使用固定分割点进行布局
自适应布局给出了更多的设计空间,只用考虑几种不同的状态就行。但是响应式需要考虑上百种不同的状态,更加复杂。
这个内容只能被移动端识别
2,通过媒体查询设置样式 3,设置多种视图的宽度(此时宽度不可以再写死,一般是百分比或者vw/vh/rem),图片的缩放问题,其他属性的处理步骤:
设置meta标签
设置IE的渲染方式
引入兼容的js
进css3提供的媒体查询
引入外部不同的css文件
style里面使用@media
开发的时候使用@font-face引入外部字体
@font-face {
font-family: xxx;
src:url(文件路径) format(字体文件格式处理兼容), url(文件路径) format(字体文件格式处理兼容);/*同时引入多个文件可以处理兼容*/
}
p{
font-family:xxx;
}
阿里提供的图标:在官网将需要的图标加入购物车之后,下载,下载的就是一些iconfont的字体文件和css文件,css引入项目中,然后使用class=“iconfont iconname"的方式或者编码方式去使用。可以使用font-size等去修改图标基础样式
<div class="iconfont home">div>
<div class="iconfont">̳div>
默认情况下,****js是同步加载的,js加载时是阻塞的****,后面的元素要等待js加载完后才能进行加载,在一些页面就会出现白屏效果,所以需要异步加载js。
有三种实现方案,第一种是给script标签加一个defer属性,脚本会延迟到整个页面加载完后再执行,按照原本的顺序执行,同时会在document的DOMContentLoaded之前执行。
第二种是给script标签加一个async属性,脚本会立刻被异步加载,async不能保证脚本的顺序性,但都会在onload之前完成。
第三种是创建script标签,插入到dom中,加载完后回调。
作用域:是变量和函数的可访问范围,作用域控制着变量和函数的可见性和生命周期,js中作用域主要分为全局作用域和局部作用域。在浏览器中,全局作用域就是window对象,声明一个函数,会在函数内部创建一个局部作用域。
注意:表面上看,在js中块级作用域(块级作用域指的是被{}包起来的部分)是不存在的,比如说if{…} for(){…},但是使用with,try/catch,let,const可以创建块级作用域。
作用域链:声明一个函数时,局部作用域一级一级向上包起来,就形成了作用域链。作用域链保证了对执行环境有访问权限的变量和对象的有序访问。内部环境可以通过作用域链访问外部环境,而外部环境不能通过作用域链访问内部坏境中的变量和对象。作用域的前端是当前执行代码所在环境的变量对象,终端是全局作用域。
垃圾回收是找到不再继续使用的变量,然后释放他们的内存。Js的垃圾回收是自动的。
方案一:通过标记-清除算法来进行垃圾回收的,过程是垃圾回收器判断内存中的对象是否可达,将具有可达性的对象进行标记,除了标记的对象外,删除未被标记的对象。
方案二:引用计数,跟踪每个值被引用的次数,当次数为0时就可以回收了。问题是循环引用(解除变量引用助于消除循环引用,也有利于垃圾回收)。
详细链接
实例Person1 原型Person prototype
通过使用 hasOwnProperty()方法,用于判断实例属性是否存在的。(对原型属性判断不了,判断原型属性使用hasPrototypeProperty,该方法只对唯一存在原型中的属性返回true,若属性同时也存在于实例中,返回false)。然后 属性名 in 实例 中的 in 操作符对原型属性和实例属性均能判断。配合hasOwnPrototype一起使用,可以判断属性到底是位于什么上的。
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
}
函数/类实例/对象,都会具有一个prototype属性,指向函数/类/对象的原型对象。这个原型对象具有constructor属性,指向prototype所在地函数/类/对象。所有引用类型(函数,数组,对象)都拥有_proto_
属性(隐式原型),通过_proto_
连接对象和原型组成了原型链。
使用原型对象的好处是可以让所有对象实例共享所包含的属性和方法。
ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的。
原型链:将父类实例赋给子类原型,无法传参.(原型对象等于另一个类型的实例)
构造函数:在子类构造函数内部调用父类,无法复用父类中的方法
组合模式:原型链+构造函数,原型链继承原型属性和方法,构造函数继承实例属性。缺点:父类调用两次:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name); //第二次调用 SuperType()
this.age = age;
}
SubType.prototype = new SuperType(); //第一次调用 SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
原型式:基于已有对象创建新对象,并创建自定义类型
function obj(o){ //o是已经存在的对象
function F(){}
F.prototype = o;
return new F();
}
寄生式:原型式+工厂模式,封装创建:即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象
function create(o){
var f= obj(o);
f.run = function () {
return this.arr;//同样,会共享引用
};
return f;o
}//新对象不仅具有 o的所有属性和方法,而且还有自己的 run()方法。
寄生组合式:通过借用构造函数来继承属性,通过原型链继承方法
function inheritPrototype(subType, superType){
var protoType = Object.create(superType.prototype);//创建对象,copy而不是调用,这里就与组合模式相比,减少一次对父类的调用
protoType.constructor = subType;//增强对象
subType.prototype = protoType;//指定对象
}
//examples
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name); //仅一次调用supertype
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
};
闭包:是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式是,在一个函数内部创建另外一个函数。
当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生
了闭包。 ———<你不知道的javascript>
function foo() {
let a = 2;
function bar() {
console.log(a++);//这就是闭包。按道理说,bar是在foo作用域中的,只有foo可以使用它;
}
return bar();//返回出去是很重要的
}
var f = foo();//实际上 f是调用的bar()
f();//2 f()也就是bar()竟然在自己定义的词法作用域以外的地方执行,这就是闭包的作用
f();//3
//foo()在执行之后,按理说会被垃圾回收机制回收,但是bar()闭包可以将这个内部作用域锁定(bar()依然持有对该作用域的引用,而这个引用就叫作闭包),这样就还是可以打印出a的值。且当你第二次调用f()的时候,a并不是foo初始化的2,为什么?都说了f()实际上调用的是bar(),内部作用域是被bar控制的,但是bar()里面用到的a已经在第一次使用f()的时候被修改了,第二次调用a的时候a已经是3了。
for (var i=1; i<=5; i++)
{
(function(j) { //j是形参,使用到了闭包,所以每次循环会创建一个新的作用域
setTimeout( function timer() {
console.log(j);
}, j*1000);
})( i ); //i是实参 函数表达式(function(形参))(实参)
}
//上面可以简化为
for (let i=0; i<=5; i++) { //let声明,可以用来劫持块作用域
setTimeout(i)(function timer() {
console.log(i);
}, i*1000)
}
模块机制
可以使用闭包实现模块机制
function foo(){
var a = 1;
function say(){console.log(a)}
function change(){a=2}
return {
say:say,
change:change
}
}
var bar = foo() //实际上bar是{say:say,change:change}
bar.say();//a=1
bar.change();//a=2
在现在es6的发展上,模块使用的是export default/export进行导出api,import 导入,module导入整个模块
this是在运行时基于函数的执行环境绑定的。有四种绑定规则,****默认绑定、隐式绑定、显示绑定、new 绑定****。默认绑定一般指的是将全局对象绑定到this,隐式绑定一般是某个对象上触发的,显示绑定指的是使用call和apply,主要是改变对象的prototype关联对象。 如果函数或者方法调用之前带有关键字new,它就构成构造函数调用,对于this绑定来说,称为new绑定
页面上的交互通过事件实现,事件流是从页面中接收到事件的顺序
任何js类型值都可以转成boolen
undefined,null,0,-0,NaN,””=>false
其他所有值=>true
对象转换成其他:原理是先调用valueOf,然后调用toString,
== vs ===
== 先转换类型,再进行比较
若一个为 null ,一个为 undefined ,则相等
若一个为字符串,一个为数字,则将字符串->数字,再比较
若一个为 true ,则转为1比较; 若一个为 false ,则转为0比较
若一个为对象,一个为字符串/数值,则将对象转为基础类型再比较
undefined 和 null 任何有意义值比较都是 false
NaN === NaN false
===完全等于,包括类型也要完全一致
typeof:对于基本类型,除了 null 都可以显示正确的类型,对于对象,除了函数都会显示 object
var arr = [1,2,3];
console.log(typeof arr.valueOf());//object
console.log(arr.valueOf() instanceof Array);//true
instanceof:可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
undefined===null => false
null == undefined =>true
typeof(null) (object) == typeof(undefined) (undefined) => false
浅拷贝:仅是复制了引用,彼此之间的操作会相互影响数据。
深拷贝:重新分配内存,不同的地址,相同的值,操作互不影响。
Object.assign({},o1);一维对象深拷贝。二维不行
扩展运算符 …
JSON.parse(JSON.stringify(obj)) 对function undefined的对象不能深拷贝,其余均可
递归深拷贝
function deepCopy(obj){
var res = (obj instanceof Array)? [] : {};
for(let key in obj){
if(obj[key] && typeof obj[key]=='object'){
res[key] = deepCopy(obj[key]);
}else{
res[key] = obj[key];
}
}
return res;
}
查找结点 document.xx
通过id查找dom结点:getElementByID('')
通过class属性名:getElementByClassName()
通过标签名称:getElementByTagNames()
通过指定名称:getElementByName()
匹配选择器:只返回匹配的第一个querySelector()
匹配选择器,返回匹配的所有:querySelectorAll()
获取页面中的html标签:documentElement
获取页面中的body标签:body
获取页面中所有元素节点的对象集合型:all('')
新建节点
新建元素节点:createElement()
新建属性节点:createAttribute()
新建文本节点:createTextNode()
新建注释节点:createComment()
新建文档片段节点:createDocumentFrament()
增加节点
向父元素的最后一个子节点追加新节点:appendChild()
向父节点的某个特定子节点之前插入新节点:insertBefore()
给元素增加属性节点和设置值:setAttribute()
删除节点
删除已有的子节点,返回值为删除节点:parentNode.removeChild(existingChild);
删除具有指定节点的属性,无返回值:removeAttribute(‘属性名’);
删除指定属性,返回值为删除的属性:removeAttributeNode(attrNode);
修改节点
用新节点替换父节点中已有的子节点:parentNode.replaceChild(newChild, existingChild);
修改已有节点的属性值:setAttributeNode( attributeName);
修改已有节点的属性值:setAttribute(attributeName,attributeValue)
arguments实质上是 函数内一个內建对象,它包含函数接收到的所有变量,有索引和length属性等,但是并不能使用数组方法。
转换为数组
可以使用额外的数组空间,对arguments进行for循环,讲变量取出来放在arr里面
使用arr=[].slice.call(arguments) 通过call使其能调用数组中的slice方法,从而使其能转为数组
[…arguments]
最好使用循环转化为数组,因为slice可能会不利于优化
函数知识点:区分函数的两种写法
函数声明:function作为开头是函数声明
function xx () { ... }
函数表达式:开头不是function的是函数表达式
(function xx(){ ... })() //第一对括号里面的是函数表达式,第二对括号是将前面的函数表达式立即执行
var xx = function yy(){ ... }
提升:所有的声明都会被移动到各自作用域的顶端。
现有声明,后有赋值;函数声明和变量声明都会被提升。
a = 2; //赋值声明,保留在原地
var a; //定义声明,会被提升
console.log(a) //2
/*---------*/
console.log(a) //undefined
var a = 2; //var a=2; ==》 var a; a=2; 其中var a定义声明会被提升,但是a=2赋值声明会停在原地。
/*---------*/
foo()
function foo(){ ... } //函数声明也会被提升,上面的foo()调用没有问题
/*---------*/
foo() //TypeError
var foo = function bar(){ ... } //函数表达式==》var foo这个会被提升; foo = function bar()停在原地,上面的foo()会报错
跨域:从一个域名去请求另外一个域名的资源,由于浏览器的同源策略(协议端口域名要相同),浏览器在当前域名下不能执行其他域名网站的脚本。
jsonp:本质上是hack,他利用
Nginx时一款极其强大的web服务器,轻量级、启动快、高并发。现在的项目中大多都使用Nginx进行反向代理。
缓存可以说是性能优化中简单高效的一种方式。优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且而由于缓存文件可以重复利用,可以减少带宽,降低网络负荷。
根据是否需要向服务器重新发起http请求将缓存过程分为强制缓存和协商缓存。
强制缓存:只要缓存没过有效期,就强制读取缓存,响应code为200。该类缓存必定是要持久化到disk的。Firefox中并没有对强制缓存进行进一步的分类。但谷歌浏览器的处理略有不同,分为两种:一种是(from disk cache),另一种是(from memory cache)
协商缓存: 每次读取缓存时,先到服务器端去验证一下是否有改变,如果有就获取新的,没有从缓存中读取,响应code为304
强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回304,继续使用缓存。
频繁变动的资源
Cache-Control: no-cache
对于频繁变化的资源,首先需要使用Cache-Control: no-cache使得浏览器每次都请求服务器,然后配合ETag或者Last-Modified来验证资源是否有效。这样不能减少请求量,但是可以减少响应的数据大小。
不常变化的资源
Cache-Control: max-age=31536000(一年)
对于这类资源,给Cache-Control设置很大的max-age这样浏览器之后请求相同的url会命中强制缓存。
service worker
Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。
当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容。
memory cache
Memory Cache 也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。
那么既然内存缓存这么高效,我们是不是能让数据都存放在内存中呢?这是不可能的。计算机中的内存一定比硬盘容量小得多,操作系统需要精打细算内存的使用,所以能让我们使用的内存必然不多。当我们访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存。
内存缓存中有一块重要的缓存资源是preloader相关指令(例如)下载的资源。总所周知preloader的相关指令已经是页面优化的常见手段之一,它可以一边解析js/css文件,一边网络请求下一个资源。
需要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control是什么值,同时资源的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type,CORS等其他特征做校验。
disk cache
Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。
在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自 Disk Cache
浏览器会把哪些文件丢进内存中?哪些丢进硬盘中?关于这点,网上说法不一,不过以下观点比较靠得住:
对于大文件来说,大概率是不存储在内存中的,反之优先。当前系统内存使用率高的话,文件优先存储进硬盘
push cache
Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
解析过程:DNS查询过程:浏览器缓存→系统缓存→路由器缓存→hosts文件→ISP DNS缓存→递归搜索
DNS缓存:只要有DNS解析的地方就有DNS缓存,默认浏览器会缓存访问过域名的ip地址,设置系统缓存。
DNS预解析:
1、告诉浏览器,页面要进行DNS预解析
<meta http-equiv='x-dns-prefetch- control' content='on'>
2、通过link标签指定域名进行cdn域名预解析
DNS负载均衡
TCP连接
HTTP请求优化:减少请求次数,减少单次请求花费的时间
减少请求次数:
减少单次请求花费时间
主要是减少请求的文件的体积,自然可以减少时间。
缓存
减少网络IO消耗,提高页面的访问速度
浏览器渲染流程:JS执行 => CSS 样式渲染 => 布局 => 绘制 => 合成
对上面的流程有些不解,与一般的过程似乎有区别,DOM+CSSOM生成渲染树 https://www.jianshu.com/p/e6252dc9be32
js执行优化
1)对于动画效果的实现,避免使用 setTimeout 或 setInterval,使用 requestAnimationFrame
2)如果不需要访问 DOM,可以将长时间运行的 Javascript 工作从主线程移动 worker 线程
3)如果必须在主线程上执行大型任务,可以将大型任务分割为多个微任务,每个微任务所占时间不超过几毫秒
css样式渲染优化:降低选择器的复杂性, 尽量将嵌套层减少到最小 。
布局优化
布局是浏览器计算各元素的几何信息的过程,包括元素的大小和在页面中的位置。
1)尽可能避免布局操作
几何属性(高度、宽度、位置等)的更改都会引发布局计算,而布局几乎总是作用到整个文档,所以会耗费大量性能。
2)使用 flexbox 布局模型
flexbox 布局模型的性能更好。
绘制优化:绘制是浏览器填充像素的过程。绘制通常是渲染过程中性能开销最大的部分,应该尽可能避免绘制。
合成优化(composite):合成是将页面已绘制的部分放在一起在屏幕上显示的过程。页面中的合成层数量不宜太多(需要管理的合成层数量过多影响性能),如果没有必要,还是不要将元素提升为合成层。
坚持使用 transform 和 opacity 属性更改来实现动画
使用 transform 和 opacity 时要注意的是,这些属性所在的元素应处于其自身的合成器层(will-change 提升)。
雪碧图:也叫精灵图,是把多张小图放在一张大图上,目的是为了减少请求次数,达到优化目的。
JS的解析是由浏览器中的JS解析引擎完成的。JS是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始。但是又存在某些任务比较耗时,如IO读写等,所以需要一种机制可以先执行排在后面的任务,这就是:同步任务(synchronous)和异步任务(asynchronous)。JS的执行机制就可以看做是一个主线程加上一个任务队列(task queue)。同步任务就是放在主线程上执行的任务,异步任务是放在任务队列中的任务。所有的同步任务在主线程上执行,形成一个执行栈;异步任务有了运行结果就会在任务队列中放置一个事件;脚本运行时先依次运行执行栈,然后会从任务队列里提取事件,运行任务队列中的任务,这个过程是不断重复的,所以又叫做事件循环(Event loop)。
(1)什么是进程?
进程是CPU进行资源分配的基本单位
(2)什么是线程?
线程是CPU调度的最小单位,是建立在进程的基础上运行的单位,共享进程的内存空间。
那么我们可以得出结论:
1、进程是会占用系统资源;2、一个进程内可以存在一个或者多个线程,这就是单线程和多线程;3、无论是单线程还是多线程都是在一个进程内。
关于微任务和宏任务在浏览器的执行顺序是这样的:执行一只task(宏任务),执行完micro-task队列 (微任务)。如此循环往复下去。
常见的 task(宏任务)比如:setTimeout、setInterval、script(整体代码)、 I/O 操作、UI 渲染等。
常见的 micro-task 比如: new Promise().then(回调)、MutationObserver(html5新特性) 等。
未完待续
增加块级作用域,在块级作用域中可以使用
不允许在同作用域中重复声明同一个变量
快速获取数组或对象中的元素或属性,而无需使用对索引或属性一个一个赋值了
let [a, b, c] = [1, 2, 3];
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; // foo = 'aaa' // bar = 'bbb'
扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1,2,3]) //1 2 3
ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) {
//不使用rest运算符的话,values=1,且执行下面的代码会报错,以为一个数并不是可以迭代的iterator对象
//使用之后,values= [1,2,3]
let sum = 0;
for(let val of values) {
sum += val;
}
return sum;
}
console.log(add(1,2,3)) //6
ES6的基本数据类型:Number String Boolen Object Null Undefined 还加上了Symbol
Symbol:表示独一无二的值,最大的用处是定义对象的唯一属性名
let sy = Symbol("KK");//Symbol()写法没有登记机制
console.log(sy); // Symbol(KK)
typeof(sy); // "symbol"
// 相同参数 Symbol() 返回的值不相等
let sy1 = Symbol("kk");
sy === sy1; // false
如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。
let sy = Symbol('kk');
let sy1 = Symbol('yy')
let my = {[sy]:'key1',[sy1]:'key2'}
console.log(my);//{ [Symbol(kk)]: 'key1', [Symbol(yy)]: 'key2' }
Object.getOwnPropertySymbols(my);//[ Symbol(kk), Symbol(yy) ]
Reflect.ownKeys(my);//[ Symbol(kk), Symbol(yy) ]
Symbol.for() 类似单例模式,首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值(相当于浅拷贝),若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。 调用Symbol.for("cat")
30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")
30 次,会返回 30 个不同的 Symbol 值。
Symbol()写法没有登记机制 ,Symbol.for()有登记机制。
let y1 = Symbol('yellow');
let y2 = Symbol.for('yellow');//Symbol.for()创建的值会被登记在全局环境中供搜索,而Symbol()创建的值不会被登记,所以Symbol.for("yellow")依然会新创建一个Symbol值,而不是返回Symbol("yellow")创建的值。
let y3 = Symbol.for('yellow');//这是全局环境中也已经存在这个Symbol值,所以直接返回y1
console.log(y1 == y2) //false 创建的环境不同
console.log(y3 == y2) //true
Symbol.keyFor()方法返回一个已经登记的Symbol的key
let b1 = Symbol.for('blue');//全局中已登记
console.log(Symbol.keyFor(b2)) //"blue"
let b2 = Symbol('blue2');//未登记
console.log(Symbol.keyFor(b2)) //undefined
Set:允许存储任何类型值,不重复的值的集合。
- add(value):添加某个值,返回Set结构本身。
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
- has(value):返回一个布尔值,表示该值是否为Set成员。
- clear():清除所有成员,没有返回值。
WeakSet:与Set一样,不重复的值的集合;WeakSet 的成员只能是对象,而不能是其他类型的值,弱引用(垃圾回收机制会自动回收该对象所占用的内存,不考虑WeakSet的引用 ), 只有add
、delete
、has
三个方法
Map:一种更完善的 Hash 结构实现,键值对的集合。
- size属性 map.size获取map大小
- set(key,value):设置新的键值对
- get(key):获取指定键的值
- has(key):是否有该键名
- delete(key):删除指定键值对
- clear(): 整个map清空
遍历方法:
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
WeakMap: 是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。 拥有get
、has
、delete
方法。
一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for…of循环遍历它的成员。也就是说,for…of循环内部调用的是数据结构的Symbol.iterator方法。
for…of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。
Promise被设计用于改善JS中的异步编程,与事件及回调函数相比,在异步操作方面提供了更多的控制权与组合性。可以避免层层嵌套的回调函数(回调地狱)。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
状态:挂起(pending)、已完成(resolve)、已拒绝(reject)
缺陷:无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
具体:
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去在.then()中接受;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去在.then()中接受。
var p = new Promise(function(resolve, reject) {
//异步任务代码
})
p.then(function(res){
//成功的结果
console.log(res)
},function(err){
//失败的结果
console.log(err)
})
.then(...)
.then(...)//层层回调,使用链式,不再是嵌套形成回调地狱
Promise.all() 将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
Promise.all([p1,p2,p3]).then((res) => {
console.log(res);//成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的
}).catch((err) => {
console.log(err);
})
Promise.race() 将多个Promise实例包装成一个, 哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
Promise.race([p1,p2,p3]).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
})
与普通函数区别
一是在 function 后面,函数名之前有个 *
函数内部有 yield 表达式。
next()
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值。
function* func() {
console.log('1');
var x = yield '1';//第一个next()执行到这里停止,有参数可接受,x='hh'
console.log('2'+x);
yield '2';//第二个next执行到这里,
console.log('3');
return '3';
}
var f = func();
f.next();//1 {value:'1',done:false}
f.next('hh');//2 {value:'2',done:false}
f.next();//3hh {value:'3',done:true}
f.next();//{value:undefined;done:true}
应用场景
Generator 函数的语法糖,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。
async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
//一般是和promise接口调用的时候使用,可以简化代码
async function xx() {
const data = await $http.get('...');
}
类的定义
class Foo {
constructor(x,y) {
this.x = x;
this,y = y;
}
add() {
return this.x + this.y;
}
}
var f = new Foo(1,2);
console.log(f.x); // 1
console.log(f.add()); //3
类的继承
class Bar extends Foo {
constructor(x,y,z) {
super(x,y);//必写!继承父类的属性和方法
this.z = z;
}
addAll() {
return super.add() + this.z;
}
}
var b = new Bar(1,2,3);
console.log(`${b.x} , ${b.z}`); //1 , 3
console.log(b.addAll());//6
Proxy ( 代理 ) 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
var proxy = new Proxy(target, handler);
//target:要拦截的目标
//handler:定制拦截行为
脏检查
,watcher越多越慢。(脏检查: 在angular中,数据绑定到html中后,这个数据改变了,需要检查刷新,这就触发了脏检查 )实现一个监听器:Observer,对数据对象进行遍历,包括子属性对象的属性,利用Object.defineProperty()对属性加上setter和getter,实时监听到数据的变化。(阅读源码可知道,object.defineProperty()对数组不行,所以在2.x版本的源码中是重写了array的几个方法,将数据变为响应式加入到dep中,但是3.x的版本中是使用的 Proxy: 就没以上的问题,原生支持监听数组变化,并且可以直接对整个对象进行拦截)
实现一个解析器:Compile,解析Vue模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据变动就使用更新函数更新。
实现一个订阅者:Watcher,是Observer和Compile之间通讯的桥梁,主要任务是订阅Observer中属性变化的消息,收到属性值变化的消息时候触发Compile中对应的更新函数。
通过建立虚拟dom树document.createDocumentFragment(),方法创建虚拟dom树。
一旦被监测的数据改变,会通过Object.defineProperty定义的数据拦截,截取到数据的变化。
截取到的数据变化,从而通过订阅—发布者模式,触发Watcher(观察者),从而改变虚拟dom的中的具体数据。的值,完成双向绑定
最后,通过更新虚拟dom的元素值,从而改变最后渲染dom树
应用场景:
v-if适用于在运行时很少改变条件,不需要频繁切换条件的场景。v-show适用于频繁切换条件的场景。
vue-router 有 3 种路由模式:hash、history、abstract,对应的如下所示:
hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 ‘#search’:
https://www.word.com#search
hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;
使用 hashchange
事件来监听 hash 值的变化,从而对页面进行跳转(渲染)
history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
其中做最主要的 API 有以下两个:history.pushState()
和 history.repalceState()
。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
我们可以使用 popstate
事件来监听 url 的变化,从而对页面进行跳转(渲染),注意上面两个主要的api不会触发这个事件。
abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
对比hash和history:通过history api,我们丢掉了丑陋的#,但是它也有个问题:不怕前进,不怕后退,就怕刷新,f5,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的,不玩虚的。在hash模式下,前端路由修改的是#中的信息,而浏览器请求时是不带它玩的,所以没有问题.但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,会分分钟刷出一个404来。
生命周期 | 描述 |
---|---|
beforeCreate | 组件实例被创建之初,组件的属性生效之前 |
Created | 组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用 |
beforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用 |
Mounted | el被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子 |
beforeUpdate | 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前 |
Update | 组件数据更新之后 |
Activited | keep-alive 专属,组件被激活时调用 |
Deactivated | keep-alive 专属,组件被销毁时调用 |
beforeDestory | 组件销毁前调用 |
Destoryed | 组件销毁后调用 |
加载渲染过程
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
子组件更新过程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
父组件更新过程
父 beforeUpdate -> 父 updated
销毁过程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
keep-alive是vue内置的一个组件,可以使被包含的组件保留状态,避免重新渲染,有以下特性:
//vue的组件模板中,js部分,data是函数
data () {
return {...}
}
原因:组件是用来复用的,且js里对象是引用关系,如果组件中的data是一个对象,那么子组件里面的data都会互相影响,但是放在函数中,每个实例就可以维护一份被返回对象的独立拷贝,组件实例之间的data属性值不会相互影响;但是new Vue实例不会复用,所以直接是data:{}
操作表单的时候,使用v-model进行双向绑定,实际上相当于v-bind+v-on的结合;
Vue.directive(指令名称,指令实现的函数)
Vue.directive('nm',function(el,binding,vnode) {
//el:指令所绑定的元素
//binding:一个对象,包含指令信息,打印出来看
//vnode:虚拟节点
el.style.color = binding.value
})
指令周期:
Vue.directive('my-directive', {
bind: function () {},
inserted: function () {},
update: function () {},
componentUpdated: function () {},
unbind: function () {}
})
Axios是一个基于promise的http库,支持promise的api,且对于响应默认转换为json类型,安全性高,客户端支持防御[CSRF](#2.2 跨站请求伪造),默认不带cookies。ajax技术实现了网页的局部数据刷新,axios实现了对ajax的封装。
//vue中使用axios
axios.defaults.baseURL = 'xxx' //接口的根路径
//request
axios.interceptors.request.use((config)=>{
console.log(config);
//将token绑定在请求数据的头部,这样服务器可以有选择的返回数据,只对有效的请求返回数据
config.headers.Authorization = window.sessionStorage.getItem("token");
return config;
})
//response
axios.interceptors.response.use(config =>{
return config;
})
Vue.prototype.$http = axios; //$http是axios别称
//使用示例----------------
this.$http.put()/get()/post()/delete()
SPA:single-page-application,在页面初始化的时候加载相应HTML、JS和CSS。一旦加载完成,页面不会再因为操作而重新加载或者跳转,取而代之的是使用路由机制实现页面的改变。
虚拟 DOM 的实现原理主要包括以下 3 部分:
对dom树同一层节点进行对比,将算法复杂度降低到o(n);
key是vue中vnode的唯一标记,通过key,diff操作更加准确快速
Vue的diff过程是:oldCh 和 newCh 各有两个头尾的变量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex,它们会新节点和旧节点会进行两两对比,即一共有4种比较方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex,如果以上 4 种比较都没匹配,如果设置了key,就会用 key 再进行比较,在比较的过程中,遍历会往中间靠,一旦 StartIdx > EndIdx 表明 oldCh 和 newCh 至少有一个已经遍历完了,就会结束比较。
props:父组件通过属性绑定将值传递给子组件,子组件使用props数组进行接收;
单向传递,父组件当前属性值更改,会影响子组件,反之不会。**但是如果传递的是引用类型(比如数组),父子组件中该数据变化都会互相影响。**可以使用深拷贝解决。
<div id="app">
<hello-world :title='msg' alter='其他' ></hello-world>
</div>
//然后在子组件上使用props接受title
const helloWorld = {
data:function(){
return {}
},
props:['title','alter'],
template:`父组件给我传递的东西{{title}}我自己的数据{{text}}`
}
-
e m i t : emit: emit:emit方法传入事件的名称和参数(可以多个)。然后在父组件上使用v-on(@事件名称)监听,参数一个的时候,使用$event接受,参数多个使用arguments数组接受。
<div id="app">
<hello-world @text-add="handle(arguments)"></hello-world>
</div>
const hellowWorld = {
data:function(){return {}},
template:``
}
-
EventHub:组件之间数据交互;通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级
var eventHub = New Vue();
//事件的开启与销毁
eventHub.$on('name',(数据)=>{...});//写在指定组件的mounted周期
eventHub.$off('name');
//事件的触发
eventHub.$emit('name',数据)//写在指定组件的methods里
-
Vuex:适用于 父子、隔代、兄弟组件通信
-
a t t r s / attrs/ attrs/listeners: 仅仅是传递数据 的时候,使用Vuex太复杂,就使用 a t t r s / attrs/ attrs/listeners,
$attrs:包含绑定在子组件上,但是没有在props声明的属性
l i s t e n e r s : 包 含 了 父 作 用 域 中 的 ( 不 含 . n a t i v e 修 饰 器 的 ) v − o n 事 件 监 听 器 ( 非 原 生 事 件 ) 。 它 可 以 通 过 v − o n = " listeners: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器(非原生事件)。它可以通过 v-on=" listeners:包含了父作用域中的(不含.native修饰器的)v−on事件监听器(非原生事件)。它可以通过v−on="listeners" 传入内部组件 。
<com-child :foo='foo' :boo='boo' :title='title'></com-child>
//子组件中
props:['foo'],//porps里面只有foo,所以$attrs里面是boo,title属性值,$attrs可以直接在子组件使用,也可以使用v-bind="$attrs"继续传给下一个子组件
inheritAttrs: false,//配合使用!
-
provide/inject
祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量 ; 使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
provide 和 inject 绑定并不是可响应的。这是刻意为之的 ,就是说provide中的属性更改后,inject里面的值不会更新。 传入了一个可监听的对象,那么其对象的属性还是可响应的。
//a.vue 父组件
export default {
provide: {name:'a-name'},
//下面这种可以响应
provide () {
return {
NAME: this
}
},
methods:{
upprovide(){
this.asideW = '500px'; // 修改 属性值得变化,可以传递到 子孙后代的任意组件 同时响应变化
}
}
}
//b.vue 子孙组件
export default {
inject: ['name','NAME'],
mounted () {
console.log(this.name);//a-name
},
methods: {
upprovide(){
//这里修改之后父也会响应数据的变化
this.NAME.asideW = '200px';
}
}
}
-
p a r e n t / parent/ parent/children/$roo/ref : 无法在跨级或兄弟间通信。
p a r e n t / parent/ parent/children:访问父, this. p a r e n t 查 找 当 前 组 件 的 父 组 件 / 访 问 子 实 例 , t h i s . parent查找当前组件的父组件 /访问子实例, this. parent查找当前组件的父组件/访问子实例,this.children查找当前组件的直接子组件,可以遍历全部子组件
this. r o o t 查 找 根 组 件 , 并 可 以 配 合 root查找根组件,并可以配合 root查找根组件,并可以配合children遍历全部组件
ref:普通的dom上使用指向的是dom元素,在子组件上使用指向子组件 ,this.$ref.name 访问指定名称的组件
//childred
export default {
data () {
return {name:'children-title'}
},
methods: {
say() {
console.log('hello')
}
}
}
//parent
<template>
<com-a ref='comA'></com-a>
</template>
<script>
export defualt {
mounted () {
const comA = this.$ref.comA;
console.log(comA.name);//√
comA.say();//√
}
}
</script>
14.17 Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)
只有mutations可以修改state中的数据
let store = new Vue.Store({
state : {},//全局数据,使用mapState读取数据
getters : {},//类似computed,使用mapGetters
mutations : {},//同步方法,更改state数据,mapMutations
actions : {}//异步方法,驱动mutations操作,this.$store.dispatch('actionName')state
})
![※前端面试--知识总结_第25张图片](http://img.e-com-net.com/image/info8/79cf46d9d61a42a98c86fb46d1b087e3.jpg)
vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,****具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。****
14.18 Vue SSR
服务端渲染:将同一个组件渲染为服务端的HTML字符串,将他们直接发送到浏览器,最后将这些静态标记激活为客户端上可以交互的应用程序。
- 优点:更好的SEO;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
- 缺点:更多的开发条件限制;更多的服务器负载。
14.19 Vue优化
查看详细解答看链接~
14.20 Vue3.0更新
-
监测机制的改变
vue3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:
·只能监测属性,不能监测对象
·检测属性的添加和删除;(由全局api完成)
·检测数组索引和长度的变更;
·支持 Map、Set、WeakMap 和 WeakSet。
-
模板
模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
-
对象式的组件声明方式
3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。
15.工程化
15.1 git/svn
- git:四大区域:工作区,暂存区,本地版本库,远程仓库
-
git工作流程:
a) 在工作目录中修改某些文件
b) 对修改后的文件进行快照,然后保存到暂存区域
c) 提交更新,将保存在暂存区域的文件快照永久转储到Git目录中
-
常用指令
a) git config
:设置用户名、邮箱等配置文件
b) git init
:创建仓库
c) git branch
:查看当前分支
d) git checkout
:切换分支
e) git status
:查看仓库的状态
f) git diff 文件名
:与上次相比修改了哪些内容
git diff commitA commitB
:对比两次commit之间的差异
g) git add 文件名
:将添加的文件放到暂存区中
h) git commit -m “注释”
:将暂存区的内容提交到本地仓库
i) git clone 远程仓库地址
:将远程仓库克隆到本地(没有本地仓库时)
j) git pull 远程仓库地址
:将远程 repository 里新的 commit 数据(如有的话)下载过来,并且与本地代码merge。(有本地仓库时)
k) git fetch 远程仓库地址
:相当于是从远程获取最新版本到本地,不会自动merge
-
与SVN的区别
a) Git是分布式的,SVN是集中式的(Git操作处理速度快,不依赖网络)
b) Git有强大的分支管理功能,而SVN较好的权限管理功能。
SVN
- 优点:管理方便,逻辑清晰,符合一般思维;易于管理,集中式更加保障安全;代码一致性高;适合开发人数不多的项目;
- 缺点:服务器压力大,数据库容量暴增;不能连接到服务器时不能工作;不适合开源开发。
Git
- 优点:适合分布式开发;公告服务器和数据量不大;速度快、灵活;任意两个开发者之间可以容易解决冲突;支持离线工作;
- 缺点:学习周期长;不符合常规思维;代码保密性差,clone之后源码就暴露了;
15.2 git rebase–git merge
git实现合并的两种方法;git rebase
:复位基底,将两个分支融合成一个新的线性提交;git merge
,把本地代码和取得的远程仓库代码合并,会生成一个新的节点,之前的提交会分开显示。
15.3 git revert
如果我们想撤销之前的某一版本,但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法。比如,想要撤销版本二,但又不想影响版本三的提交,就可以用 git revert
命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。
![※前端面试--知识总结_第31张图片](http://img.e-com-net.com/image/info8/dd57151d07234a9c86fb989a8c950694.jpg)
15.4 git reset
如果想恢复到之前某个提交的版本,且那个版本之后提交的版本我们都不要了,就可以用这种方法。
![※前端面试--知识总结_第32张图片](http://img.e-com-net.com/image/info8/ac082d71a5a048d4b8acd2d6785f476c.jpg)
git add
过程中提交了不想提交的文件,可以使用git reset filename
将文件从暂存区删除。
git reset和 git revert:
两个命令的目的是一样的,但是做法不一样,revert会以创建新的commit方式去撤销commit,可以保留之前的commit历史。
15.5 git pull
git pull = git fetch + git merge
拉取远程分支上的代码并合并到本地分支,目的是消除冲突
git pull --rebase = git fetch + git rebase
15.6 fork
-
fork:对仓库的克隆,克隆一个仓库并且允许对该仓库进行改变,但是不会影响原始项目;一般适用于更改别人的项目,做贡献,或者使用别人的项目开始项目。
eg : 在github上发现别人项目的bug,可以fork+pull request对项目进行修复,当拥有者认可之后会更新到原始项目中去。
-
Clone:克隆项目到本地。
-
Branch: 是一种机制,用于处理单一存储仓库中的变更,并最终目的是用于与其他部分代码合并。
15.7 Babel
babel是转译器,把同种语言的高版本规则翻译成低版本规则
和编译器类似,babel的转译过程也分为三个阶段:parsing、transforming、generating(解析,转换,生成),以ES6代码转译为ES5代码为例,babel转译的具体过程如下:ES6代码输入 ==》 babylon进行解析 ==》 得到AST==》 plugin用babel-traverse对AST树进行遍历转译 ==》 得到新的AST树==》 用babel-generator通过AST树生成ES5代码
15.8 Webpack
webpack是一个前端开发打包工具。将所有的资源都看作模块,可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等需要时再异步加载。通过 loader的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、AMD 模块、ES6 模块、CSS、图片、JSON、Coffeescript、LESS 等。
Gulp和webpack两者区别:
侧重点不同:Gulp侧重于前端开发的整个过程控制。例如刷新页面,雪碧图,压缩js等;Webpack侧重于模块打包,将资源都看作模块,使用loader和plugins对资源进行处理。
15.9 模块化发展
解决项目中全局变量被污染问题;开发效率高;方便维护代码和复用;解决文件的依赖问题,不用在意文件之间引用时候的顺序。
15.10 RequireJS
RequireJS是js模块加载器。
- 实现js文件的异步加载,避免网页失去响应
- 管理模块之间的依赖性,便于代码维护和编写
- 基于AMD模块化机制,让前端代码实现模块化;
main.js文件只要是加载RequireJS的配置项:
- baseUrl:模块默认加载路径
- paths:自定义模块加载路径
- shim:定义模块之间的依赖关系
16.计算机基础
16.1 操作系统
-
四个特性
- 并发:并发指宏观上在一段时间内可以同时运行多个程序。并行指同一时刻能运行多个指令。
- 共享:系统中的资源可供内存中多个并发执行的进程共同使用
- 虚拟:把一个物理实体变成若干个逻辑实体
- 异步:进程的执行不是一次性的,而是走走停停
-
进程与线程
- 进程:资源分配和调度的的基本单位
- 线程:独立调度的最小单位
- 联系: 一个程序至少有一个进程,一个进程至少有一个线程。 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
- 进程通信:进程间传输信息。方式有管道,消息队列,信号量,共享存储(允许多进程共享一个给定的存储区),套接字
-
死锁
- 概念:计算机系统中,如果系统的资源分配策略不当,就会导致进程因为竞争资源不当产生死锁。
- 原因:系统资源不足;进程推进顺序不合理;资源分配不当
- 导致的必要条件:
- 互斥条件:一个资源每次只能被一个资源使用
- 请求与保持:1个进程因为请求而阻塞,对已经获取到的资源保持不放
- 不剥夺条件:进程已经获取的资源,在没有使用完之前不可以剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系;
-
内存管理
-
块式管理
把主存分为一大块、一大块的,当所需的程序片断不在主存时就分配一块主存空间,把程序片断载入主存,就算所需的程序片度只有几个字节也只能把这一块分配给它。这样会造成很大的浪费,但易于管理。
-
页式管理
把主存分为一页一页的,每一页的空间要比一块一块的空间小很多,显然这种方法的空间利用率要比块式管理高很多。
-
段式
把主存分为一段一段的,每一段的空间又要比一页一页的空间小很多,这种方法在空间利用率上又比页式管理高很多,但是也有另外一个缺点。一个程序片断可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上
-
段页式管理(最常用)
结合了段式管理和页式管理的优点。把主存分为若干页,每一页又分为若干段。
17.数据库
17.1 sql查询语句
//基本查询
SELECT name,age FROM table01; //检索指定列
selct * FROM table; //检索所有列
SELECT DISTINCT year FROM table;//检索不同的行,做集合或者说滤重
SELECT name FROM table LIMIT 5;//返回的结果不多于5行
SELECT name FROM table LIMIT 5,5;//从第五行开始检索,检索出的结果不多于5行
SELECT name FROM table ORDER BY name ASC;//检索出的结果用指定的列进行排序,排序规则是升序(升序是默认的,指定降序是DESC)
SELECT id,name FROM table ORDER BY id,name;//检索结果先用id排序,再在同id上用name再排序
SELECT age FROM table ORDER BY age DESC LIMIT 1;//通过排序和LIMIT可以寻找最大最小值,当然也可以使用sql里面的max min方法
//数据过滤
where = <> != < <= > >= BETWEEN IN NOT LIKE
SELECT NAME,AGE FROM TABLE WHERE NAME='ASTER' AND/OR AGE<=20;//条件查询
SELECT ID FROM TABLE WHERE ID<>1000;//不等于
SELECT AGE FROM TABLE WHERE AGE BETWEEN 5 AND 20 或者 SELECT AGE FROM TABLE WHERE AGE (NOT) IN(5,20);//范围
SELECT NAME FROM TABLE WHERE ID IS NULL;//空值
SELECT name from tab where name (not) like 'a%';//搜索列中的指定模式,like后面的东西是指定模式。该句表示搜索name以a开头的列
//pattern指定模式的写法
//%通配符 :%表示0或多个字符
'a%' --- 以a开头 a ax axy axyz ...
'%a' --- 以a结尾 a xa xya xyza ...
'%a%' --- 包含a a xa ya xay ...
//_ 通配符:_表示单个字符
'_空调' --- X空调
'__空调' --- XY空调
//正则 REGEXP
select name from tab where name REGEXP '表达式'
//创建计算字段
//Concat
SELECT CONCAT(id,'--',name) FROM tab;//检索出的结果是 字段名CONCAT(id,'--',name):结果id--name
//AS使用别名
SELECT CONCAT(id,'--',name) AS title FROM tab;//字段名 title : 结果id--name
//执行算术计算 + - * /
select num*price as money from tab;
//使用内置函数
AVG() COUNT() MAX() MIN() SUM()
//组合 GROUP BY
//过滤分组 HAVING
SELECT id,COUNT(*) AS num FROM tab GROUP BY id HAVING COUNT(*) >=2; //过滤出条数大于等于2的分组
//使用子查询 组合多个查询语句
select name from tab1 where age in (select age from tab2 where age=12)
sql语句顺序
select .. from .. where .. group by .. having .. order by .. limit ..
17.2 MySQL
-
索引:帮助MySQL高效获取数据的数据结构
-
创建索引:CREATE INDEX
为给定图表创建索引
-
索引结构分类:B+Tree HASH 等值查询使用Hash,范围查询使用B+
-
B+Tree索引:平衡多叉树。与红黑树比较有下面的特点:
- 更少的查找次数:平衡树查找的时间复杂度等于树高h,h = logdN,d为每个节点的出度。红黑树的出度为2,B+的出度一般都很大。所以红黑树的树高也就更大,需要进行查找的次数也更多。
- 利用磁盘预读特性:为了减少磁盘IO,磁盘一般不是严格按需读取,而是选择预读取。在预读过程中磁盘会按顺序读,需要时间很少,速度很快。同时数据库系统将索引的一个节点设置为一页大小,因此每次io读取就会载入一个节点。
-
存储引擎:
-
InnoDB:是MySQL的默认事务型存储引擎,只有它不满足时才会使用其他的。
-
MyISAM:设计简单,数据紧密存储,对于只读数据或者表比较小,可以容忍修复操作,可以使用它,不支持事务,不支持行级锁,只能对整表加锁。读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入
InnoDB
MyISAM
事务
事务型,可以使用Commit和Rollback
非事务型
并发
支持行级锁
支持表级锁
外键
支持
不支持
备份
支持在线热备份
不支持
崩溃恢复
崩溃后损坏概率低
崩溃后损坏概率高,恢复速度慢
其他特性
支持压缩表和空间数据索引
-
事务
- 事务是由一步或者几步数据库操作序列组成逻辑执行单元,这些操作要么全部执行,要么全部不执行。一般是增删改操作。(区分程序和事务:一段程序种包含多个事务)
- 特点:
- 原子性:事务中最小的执行单位
- 一致性:事务执行的结果,必须是使得数据库从一个一致性状态,变到另一个一致性状态;
- 隔离性:各个事务执行互不干扰。
- 持续性:持久性,事务一旦提交,对数据做的更改都会被记到永久存储器(物理数据库)
-
并发一致性问题
在并发环境下事务的隔离性很难保证,因此会出现并发一致性问题:1)丢失修改 2)读脏数据 3)不可重复读 4)幻影读
-
封锁
行级锁和表级锁
封锁类型:
读写锁 【1】排他锁 x锁,写锁 【2】共享锁 S锁,读锁
意向锁
封锁协议:
- 一级:事务T要修改数据A时加上X锁,直到T结束才释放,解决丢失修改问题。
- 二级:一级基础上,要求读数据A加上S锁,读完释放,解决读脏数据问题。
- 三级:二级基础上,读取数据A加上S锁,事务结束释放,解决不可重复读的问题。
-
隔离级别
- 未提交读:事务中的修改,即使没有提交,对其他事务也是可见的。
- 提交读:事务中的修改,必须提交后,对其他事务才可见。
- 可重复读:保证在同一个事务中多次读取同样数据结果一致。
- 可串行化:强制事务串行执行。
-
关系型数据库设计理论
- 函数依赖:A->B表示B函数依赖A
- 异常:
- 冗余数据:数据库表中相同数据出现两次
- 修改异常:修改一个记录中的数据,相关的记录中没有同步修改
- 删除异常:删除一个信息,丢失了其他的
- 插入异常:无法插入信息
- 范式 ??
- 第一范式:属性不可分。【原子性,字段不可再分割】
- 第二范式:每个非主属性完全函数依赖于键码,通过分解实现。【完全依赖,没有部分依赖】
- 第三范式:非主属性不传递函数依赖于键码。【没有传递依赖,保持每列都跟主键直接相关】
-
ER图
实体-关系图,有三个组成部分:实体,属性和关系
实体-数据表,属性-数据列,关系-数据表与数据表之间的关系
-
设计表应考虑的因素
·作用是什么/至少包含的数据,这些数据属于哪些实体对象
·E-R模型
·考虑每一列的数据类型
·允许为空和默认值,原则是尽量少允许为空
·主键问题
·约束和规则
·外键问题
·考虑是否使用索引
18.数据结构
排序算法
时间复杂度 O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n)
![img](http://img.e-com-net.com/image/info8/2610f1445042404dacf7438b809e194f.jpg)
19.代码
19.1 call/apply/bind
call 和 apply是 Function 对象的原型方法,它们能够将特定函数当作一个方法绑定到指定对象上并进行调用,call传递的参数是多个参数,apply传递的参数是数组元素
bind返回一个绑定了this的函数.
function.call(thisobj, args...);
function.apply(thisobj, [args]);
function.bind(thisobj,args...);//args可以传递部分
//将函数function绑定在thisobj上使用。
Function.prototype.mycall = function (obj){
console.log('this is mycall')
var obj = obj || window;
obj.fn = this;
var args = [...arguments].slice(1);//调用call的时候,第一个参数是this对象,后面的才是传递的参数
var result = obj.fn(...args);
delete obj.fn;
return result;
}
function add(x,y) {
return x+y;
}
console.log(add.mycall(this,10,20));//this is my call 30
Function.prototype.myapply = function (obj) {
console.log('this is my apply');
var obj = obj || window;
obj.fn = this;
var result = null;
if(arguments[1].length != 0) {
result = obj.fn(...arguments[1])
}else {
result = obj.fn();
}
delete obj.fn;
return result;
}
console.log(add.myapply(this,[15,25]))//this is my apply 40
Function.prototype.mybind = function (obj) {
console.log('this is mybind')
if(typeof this != 'function') {
throw new TypeError('error type');
}else{
var _this = this;
var args = [...arguments].slice(1);
return function F(){
if(this instanceof F){
return new _this(...args, ...arguments);
}else{
return _this.apply(obj,args.concat(...arguments));
}
}
}
}
var myadd = add.mybind(this,10)//args可以只传递部分
console.log(myadd(20))
19.2 防抖节流
防抖:将几次操作合并为一次。
//一般是设置一个计时器,事件触发之后,不立刻执行,在延迟时间之后再触发。如果时间内被触发计时器就重新开始计时。如果时间内没有再次触发滚动事件,那么就执行函数。
function debounce(fu,delay) {
//计时器
let timer = null;
return function() {
// this 指向不变以及依旧能接受到 e 参数。
let context = this;
let args = arguments;
if(timer) {clearTimeout(timer);}
timer = setTimeout(() => {
fn.apply(context,args)
},delay)
}
}
节流:连续触发函数,n秒内只会执行一次
//1==时间戳版本 2==定时器版本
function throttle(type,fn,delay) {
if(type==1) {var prev = 0;}
else if(type==2) {var timer = null;}
return function() {
let context = this;
let args = arguments;
if(type==1){
let now = Date.now();
if(now-prev > delay){
fn.apply(context,args);
prev = now;
}
}else if(type==2){
if(!timer){
timer = setTimeout(()=>{
timer = null;
fn.apply(context,args)
},delay)
}
}
}
}
19.3 Ajax
let xhr = new XMLHttpRequest();//实例化
xhr.open(method, url, async);//初始化
xhr.send(data);//发送请求
xhr.onreadystatechange = () => {//获取请求结果
if(xhr.readyStatus === 4 && xhr.status === 200) {
console.log(xhr.responseText;
}
}
19.4 设计模式
订阅-发布者模式:面试考
vue的emit on就是这个模式
19.5 快排
19.6 扁平化数组
Array.prototype.flat():该方法会按照指定的深度遍历递归数组,并将所有的元素与遍历到的数组元素合并为新的数组返回。该方法不会修改原有的数组
var mflat = function(arr, depth) {
let res = [],
depthArg = depth || 1,
depthNum = 0,
flatMap = (arr) => {
arr.map((element, index, array) => {
// if(Object.prototype.toString.call(element).slice(8, -1) === 'Array'){
if(Array.isArray(element)){
if(depthNum < depthArg){
depthNum++;
flatMap(element);
}else{
res.push(element);
}
}else{
res.push(element);
if(index === array.length -1) depthNum = 0;
}
});
};
flatMap(arr);
return res;
};
let arr = [1,2,3,[4,5,[6,7]]]
arrNew = mflat(arr,2);//[1,2,3,4,5,6,7]
flat()默认只会“拉平”一层 ,默认值是1,想要将多层嵌套的数组变成一维,可以使用Infinity
如果原数组有空位,flat()方法会跳过空位。
let arr = [1,2,[3,[4,5]]];
arrNew1 = arr.flat();//[1,2,3,[4,5]]
arrNew2 = arr.flat(Infinity);//[1,2,3,4,5]
19.7 回文字符串
-
双指针:
let left=0,right=len-1;
while(left<right){
if(s[left] != s[right]) return false;
left++;
right--;
}
19.8 一些算法总结
- 数组:出的功能:shift pop 入的功能:unshift push
一维数组,排序遍历,辅助数组,交换位置,二分查找(使用二分查找的之后时间复杂是log)
二维数组,行号和列号关系
-
字符串:
转成数组(split()),利用数组方法
辅助字符串
字符串方法 indexOf() lastIndexOf() replace()
统计字符串中各个字母
arr = Array(26).fill(0);
for(let i=0; i<len; i++) {
arr[s.charCodeAt(i) - 97] ++;//默认字符串里面都是小写字符
}
-
链表:
// Definition for singly-linked list.
function ListNode(val) {
this.val = val;
this.next = null;
}
//统计链表的长度
node = head;
while(node){
len++;
node = node.next;
}
//一般程序中,不要损坏原有的链表,先将链表进行备份,在备份的链表上进行修改
let dummy = new ListNode(-1);
dummy.next = head;
//在dummy上操作... ...
return dummy.next;
单向链表,循环,递归( 最先调用的函数会在递归过程中最后被执行,而最后调用的会最先执行 )
//翻转链表head newl=null;
while(head){
let next = head.next;
head.next = newl;
newl = head;
head = next;
}
//寻找链表的中点,使用快慢指针 slow=head,fast=head
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//环形链表,同样使用快慢指针,终究是会相遇的
-
树
//Definition for a binary tree node.
function TreeNode(val) {
this.val = val;
this.left = this.right = null;
}
三种遍历方式,宽度优先遍历
//二叉树的深度
let query=[root],n=0;
while(query.length){
let arr=[];
while(query.length){
let node = query.shift();
if(node.left) arr.push(node.left);
if(node.right) arr.push(node.right);
}
query=arr;
n++;
}
//中序遍历
var inorderTraversal = function(root) {
let curr=root,stack=[],list=[];
while(curr!=null || stack.length) {
while(curr!=null){
stack.push(curr)
curr=curr.left;//添加左子结点
}
curr=stack.pop();
list.push(curr.val);
curr=curr.right;
}
return list
};
-
栈和队列
stack堆栈,先进后出。进栈 push 出栈 pop
queue队列,先进先出, 只允许在表的前端(front)进行删除操作shift,而在表的后端(rear)进行插入操作 push
-
递归
排序
回溯法
动态规划 dp=new Array(); 然后寻找dp[i]与其他的关系
// if(Object.prototype.toString.call(element).slice(8, -1) === 'Array'){
if(Array.isArray(element)){
if(depthNum < depthArg){
depthNum++;
flatMap(element);
}else{
res.push(element);
}
}else{
res.push(element);
if(index === array.length -1) depthNum = 0;
}
});
};
flatMap(arr);
return res;
};
let arr = [1,2,3,[4,5,[6,7]]]
arrNew = mflat(arr,2);//[1,2,3,4,5,6,7]
**flat()默认只会“拉平”一层** ,默认值是1,想要将多层嵌套的数组变成一维,可以使用Infinity
**如果原数组有空位,flat()方法会跳过空位。**
let arr = [1,2,[3,[4,5]]];
arrNew1 = arr.flat();//[1,2,3,[4,5]]
arrNew2 = arr.flat(Infinity);//[1,2,3,4,5]
### 19.7 回文字符串
1. 双指针:
```js
let left=0,right=len-1;
while(left
19.8 一些算法总结
- 数组:出的功能:shift pop 入的功能:unshift push
一维数组,排序遍历,辅助数组,交换位置,二分查找(使用二分查找的之后时间复杂是log)
二维数组,行号和列号关系
-
字符串:
转成数组(split()),利用数组方法
辅助字符串
字符串方法 indexOf() lastIndexOf() replace()
统计字符串中各个字母
arr = Array(26).fill(0);
for(let i=0; i<len; i++) {
arr[s.charCodeAt(i) - 97] ++;//默认字符串里面都是小写字符
}
-
链表:
// Definition for singly-linked list.
function ListNode(val) {
this.val = val;
this.next = null;
}
//统计链表的长度
node = head;
while(node){
len++;
node = node.next;
}
//一般程序中,不要损坏原有的链表,先将链表进行备份,在备份的链表上进行修改
let dummy = new ListNode(-1);
dummy.next = head;
//在dummy上操作... ...
return dummy.next;
单向链表,循环,递归( 最先调用的函数会在递归过程中最后被执行,而最后调用的会最先执行 )
//翻转链表head newl=null;
while(head){
let next = head.next;
head.next = newl;
newl = head;
head = next;
}
//寻找链表的中点,使用快慢指针 slow=head,fast=head
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//环形链表,同样使用快慢指针,终究是会相遇的
-
树
//Definition for a binary tree node.
function TreeNode(val) {
this.val = val;
this.left = this.right = null;
}
三种遍历方式,宽度优先遍历
//二叉树的深度
let query=[root],n=0;
while(query.length){
let arr=[];
while(query.length){
let node = query.shift();
if(node.left) arr.push(node.left);
if(node.right) arr.push(node.right);
}
query=arr;
n++;
}
//中序遍历
var inorderTraversal = function(root) {
let curr=root,stack=[],list=[];
while(curr!=null || stack.length) {
while(curr!=null){
stack.push(curr)
curr=curr.left;//添加左子结点
}
curr=stack.pop();
list.push(curr.val);
curr=curr.right;
}
return list
};
-
栈和队列
stack堆栈,先进后出。进栈 push 出栈 pop
queue队列,先进先出, 只允许在表的前端(front)进行删除操作shift,而在表的后端(rear)进行插入操作 push
-
递归
排序
回溯法
动态规划 dp=new Array(); 然后寻找dp[i]与其他的关系
插入,冒泡,归并和快速排序比较