好久没有更新帖子了,最近一直在复习准备秋招,最近终于拿到了第一份意向书,终于有时间来整理一下自己最近的复习内容,以下知识点都是楼主在面试中被问到的问题,我做了几大块的分类,各位需要的小伙伴可以收藏一下。因为还在继续面试,所以还会不定时更新,因为掘金这边文章有字数限制,所以这边放出了HTML部分,CSS部分,JS部分的知识点,Vue,git,webpack,计网相关的知识点放在下一篇文章中juejin.im/post/687707… 。
HTML部分:
HTML标签的分类:
HTML文档标签
-
<!DOCTYPE>
:定义文档类型。 -
<html>
: 定义HTML文档。 -
<head>
:定义文档的头部。
<meta>
:定义元素可提供的有关页面的元信息,比如针对搜索引擎和更新频度的描述和关键字。
meta
标签共有两个属性,http-equiv
属性:相当于http头文件的作用,向浏览器传回一些有用的信息,使用content规定属性的内容比如name
属性:主要用来描述网页,content中的内容主要是便于搜索引擎机器人查找信息和分类信息用的。
<base>
:定义页面上的所有链接规定默认地址或默认目标。
<title>
:定义文档标题。
<link>
:定义文档与外部资源的关系。
<style>
:定义HTML文档样式信息。 -
<body>
:定义文档的主体。(脚本在非必需情况时在的最后)
<script>
:定义客户端脚本,比如javascript。
<noscript>
:定义脚本未被执行时的替代内容。(如文本)
按闭合特征分类:
- 闭合标签是指由开始标签和结束标签组成的一对标签,这种标签允许嵌套和承载内容,例如
<html></html>
、<p></p>
等。 - 空标签是指没有内容的标签,在开始标签中自动闭合。常见的空标签有:
<br>
、<hr>
、<img>
、<input>
、<link>
<meta>
。
按是否换行特征分类:
- 块级元素:块级元素是值本身属性为display:block的元素。
- 内联元素:内联元素是指本身属性为display:inline的元素,其宽度随元素的内容而变化。
块级元素的特点:
- 每个块级元素独占一行,从上到下排布。
- 块级元素可以直接控制宽度高度等盒子模型相关的css属性。
- 不设置宽度的情况下,块级元素的宽度是其父级元素内容的宽度。
- 不设置高度的情况下,块级元素的高度是他本身内容的高度。
常用块级元素:
内联元素的特点:
- 内联元素会和其他元素从左到右显示在一行中。
- 内联元素不能直接控制宽度高度以及盒子模型相关的css属性,可以设置内外边距的水平方向的值。也就是说,对于内联元素的margin和padding,只有margin-left/margin-right和padding-left/padding-right是有效的,竖直方向的margin和pading无效果。
- 内联元素的宽高是由其内容本身的大小决定的。
- 内联元素只能容纳文本或者其他内联元素,不允许在内联元素中嵌套块级元素。
常见的内联元素:
H5新特性(常用):
HTML5新增元素:
1)标签增删
8个语义元素 header
section
footer
aside
nav
main
article
figure
内容元素mark
高亮 progress
进度
新的表单控件calander
date
time
email
url
search
新的input类型 color
date
datetime
datetime-local
email
移除过时标签big
font
frame
frameset
2)canvas
绘图,支持内联SVG。支持MathML
3)多媒体audio
video
source
embed
track
4)本地离线存储,把需要离线存储在本地的文件列在一个manifest配置文件
5)web存储,localStorage
、SessionStorage
cookie sessionStorage localStorage区别:
Cookie属性详解
Name
字段:为一个cookie的名称
Value
字段:为一个cookie的值
Domain
字段:为可以访问此cookie的域名,二级域名可以获取顶级域名中的cookie,同二级域名之间不可相互获取,顶级域名不可获取二级域名的cookie
Path
字段:为可以访问此cookie的页面路径,只有此路径下的页面可以读取此cookie
Expires/Max-Age
字段:为此cookie的超时时间,若设置为一个时间,则当达到此时间之后,此cookie失效,不设置的话默认值为Session,即浏览器关闭后才会失效。
Size
字段:表示此cookie的大小
http
字段:表示此cookie的httponly属性,若此属性设置为true,则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问。
Secure
字段:表示设置是否只能通过https来传递此条cookie。
Cookie与webstorage:
- cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。
- cookie数据还有路径(path)的概念,可以限制。cookie只属于某个路径下存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如回话标识。
- webStorage虽然也有存储大小的限制,但是比cookie大得多,可以达到5M或更大
sessionStorage与localStorage:
- 数据的有效期不同:
- sessionStorage:仅在当前的浏览器窗口关闭有效;
- localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前一直有效,即使窗口和浏览器关闭。
- 作用域不同:
- sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;localStorage:在所有同源窗口都是共享的;
- cookie:也是在所有同源窗口中共享的。
HTTP状态码:
-
1xx(临时响应):表示临时响应并需要请求者继续执行操作的状态码。
- 100(继续)请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。
- 101(切换协议)请求者已要求服务器切换协议,服务器已确认并准备切换。
-
2xx (成功):表示成功处理了请求的状态码。
- 200(成功)服务器已成功处理了请求。通常,这表示服务器提供了请求的网页。如果是对您的 robots.txt 文件显示此状态码,则表示 Googlebot 已成功检索到该文件。
- 201(已创建)请求成功并且服务器创建了新的资源。
- 202(已接受)服务器已接受请求,但尚未处理。
- 203(非授权信息)服务器已成功处理了请求,但返回的信息可能来自另一来源。
- 204(无内容)服务器成功处理了请求,但没有返回任何内容。
- 205(重置内容)服务器成功处理了请求,但没有返回任何内容。与 204 响应不同,此响应要求请求者重置文档视图(例如,清除表单内容以输入新内容)。
- 206(部分内容)服务器成功处理了部分 GET 请求。
-
3xx (重定向):要完成请求,需要进一步操作。通常,这些状态码用来重定向。
- 300(多种选择)针对请求,服务器可执行多种操作。服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
- 301(永久移动)请求的网页已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。您应使用此代码告诉 Googlebot 某个网页或网站已永久移动到新位置。
- 302(临时移动)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来响应以后的请求。此代码与响应 GET 和 HEAD 请求的 301 代码类似,会自动将请求者转到不同的位置,但您不应使用此代码来告诉 Googlebot 某个网页或网站已经移动,因为 Googlebot 会继续抓取原有位置并编制索引。
- 303(查看其他位置)请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。对于除 HEAD 之外的所有请求,服务器会自动转到其他位置。
- 304(未修改)自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。如果网页自请求者上次请求后再也没有更改过,您应将服务器配置为返回此响应(称为 If-Modified-Since HTTP 标头)。服务器可以告诉 Googlebot 自从上次抓取后网页没有变更,进而节省带宽和开销。
- 305(使用代理)请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
- 307(临时重定向)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来响应以后的请求。此代码与响应 GET 和 HEAD 请求的 301 代码类似,会自动将请求者转到不同的位置,但您不应使用此代码来告诉 Googlebot 某个页面或网站已经移动,因为 Googlebot 会继续抓取原有位置并编制索引。
-
4xx(请求错误):这些状态码表示请求可能出错,妨碍了服务器的处理。
- 400(错误请求)服务器不理解请求的语法。
- 401(未授权)请求要求身份验证。对于登录后请求的网页,服务器可能返回此响应。
- 403(禁止)服务器拒绝请求。如果您在 Googlebot 尝试抓取您网站上的有效网页时看到此状态码(您可以在 Google 网站管理员工具诊断下的网络抓取页面上看到此信息),可能是您的服务器或主机拒绝了 Googlebot 访问。
- 404(未找到)服务器找不到请求的网页。例如,对于服务器上不存在的网页经常会返回此代码。
- 405(方法禁用)禁用请求中指定的方法。
- 406(不接受)无法使用请求的内容特性响应请求的网页。
- 407(需要代理授权)此状态码与 401(未授权)类似,但指定请求者应当授权使用代理。如果服务器返回此响应,还表示请求者应当使用代理。
- 408(请求超时)服务器等候请求时发生超时。
-
5xx(服务器错误):这些状态码表示服务器在处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错。
- 500(服务器内部错误)服务器遇到错误,无法完成请求。
- 501(尚未实施)服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
- 502(错误网关)服务器作为网关或代理,从上游服务器收到无效响应。
- 503(服务不可用)服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
- 504(网关超时)服务器作为网关或代理,但是没有及时从上游服务器收到请求。
- 505(HTTP 版本不受支持)服务器不支持请求中所用的 HTTP 协议版本。
前端优化方法:
方法分类:
- 降低请求量:合并资源,减少HTTP 请求数,minify / gzip 压缩, webPlazyLoad。
- 加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发。
- 缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存localStorage。
- 渲染:JS/CSS优化,加载顺序,服务端渲染,pipeline。
具体措施:
- 减少HTTP请求次数:CSS Sprites,js、CSS源码压缩、图片大小控制合适;网页Gzip、CDN托管、data缓存、图片服务器
- 前端模板JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数
- 用innerHTML替代DOM操作,减少DOM操作次数,优化JavaScript性能。
- 当需要设置的样式很多时,设置className而不是直接操作Style
- 少用全局变量、缓存DOM节点查找的结果。减少IO读取
- 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)
- 图片预加载,将样式表放在顶部,将脚本放在底部,加上时间戳。
- 防止内存泄漏:内存泄漏指任何对象在不再拥有或需要它之后仍然继续存在。垃圾回收器定期扫描对象,并计算引用了每个对象的其他的数量。如果一个对象的引用数量为0(没有其他对象引用过该对象),或对该对象的唯一引用是循环的,那么该对象的内存即可回收。
优雅降级与渐进增强:
浏览器输入网址到页面渲染全过程
渐进增强:一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后在针对高级浏览器进行效果,交互,追加功能达到更好的体验。
优雅降级:一开始就构建站点的完整功能,然后针对浏览器测试和修复,比如一开始使用css3的特性构建一个应用,然后逐步针对各大浏览器进行hack使其可以在低版本浏览器上正常浏览。
post和get区别:
- get参数通过url传递,post参数放在request body中。
- get请求在url中传递的参数是有长度限制的,而post没有。
- get比post更不安全,因为参数直接暴露在url中,所以不能用来传递敏感信息。
- get请求只能进行url编码,而post支持多种编码方式
- get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留。
- GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。
- GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
http与https有什么区别?
(1) https协议需要ca申请认证书,一般免费的较少
(2) http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议
(3) http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
(4) http的连接很简单,是无状态的,https协议是由ssl+http协议构建的可进行加密传输,身份认证的网络协议,比http安全
HTTP协议中缓存的处理流程:
缓存分为两种:强缓存和协商缓存,根据响应的header内容来决定。
- 强缓存是利用http头中的Expires和cache-Control两个字段来控制的,用来表示资源的缓存时间。强缓存中,普通刷新会忽略它,但不会清除它,需要强制刷新。浏览器强制刷新,请求会带上cache-Control :no-cache和pragma: no-cache
- 协商缓存就是由服务器来确定缓存资源是否可用,所以客户端与服务器端要通过某种标识来进行通信,从而让服务器判断请求资源是否可以缓存访问。
- 普通刷新会启用弱缓存,忽略强缓存。只有在地址栏或收藏夹输入网址、通过链接引用资源等情况下,浏览器才会启用强缓存,这也是为什么有时候我们更新一张图片、一个js文件,页面内容依然是旧的,但是直接浏览器访问那个图片或文件,看到的内容却是新的。这个主要涉及到两组协商缓存相关的header字段:Etag和If-None-Match、Last-Modified和If-Modified-Since。
浏览器的缓存过程:
浏览器请求某一资源时,会先获取该资源缓存的header信息,然后根据header中的cache-Control和Expires来判断是否过期。
- 若没过期则直接从缓存中获取资源信息,包括缓存的header的信息,所以此次请求不会与服务器进行通信。这里判断是否过期,则是强缓存相关。
- 如果显示已过期,浏览器会向服务器端发送请求,这个请求会携带第一次请求返回的有关缓存的header字段信息。 比如客户端会通过If-None-Match头将先前服务器端发送过来的Etag发送给服务器,服务会对比这个客户端发过来的Etag是否与服务器的相同, 若相同,就将If-None-Match的值设为false,返回状态304,客户端继续使用本地缓存,不解析服务器端发回来的数据,若不相同就将If-None-Match的值设为true,返回状态为200,客户端重新机械服务器端返回的数据;客户端还会通过If-Modified-since头将先前服务器端发过来的最后修改时间戳发送给服务器,服务器端通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回最新的内容,如果是最新的,则返回304,客户端继续使用本地缓存。
如何解决前端开发中的缓存问题
- 对开发者来说,只要关闭浏览器缓存就可以了,找到network,Disable cache选项,打钩即可取消缓存。
- 开发者可以关闭缓存,但是不能要求所有用户都进行此类操作,此时可以在引用的文件之后拼接随机数或者日期都可以,如css/index.css?v=0.0001,浏览器就会认为是新的请求,而不会使用缓存中的文件。由于每次资源的修改都要更新引用的位置,同时修改参数的值,所以操作起来不是很方便,除非是在动态页面比如jsp中用服务器变量(v=${sysRnd})
- 如果缓存问题出现在ajax请求中,则在ajax请求的地址追加随机数
- 直接ctrl+f5,这个办法能解决页面直接引用的资源更新的问题
- 使用浏览器的隐私模式开发;
- 如果资源引用的页面,被嵌入到了一个iframe里面,可以在iframe的区域右键单击重新加载该页面
html5如何及时更新缓存文件(js、css或图片)
- 后端接口页面设置
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
- html页面中设置meta标签的属性(meta标签的http-equiv属性语法格式是:<meta http-equiv="参数" content="参数变量值">)
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Pragma" content="no-cache">//是用于设定禁止浏览器从本地机的缓存中调阅页面内容,设定后一旦离开网页就无法从Cache中再调出
<meta http-equiv="Expires" content="0">
- 对于图片或css文件,可以在文件名后面添加一个随机数或标志位,例如
<link rel="stylesheet" href="./style/style.css?+Math.random()">
- 对于js文件也可以采取3的方法,或者在文件名后使用时间戳
<script language="javascript" src="UILib/Common/Common.js?time=new Date()">
输入URL到后发生了什么:
简单过程:
- DNS域名解析;
- 建立TCP连接;
- 发送HTTP请求;
- 服务器处理请求;
- 返回响应结果;
- 关闭TCP连接;
- 浏览器解析HTML;
- 浏览器布局渲染;
详细分析
构建请求的过程:
-
应用层进行DNS解析 通过DNS将域名解析成IP地址,在解析过程中,按照浏览器缓存,系统缓存,路由器缓存,ISP(运营商)DNS缓存,根域名服务器,顶级域名服务器,主域名服务器的顺序,逐步读取缓存,直到拿到IP地址。 这里使用DNS预解析,可以根据浏览器定义的规则,提前解析之后可能会用到的域名,使解析结果缓存到系统缓存中,缩短DNS解析时间,来提高网站的访问速度。
-
应用层生成HTTP请求报文 应用层生成针对目标服务器的HTTP报文请求,HTTP请求包括起始行,首部和主体部分。首部包括域名host,keep-alive,User-Agent,Accept-Endoding,Accept-Language,cookie等信息
-
传输层建立TCP连接 http协议使用的TCP协议。 将http请求报文按序号分为多个报文段,并对每个报文段进行封装,使用本地的随机tcp源端口简历到目标服务器的tcp80端口(https是443端口)的连接,TCP源端口和目的端口被加入到报文中。即协议数据单元,同时还会加入序列号,确认号,检验和等参数,共计添加20个字节的头部信息。 这里构建TCP连接请求会增加大量的网络延时。常用的优化方法如下:
1)资源打包,合并请求
2)多使用缓存,减少网络传输
3)使用keep-alive建立持久连接
4)使用多个域名,增加浏览器的资源并发加载数,或者使用http2的管道化连接的多路复用技术 -
网络层使用IP协议来选择线路。 处理来自传输层的数据段,将数据段装入数据包,填充包头,主要添加源和目的IP地址,然后发送数据。IP协议负责选择传送的路线,称为路由功能。
-
数据链路层实现网络相邻结点间可靠的数据通信 为了保证数据的可靠性,把数据包packet封装成帧,并按顺序传送各帧,对每个数据块计算CRC(循环冗余检验),防止数据包丢失,出错就重传。将数据包封装成帧,分为帧头和帧尾,帧尾是CRC校验部分,帧头主要是添加数据链路层的地址,源地址和目的地址,即网络相邻结点间的MAC地址
-
物理层传输数据 将数据链路层的帧转换为二进制形式的比特流,从网卡发送出去,再转换成电子,光学信号在网络中传输。
总结:6个步骤分别为:DNS协议解析URL地址,生成HTTP请求报文,构建TCP链接,使用IP协议选择传输路线,数据链路层保证数据的可靠传输,物理层将数据转换为物理信号进行传输。
补充:服务器处理及反向传输:
服务器接收到这个比特流,把比特流转换成帧格式,上传到数据链路层,服务器发现数据帧中的目的MAC地址与本网卡的MAC地址相同,服务器拆除数据链路层的封装后,把数据包上传到网络层。服务器的网络层比较数据包中的目的IP地址,发现与本机的IP地址相同,服务器拆除网络层的封装后,把数据分段上传到传输层。传输层对数据分段进行确认、排序、重组,确保数据传输的可靠性。数据最后被传到服务器的应用层
接着,通过传输层、网络层、数据链路层的层层封装,最终将响应报文封装成二进制比特流,并转换成其他信号,如电信号到网络中传输
反向传输的过程与正向传输的过程类似
浏览器的渲染过程:
主要步骤
- 浏览器将获取到的HTML文档解析成DOM树
- 处理css标记,构成层叠样式表模型CSSOM(css object model)
- 将DOM和CSSOM合并为渲染树(rendering tree),代表一系列将要被渲染的对象
- 渲染树的每个元素包含的内容是计算过的,称之为布局layout,浏览器使用一种流式处理的方法,只需要一次绘制操作就可以布局所有元素
- 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting 具体流程:
构建DOM树:
浏览器收到服务器响应来的HTML文档后,遍历文档节点,生成DOM树,DOM树构建时可能会被CSS和JS的加载而执行阻塞,display:none的元素,注释,script也会存在于DOM树中。
构建CSSOM规则树
浏览器解析CSS文件并对每个文件生成一个stylesheet对象,每个对象包含css规则。css解析可以和DOM解析同时进行,css的解析会被js互斥。
构建渲染树(render tree):
浏览器从DOM树的根结点遍历每个可见结点,然后对每个可见结点找到css样式规则并应用。Render tree与DOM tree不完全对应,display:none的元素不在render tree中,visibility:hidden元素在render tree中。这个时候还没渲染到屏幕上,渲染到屏幕需要用到各个节点的位置信息,需要布局的处理
渲染树布局(render tree layout):
布局阶段,遍历每一个render object对象,包含宽高,位置,背景色等样式信息,通过这些信息确定每个结点在页面上的确切位置。脱离文档流,脱离的是render tree
渲染树绘制(render tree paint):
绘制阶段,浏览器遍历渲染树,调用渲染器的paint()方法在屏幕上显示其内容,由浏览器的后端UI组建完成。
Js阻塞:
JS可以操作DOM来改变DOM结构,修改CSSOM样式,所以浏览器在遇到<script>
标签时,DOM构建将暂停,直到脚本执行完成,再继续构建DOM,所以<script>
标签一般放在最后。现在可以在<script>
标签上增加defer
,async
等属性,单独解析js中操作DOM和CSSOM的地方追加到DOM和CSSOM上。
Css阻塞:
CSSOM负责存储渲染信息,所以CSSOM在浏览器合成渲染树之前必须是完备的,否则就不会进入渲染过程。所以将样式放在head中,为了更快的解析css。
defer和async属性的区别:
没有defer
和async
属性的时候浏览器在读取到<script>
标签的时候会立即读取脚本文件,而不会加载后续的文档元素,会阻塞后续DOM的构建
文档解析时,遇到设置了defer
的脚本,就会在后台进行下载,但是并不会阻止文档的渲染,当页面解析&渲染完毕后。会等到所有的defer脚本加载完毕并按照顺序执行,执行完毕后会触发DOMContentLoaded(dom内容加载完毕)事件。
async
脚本会在加载完毕后执行。async
脚本的加载不计入DOMContentLoaded事件统计
有defer
或者async
属性之后,脚本文件的读取和后续文档的加载是并行的也就是异步执行,js脚本的执行会等到所有元素被解析完成之后,DOMContentLoaded事件调用前执行。
存在多个defer
的脚本文件时,他们会按照顺序defer
的顺序执行,而async
的加载则是无序的,只要DOM加载完成会立即执行,不考虑脚本之间的依赖关系。
重排(layout)和重绘(repaint):
DOM的变化影响到了已渲染完成的几何属性,比如宽高等,浏览器将重新计算元素的几何属性,其他元素的几何属性也会受到影响,浏览器需要重新构造渲染树(Render树),这个过程称之为重排(也叫回流reflow),浏览器将受到影响的部分重新绘制在屏幕上的过程称之为重绘。display:none
会触发reflow,而visibility: hidden
属性则并不算是不可见属性,只会触发repaint
重排产生的原因有:
- 添加或删除可见的DOM元素,
- 元素的尺寸位置发生改变。
- 浏览器页面初始化
- 浏览器窗口大小发生变化
- 重排一定导致重绘,重绘不一定导致重排。
减少重绘重排的方法:
- 不在布局信息改变时做DOM查询
- 不要一条一条的修改DOM样式,使用csstext,className一次性改变属性
- 在内存中多次操作节点,完成后再添加到文档中
- 对于一个元素进行复杂的操作时,先隐藏,操作完再展示
- 需要经常获取那些引起浏览器重排的属性时,缓存到变量中
- 不要使用table布局,一个小改动会导致table重新布局
- 对于多次重排的元素,比如说动画,使用绝对定位让其脱离文档流,使其不影响其他元素,减少重绘范围
http报文构成:
使用HTTP长连接有哪些优点:
HTTP长连接(持久化连接): 在一个TCP连接的基础之上,发送多个HTTP请求以及接收多个HTTP响应,这是为了避免每一次请求都去打开一个新的连接。
在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。
而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码:Connection:keep-alive
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。
Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。
HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。
长连接的特点:
可以省去较多的TCP连接建立和关闭的操作,减少浪费,节约时间。适用于频繁请求服务器资源的客户端之间建立。
短连接的特点:
对于服务器来说管理较为简单,存在的连接都是有用的连接,但是如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。WEB网站的http服务一般采用的都是短连接服务。
客户端和服务器之间的连接如果一直不关闭,随着建立连接的客户端越来越多,会增加服务器的压力,所以一般采取以下几种策略:
1.关闭一些长时间没有发生读写事件的连接。
2.限制每个客户端的最大长连接数量。
TLS/SSL协议是如何保障信息安全的:
SSL(安全套接字协议):是web浏览器和web服务器之间安全交换信息的协议,提供两个基本的安全服务,鉴别和保密 TLS(安全传输层协议):用于在两个通信应用程序之间提供保密性和数据完整性。其中TLS是SSL的升级版
SSL/TLS协议的基本过程
(1) 客户端向服务器端索要并验证公钥。
(2) 双方协商生成"对话密钥"。
(3) 双方采用"对话密钥"进行加密通信。
具体过程可细分为8个步骤:
- 客户端向服务器发起https请求,连接到服务器的443端口
- 服务器有密钥对,即公钥和私钥,用来进行非对称加密
- 服务器将公钥发送给客户端
- 客户端收到服务端的数据后,先验证其证书的合法性,如果合法,客户端生成客户端密钥,并使用服务端接收到的公钥对客户端密钥进行加密。
- 客户端再次发起https请求,将加密之后的客户端密钥发送给服务器
- 服务器接收到客户端发送过来的加密后的密钥,用自己的私钥进行解密,然后用客户端密钥对要发送的数据进行加密。
- 将加密后的数据发送给客户端
- 客户端使用客户端密钥对加密数据进行解密,得到服务器发送的数据。
SSL协议的内容:
- 握手协议 握手协议是客户端和服务器用SSL连接通信时使用的第一个子协议,握手协议包括客户机与服务器之间的一系列消息,是SSL协议中最复杂的协议,该协议允许服务器和客户机相互验证,协商加密和MAC算法(带秘密密钥的Hash函数)及保密密钥,用来保护在SSL记录中发送的数据,握手协议是在应用程序的数据传输之前使用的。 1.1 建立安全能力,发送信息,交换版本,随机数,会话ID,密码套件,压缩方法等信息 1.2 服务器鉴别与密钥交换 1.3 客户机鉴别与密钥交换 1.4 完成
- 记录协议 记录协议在客户机和服务器成功握手之后使用,即客户机和服务器鉴别对方和确定安全信息交换使用的算法后,进入SSL记录协议。
- 警报协议 客户机和服务器发现错误时,向对方发送一个警报信息,如果是致命错误,则算法立即关闭SSL连接,双方删除相关的会话号,密钥等。
总结:SSL中,使用握手协议协商加密和MAC算法以及保密密钥,使用握手协议对交换的数据进行加密和签名,使用警报协议定义数据传输过程中,出现问题改如何解决。
HTTP 1.0,1.1,2.0协议的区别:
HTTP1.0 HTTP 1.1主要区别
长连接
HTTP 1.0需要使用keep-alive参数来告知服务器端要建立一个长连接,而HTTP1.1默认支持长连接。
HTTP是基于TCP/IP协议的,创建一个TCP连接是需要经过三次握手的,有一定的开销,如果每次通讯都要重新建立连接的话,对性能有影响。因此最好能维持一个长连接,可以用个长连接来发多个请求。
节约带宽
HTTP 1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,否则返回401。客户端如果接受到100,才开始把请求body发送到服务器。
这样当服务器返回401的时候,客户端就可以不用发送请求body了,节约了带宽。
另外HTTP还支持传送内容的一部分。这样当客户端已经有一部分的资源后,只需要跟服务器请求另外的部分资源即可。这是支持文件断点续传的基础。
HOST域
现在可以web server例如tomat,设置虚拟站点是非常常见的,也即是说,web server上的多个虚拟站点可以共享同一个ip和端口。
HTTP1.0是没有host域的,HTTP1.1才支持这个参数。
HTTP1.1 HTTP 2.0主要区别
多路复用
HTTP2.0使用了(类似epoll)多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。
当然HTTP1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。
TCP连接有一个预热和保护的过程,先检查数据是否传送成功,一旦成功过,则慢慢加大传输速度。因此对应瞬时并发的连接,服务器的响应就会变慢。所以最好能使用一个建立好的连接,并且这个连接可以支持瞬时并发的请求。
数据压缩
HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。
请求与响应首部的定义在HTTP2.0中基本没有变,只是所有首部键必须全部小写,而且要求行要独立为:method:、:scheme:、:host:、:path:这些键值对
对于相同的数据,不再重新通过每次请求和响应发送。每个新的首部键值对要么追加到当前表的末尾,要么替换表中之前的值。首部表在HTTP2.0的链接存续期内始终存在,由客户端和服务端共同渐进的更新。
服务器推送
意思是说,当我们对支持HTTP2.0的web server请求数据的时候,服务器会顺便把一些客户端需要的资源一起推送到客户端,免得客户端再次创建连接发送请求到服务器端获取。这种方式非常合适加载静态资源。
服务器端推送的这些资源其实存在客户端的某处地方,客户端直接从本地加载这些资源就可以了,不用走网络,速度自然是快很多的。
二进制帧层
HTTP2.0会把所有信息分割成更小的消息和帧,并采用二进制格式将其封装。例如header封装到Headers帧,request body封装到Data帧。
HTTP2.0通信都在一个TCP连接上完成,这个连接可以承载任意数量的双向数据流,相应的每个数据流以消息的形式发送。而消息由一或多个帧组成,这些帧可以乱序发送,然后根据每个帧首部的流标识符重新组装。
WebSocket协议:
ajax轮询
ajax(异步的javascript与xml技术)是一种有效利用javascript和dom的操作,以达到局部web页面的提花和加载的异步通信手段。和以前的同步通信相比,他只更新一部分页面,相应中传输的数据量会因此的减少。
ajax轮询的原理是,让浏览器每隔一段时间就发送一次请求,询问服务器是否有新消息。而利用ajax实时的从服务器获取内容,有可能导致大量的请求产生。
长轮询
原理和ajax轮询差不多,都是采用轮询的方式,不过采用的是阻塞模型。也就是说,当客户端发起连接后,如果服务器端内容没有更新,将响应至于挂起状态,一直不回复response给客户端,知道有内容更新,再返回响应。
- http的特点:被动性,请求只能由客户端发起。服务器端不能主动联系客户端。
- ajax轮询 需要服务器有很快的处理速度和资源。(速度)
- 长轮询 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
WebSocket - WebSocket是HTML5出的东西(协议),是HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。
- 一旦web服务器和客户端建立起websocket协议的通信连接,之后所有的通信都依靠这个专用连接进行。只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。
- 通过在请求首部中设置
Connection: Upgrade
来使用websocket协议
websocket协议具有以下的特点
推送功能
支持服务器端向客户端推送功能。服务器可以直接发送数据而不用等待客户端的请求。
减少通信量
只要建立起websocket连接,就一直保持连接,在此期间可以源源不断的传送消息,直到关闭请求。也就避免了HTTP的非状态性。
和http相比,不但每次连接时的总开销减少了,而且websocket的首部信息量也小 ,通信量也减少了。
支持双向通信,实时性更强。
更好的二进制支持。
较少的控制开销。
连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头部。
支持扩展
ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)
CSS部分:
css3新增:
CSS3边框如border-radius
,box-shadow
等;CSS3背景如background-size
,background-origin
等;CSS3 2D,3D转换如transform
等;CSS3动画如animation
等。
CSS3的新特性中,在布局方面新增了flex布局,在选择器方面新增了例如first-of-type
,nth-child
等选择器,在盒模型方面添加了box-sizing
来改变盒模型,在动画方面增加了animation
,2d变换,3d变换等,在颜色方面添加透明,rbga等,在字体方面允许嵌入字体和设置字体阴影,最后还有媒体查讯等.
css的两种盒模型
在标准的盒子模型中,width指content部分的宽度
在IE盒子模型中,width表示content+padding+border这三个部分的宽度
如果想要切换盒模型也很简单,这里需要借助css3的box-sizing属性
box-sizing: content-box 是W3C盒子模型
box-sizing: border-box 是IE盒子模型
box-sizing的默认属性是content-box
垂直居中方式:
- 使用定位和负外边距,父元素,子元素固定宽高,父相子绝,子元素top属性50%,上外边距margin-top为子元素本身高度的一半(这种方式需要知道子元素的高度)
- 使用定位和transform,父元素固定宽高,父相子绝,子元素top属性50%,transform属性为Y轴方向偏移-50%
- 使用定位自动外边距,父元素,子元素固定宽高,父相子绝,子元素top,bottom属性为0,margin在垂直方向auto
- 使用flex布局,父元素,子元素固定宽高,父元素使用flex布局,设置align-items属性为center
- 使用flex布局,父元素,子元素固定宽高,父元素使用flex布局,设置flex-direction属性(设置主轴方向)为column(纵轴),justify-content属性设置主轴上的对齐方式为center
- 使用 display:table-cell ,父元素固定宽高,父元素设置display:table,子元素设置display:table-cell,并且vertical-align:middle
transition和animation的区别:
animation和transition大部分属性是相同的,他们都是随时间改变元素的属性值,他们的主要区别是transition需要触发一个事件才能改变属性,而animation不需要触发任何事件的情况下才会随时间改变属性值,并且transition为2帧,从from .... to,而animation可以一帧一帧的。
animation属性
- name 设置动画的名称,
- duration 设置动画完成的周期,
- timing-function 设置动画的速度曲线,
- delay 设置动画什么时候开始,
- iteration-count 设置动画播放的次数,
- direction 规定下一个周期是否逆向的播放,
- play-state 动画是否正在进行或者暂停,
- fill-mode 设置动画停了之后位置什么状态
transition属性 - property 去设置过渡效果的属性名称,
- duration 设置过渡效果的周期,
- timing-function 规定速度效果的速度曲线,
- delay 设定过渡效果什么时候开始;
区别:
- transition 是过渡,是样式值的变化的过程,只有开始和结束;animation 其实也叫关键帧,通过和 @keyframe 结合可以设置中间帧的一个状态;
- animation 配合 @keyframe 可以不触发事件就触发这个过程,而 transition 需要通过 hover 或者 js 事件来配合触发;
- animation 可以设置很多的属性,比如循环次数,动画结束的状态等等,transition 只能触发一次;
- animation 可以结合 keyframe 设置每一帧,但是 transition 只有两帧;
- 在性能方面:浏览器有一个主线程和排版线程;主线程一般是对 js 运行的、页面布局、生成位图等等,然后把生成好的位图传递给排版线程,而排版线程会通过 GPU 将位图绘制到页面上,也会向主线程请求位图等等;我们在用使用 animation 的时候这样就可以改变很多属性,像我们改变了 width、height、position 等等这些改变文档流的属性的时候就会引起,页面的回流和重绘,对性能影响就比较大,但是我们用 transition 的时候一般会结合 transform 来进行旋转和缩放等不会生成新的位图,当然也就不会引起页面的重排了;
transition、transform和translate这三者
transform是转换,指的是改变所在元素的外观,它有很多种手段(转换函数)来改变外观,例如 位移、缩放、旋转等,而其中的位移的函数名就叫translate,所以说,translate是transform的一部分。
transform: [转换函数];
属性函数:
translate(x, y):元素位移,元素偏移的x轴和y轴距离,可为负
scale(x, y):元素缩放,元素x轴和y轴缩放的倍数,小于1为缩小,大于1位放大,小于0效果和为0时相等
rotate(angle):元素旋转,旋转的角度,单位deg,顺时针旋转
transform: translate(-10px, 10px);//当前元素往上移动 10 像素,往右移动 10 像素。
transform: translate(10px, 10px) rotate(10deg);//指定多个转换效果。
transition是过渡,指的是某个CSS属性值如何平滑的进行改变,就是平常说的 动效。而transform是没有动画效果,你改变了它的值,元素的样子就唰的改变了。
transition: [属性名] [持续时间] [速度曲线] [延迟时间];
transition: height 2s ease .5s;//高度属性的值改变时,延迟 0.5 秒后以 ease 曲线进行过渡,持续2秒。
visibility=hidden, opacity=0,display:none:
- opacity=0,该元素隐藏起来了,但不会改变页面布局,并且,如果该元素已经绑定一些事件,如click事件,那么点击该区域,也能触发点击事件的方法。
- visibility=hidden,该元素隐藏起来了,但不会改变页面布局,但是不会触发该元素已经绑定的事件。
- display=none,把元素隐藏起来,并且会改变页面布局,可以理解成在页面中把该元素删除掉一样。
position属性比较:
固定定位fixed:
元素的位置相对于浏览器窗口是固定位置,即使窗口是滚动的它也不会移动。Fixed定位使元素的位置与文档流无关,因此不占据空间。 Fixed定位的元素和其他元素重叠。
相对定位relative:相对于他原本的位置进行移动
如果对一个元素进行相对定位,它将出现在它所在的位置上。然后,可以通过设置垂直或水平位置,让这个元素“相对于”它的起点进行移动。 在使用相对定位时,无论是否进行移动,元素仍然占据原来的空间。因此,移动元素会导致它覆盖其它框。
绝对定位absolute:相对于他的父元素进行移动
绝对定位的元素的位置相对于最近的已定位父元素,如果元素没有已定位的父元素,那么它的位置相对于<html>
。 absolute 定位使元素的位置与文档流无关,因此不占据空间。 absolute 定位的元素和其他元素重叠。
粘性定位sticky:
先是相对于他原本的位置进行移动,然后在超出目标区域后,固定在目标位置,即先按照relative定位方式定位,然后按照fix定位方式定位。
元素先按照普通文档流定位,然后相对于该元素在流中的flow root(BFC)和 containing block(最近的块级祖先元素)定位。而后,元素定位表现为在跨越特定阈值前为相对定位,之后为固定定位。
默认定位Static:
默认值。没有定位,元素出现在正常的流中(忽略top, bottom, left, right 或者 z-index 声明)。
继承定位inherit:
规定应该从父元素继承position 属性的值。
浮动清除的几种方法:
方法一:使用带clear属性的空元素
在浮动元素后使用一个空元素如<div class="clear"></div>
,并在CSS中赋予.clear{clear:both;}属性即可清理浮动。亦可使用<br class="clear" />
或<hr class="clear" />
来进行清理。
方法二:使用CSS的overflow属性
给浮动元素的容器添加overflow:hidden
;或overflow:auto
;可以清除浮动,另外在 IE6 中还需要触发 hasLayout ,例如为父元素设置容器宽高或设置 zoom:1。
在添加overflow属性后,浮动元素又回到了容器层,把容器高度撑起,达到了清理浮动的效果。
方法三:给浮动的元素的容器添加浮动
给浮动元素的容器也添加上浮动属性即可清除内部浮动,但是这样会使其整体浮动,影响布局,不推荐使用。
方法四:使用邻接元素处理
什么都不做,给浮动元素后面的元素添加clear属性。
方法五:使用CSS的:after伪元素
结合:after 伪元素(注意这不是伪类,而是伪元素,代表一个元素之后最近的元素)和 IEhack ,可以完美兼容当前主流的各大浏览器,这里的 IEhack 指的是触发 hasLayout。
给浮动元素的容器添加一个clearfix的class,然后给这个class添加一个:after伪元素实现元素末尾添加一个看不见的块元素(Block element)清理浮动。
CSS选择器有哪些,优先级呢:
CSS的四种基本选择器和四种高级选择器:
基本选择器:
标签选择器:针对一类标签
P{}:选择所有<p>
元素
ID选择器:针对某一个特定的标签使用
#id{}:选择所有id="firstname"的元素
类选择器:针对你想要的所有标签使用
.class{}:选择所有class="intro"的元素
通用选择器(通配符):针对所有的标签都适用(不建议使用)
- :选择所有元素
高级选择器:
后代选择器:用空格隔开
div p: 选择<div>
元素内的所有<p>
元素
div>p: 选择所有父级是 <div>
元素的 <p>
元素
div+p: 选择所有紧接着<div>
元素之后的<p>
元素
属性选择器:带有属性或指定属性内容
[target]:选择所有带有target属性元素
[target=-blank]:选择所有使用target="-blank"的元素
[title~=flower]:选择标题属性包含单词"flower"的所有元素
[lang|=en]:选择 lang 属性以 en 为开头的所有元素
交集选择器:用.隔开
h3.special:选择<h3>
标签并且类是.special的标签
并集选择器(分组选择器):用逗号隔开
div,p: 选择所有<div>
元素和<p>
元素
p,h1,#mytitle,.one:选择所有带有p,h1,id="mytitle",class="one"的标签
伪类选择器:
静态伪类选择器:
a:link:选择所有未访问链接
a:visited:选择所有访问过的链接
动态伪类选择器:
a:active:选择活动链接
a:hover:选择鼠标在链接上面时
input:focus:选择具有焦点的输入元素
序伪类选择器:
p:first-letter:选择每一个p元素的第一个字母
p:first-line:选择每一个p元素的第一行
p:first-child:指定只有当p元素是其父级的第一个子级的样式。
p:before:在每个p元素之前插入内容
p:after:在每个p元素之后插入内容
Css3新增选择器:
p~ul:选择p元素之后的每一个ul元素
a[src^"ttps"]:选择每一个src属性的值以"https "开头的元素
a[src$=". pdf"]:选择每一个src属性的值以” pdf结尾的元素
a[src*=" runoob"]:选择每一个src属性的值包含子字符串" 'runoob"的元素
p:first-of-type:选择每个p元素是其父级的第一个p元素
p:last-of-type:选择每个p元素是其父级的最后一个p元素
p:only-of-type:选择每个p元素是其父级的唯一p元素
p:only-child:选择每个p元素是其父级的唯一子元素
p:nth-child(2):选择每个p元素是其父级的第二个子元素
p::nth-child(-n+3):选择每个p元素是其父级的前三个子元素
p:th-last-child(2):选择每个p元素的是其父级的第二个子元素,从最后一个子项计数
p:th-of-type(2):选择每个p元素是其父级的第二个p元素
p:nth-last-of-type(2):选择每个p元素的是其父级的第二个p元素, 从最后一个子项计数
p:last-child:选择每个p元素是其父级的最后-个子级。
.root:选择文档的根元素
p:empty:选择每个没有任何子级的p元素(包括文本节点)
#news:target:选择当前活动的#news元素(包含该锚名称的点击的URL)
inputenabled:选择每一个已启用的输入元素
input:disabled:选择每一个禁用的输入元素
input.checked:选择每个选中的输入元素
:not(p):选择每个并非p元素的元素
:selection:匹配元素中被用户选中或处于高亮状态的部分
:out-of-range:匹配值在指定区间之外的input元素
:in-range:匹配值在指定区间之内的input元素
.read-write:用于匹配可读及可写的元素
:read-only:用于匹配设置"readonly" (只读)属性的元素
:optional:用于匹配可选的输入元素
.required:用于匹配设置了"required" 属性的元素
valid:用于匹配输入值为台法的元素
cinvalid:用于匹配输入值为非法的元素
Css选择器优先级:
- 在属性后面使用 !important 会覆盖页面内任何位置定义的元素样式。
- 作为style属性写在元素内的样式
- id选择器
- 类选择器
- 元素选择器
- 通配符选择器
- 浏览器自定义或继承
margin塌陷和合并问题:
首先,margin塌陷是相对于父子级关系的两个元素,而margin合并是相对两个兄弟级关系的两个元素
两个兄弟级关系的元素,垂直方向上的margin,其外边距会发生重叠现象,两者两个的外边距取的是两个所设置margin的最大值,就是所说的margin合并问题
两个父子级关系的元素,垂直方向上的margin会粘合在一起,外层和模型的margin-top取两个元素中margin-top的最大值,发生margin塌陷的内层元素相对于整个文档移动
解决方案:两者都可以通过触发BFC来解决
什么是BFC:
直译成:块级格式化上下文,是一个独立的渲染区域,并且有一定的布局规则。
- BFC区域不会与float box重叠。
- BFC是页面上的一个独立容器,子元素不会影响到外面。
- 计算BFC的高度时,浮动元素也会参与计算。
- 并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
哪些元素会生成BFC:
- float不为none的元素
- position为fixed和absolute的元素
- display为inline-block、table-cell、table-caption,flex,inline-flex的元素
- overflow不为visible的元素
inline-block、inline和block的区别:
- Block是块级元素,其前后都会有换行符,能设置宽度,高度,margin/padding水平垂直方向都有效。
- Inline:设置width和height无效,margin在竖直方向上无效,padding在水平方向垂直方向都有效,前后无换行符
- Inline-block:能设置宽度高度,margin/padding水平垂直方向 都有效,前后无换行符
px、em、rem、%、vw、vh、vm这些单位的区别
- em:参考的是父元素的font-size,具有继承的特点,如果自身定义了font-size则按自身来计算(浏览器默认字体是16px),整个页面内1em不是一个固定的值
- rem:相对于根元素html,可以设置根元素html的font-size为10px,则1.2em就是12px;
- vw:css3新单位,view width的缩写,是指可视窗口的高度,假如宽度是1200px,则10vw就是120px;举个例子:浏览器宽度1200px, 1 vw = 1200px/100 = 12 px。
- Vh:类似vw,指的是可视窗口的高度。
- Vm:相对于视口的宽度或高度中较小的那个,其中最小的单位被均分为100个单位,举个例子:浏览器高度900px,宽度1200px,取最小的浏览器高度,1 vm = 900px/100 = 9 px。
flex布局:
常用的Flex属性:
-
display:指定 HTML 元素盒子类型。
-
flex-direction:指定了弹性容器中子元素的排列方式
- row:横向从左到右排列(左对齐),默认的排列方式。
- row-reverse:反转横向排列(右对齐,从后往前排,最后一项排在最前面。
- column:纵向排列。
- column-reverse:反转纵向排列,从后往前排,最后一项排在最上面。
-
justify-content:设置弹性盒子元素在主轴(横轴)方向上的对齐方式。
- flex-start:弹性项目向行头紧挨着填充。这个是默认值。第一个弹性项的main-start外边距边线被放置在该行的main-start边线,而后续弹性项依次平齐摆放。
- flex-end:弹性项目向行尾紧挨着填充。第一个弹性项的main-end外边距边线被放置在该行的main-end边线,而后续弹性项依次平齐摆放。
- center:弹性项目居中紧挨着填充。(如果剩余的自由空间是负的,则弹性项目将在两个方向上同时溢出)。
- space-between:弹性项目平均分布在该行上。如果剩余空间为负或者只有一个弹性项,则该值等同于flex-start。否则,第1个弹性项的外边距和行的main-start边线对齐,而最后1个弹性项的外边距和行的main-end边线对齐,然后剩余的弹性项分布在该行上,相邻项目的间隔相等。
- space-around:弹性项目平均分布在该行上,两边留有一半的间隔空间。如果剩余空间为负或者只有一个弹性项,则该值等同于center。否则,弹性项目沿该行分布,且彼此间隔相等(比如是20px),同时首尾两边和弹性容器之间留有一半的间隔(1/2*20px=10px)。
-
align-items:设置弹性盒子元素在侧轴(纵轴)方向上的对齐方式。
- flex-start:弹性盒子元素的侧轴(纵轴)起始位置的边界紧靠住该行的侧轴起始边界。
- flex-end:弹性盒子元素的侧轴(纵轴)起始位置的边界紧靠住该行的侧轴结束边界。
- center:弹性盒子元素在该行的侧轴(纵轴)上居中放置。(如果该行的尺寸小于弹性盒子元素的尺寸,则会向两个方向溢出相同的长 + 度)。
- baseline:如弹性盒子元素的行内轴与侧轴为同一条,则该值与'flex-start'等效。其它情况下,该值将参与基线对齐。
- stretch:如果指定侧轴大小的属性值为'auto',则其值会使项目的边距盒的尺寸尽可能接近所在行的尺寸,但同时会遵照'min/max-width/height'属性的限制。
-
flex-wrap:设置弹性盒子的子元素超出父容器时是否换行。
- nowrap - 默认, 弹性容器为单行。该情况下弹性子项可能会溢出容器。
- wrap - 弹性容器为多行。该情况下弹性子项溢出的部分会被放置到新行,子项内部会发生断行
- wrap-reverse -反转 wrap 排列。
-
align-content:修改 flex-wrap 属性的行为,类似 align-items, 但不是设置子元素对齐,而是设置行对齐。
- stretch - 默认。各行将会伸展以占用剩余的空间。
- flex-start - 各行向弹性盒容器的起始位置堆叠。
- flex-end - 各行向弹性盒容器的结束位置堆叠。
- center -各行向弹性盒容器的中间位置堆叠。
- space-between -各行在弹性盒容器中平均分布。
- space-around - 各行在弹性盒容器中平均分布,两端保留子元素与子元素之间间距大小的一半。
-
flex-flow:flex-direction 和 flex-wrap 的简写
-
order:设置弹性盒子的子元素排列顺序。
- integer:用整数值来定义排列顺序,数值小的排在前面。可以为负值。
-
align-self:在弹性子元素上使用。覆盖容器的 align-items 属性。
- auto:如果'align-self'的值为'auto',则其计算值为元素的父元素的'align-items'值,如果其没有父元素,则计算值为'stretch'。
- flex-start:弹性盒子元素的侧轴(纵轴)起始位置的边界紧靠住该行的侧轴起始边界。
- flex-end:弹性盒子元素的侧轴(纵轴)起始位置的边界紧靠住该行的侧轴结束边界。
- center:弹性盒子元素在该行的侧轴(纵轴)上居中放置。(如果该行的尺寸小于弹性盒子元素的尺寸,则会向两个方向溢出相同的长度)。
- baseline:如弹性盒子元素的行内轴与侧轴为同一条,则该值与'flex-start'等效。其它情况下,该值将参与基线对齐。
- stretch:如果指定侧轴大小的属性值为'auto',则其值会使项目的边距盒的尺寸尽可能接近所在行的尺寸,但同时会遵照'min/max-width/height'属性的限制。
-
flex:设置弹性盒子的子元素如何分配空间。 一般使用flex:1来平分空间,flex: 1; === flex: 1 1 auto;
- 第一个参数表示: flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
- 第二个参数表示: flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
- 第三个参数表示: flex-basis给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小
- auto: 计算值为 1 1 auto
- initial: 计算值为 0 1 auto
- none:计算值为 0 0 auto
- inherit:从父元素继承
- [ flex-grow ]:定义弹性盒子元素的扩展比率。
- [ flex-shrink ]:定义弹性盒子元素的收缩比率。
- [ flex-basis ]:定义弹性盒子元素的默认基准值。
流式布局与响应式布局的区别:
常用的Flex属性:
- 流式布局:使用非固定像素来定义网页内容,也就是百分比布局,通过盒子的度设置成百分比来根据屏幕的宽度来进行伸缩,不受固定像素的限制,内容自动填充。
- 响应式布局,利用css3中的Media Query(媒介查询),通过查询screen的宽度来指定某个宽度区间的网页布局
超小屏幕(移动设备):768px以下
小屏设备:768px-992px;
中屏设备:992-1200px;
宽屏设备:1200px以上
由于响应式布局开发显得繁琐,一般使用第三方响应式框架来完成,比如bootstrap来完成一部分工作
三栏布局的实现方式:
常用的Flex属性:
- 浮动
三栏设置高度,左右栏固定宽度,并分别设置左右浮动
优点:兼容性好
缺点:浮动脱离文档流,需要做清除浮动的处理 - 绝对定位
父级元素设置绝对定位,左右栏固定宽度,左栏设置left:0;右栏设置right:0;中间栏设置left和right分别为左右栏的宽度
优点:快捷,不容易出问题
缺点:布局脱离文档流,子元素也必须脱离文档流,可使用性较差 - Flex布局
父元素设置display:flex,左右栏固定宽度,中间栏设置flex:1,占据剩余空间
优点:较为完美,移动端布局多属flex - 表格布局
父元素设置display:table,左右中三栏全部设置display:table-cell,左右栏固定宽度
优点:兼容性好
缺点:操作繁琐,当其中一个单元格高度变大,其他单元格高度会随之变大 - 网格布局
父元素设置display:grid,固定宽度,grid-template-rows: 100px;
grid-template-columns: 300px auto 300px;
优点:可以做许多复杂的事情,代码比较简洁
缺点:新技术,低版本浏览器兼容性不好
JS部分:
JS基本数据类型:
简单数据类型:Undefined、Null、Boolean、Number和String,es6xinzeng
复杂数据类型:Object
通过typeof查看数据的基本类型,其中null会返回object,理解为空对象。
Null和undefined的区别:
- Null表示一个无的对象,转成数值是0,一个尚未存在的对象,常用来表示函数企图返回一个不存在的对象 用法:
- 作为函数的参数,表示该函数的参数不是对象
- 作为对象原型链的终点
- Undefined是一个表示无的原始值,转换成数值为NaN,当声明的变量未被初始化的时候,变量的默认值为undefined 用法:
- 变量被声明了,但没有被赋值时,就等于undefined
- 调用函数时,应该提供的参数没有提供,该参数等于undefined
- 对象没有赋值属性,该属性的值为undefined
- 函数没有返回值时,默认返回undefined
ES6新增
1.新增声明命令let和const,let表示变量,const表示常量。
- let和const都是块级作用域,以{}代码块作为作用域范围,只能在代码块里面使用。
- 不存在变量提升,只能先声明再使用,否则报错。在声明变量之前,该变量都是不可用的。
- 在同一个代码块内,不允许重复声明。
- Const声明的是一个只读常量,在声明时就需要赋值。(如果const的是一个对象,对象的值是可以被修改的,就是对象所指向的地址不能改变,而成员变量是可以修改的。)
2.模板字符串
用一对反引号作为标识,可以当做普通字符串使用,也可以用来定义多行字符串,也可在字符串中嵌入变量,js表达式或函数,变量,js表达式或函数需要写在${}
中.
3.函数相关扩展方法
函数的默认参数
ES6为参数提供了默认值,在定义函数时便初始化这个值,以便在参数没有传递进去时使用。
箭头函数
写法:函数名=(形参)=>{……},当函数体中只有一个表达式时,{}和return可以省略,当函数体中形参只有一个时,()可以省略。
特点:
-
箭头函数是匿名函数,不能作为构造函数,不能使用new
-
箭头函数不绑定arguments,用rest参数...代替
-
箭头函数不绑定this,会捕获其所在上下文的this值,作为自己的this值
-
任何方法都不能改变箭头函数中this的指向,箭头函数通过call(),apply()方法调用一个函数时,只传入参数,不改变this指向 箭头函数没有原型属性
-
箭头函数不能当做Generator函数,不能使用yield关键字
-
与普通函数的区别:普通函数中的this指向的始终是最后调用它的对象(比如在对象中定义的函数,这个函数内部的this,指向的是这个对象),如果没有直接调用对象,会指向undefined或者window,一般都会指向window,在严格模式下才会指向undefined。
普通函数中的this的四种调用模式:
- 函数式调用模式 如果一个函数不是一个对象的属性时,就是被当做一个函数来进行调用的。此时this指向了window
- 方法式调用 当一个函数被保存为对象的一个属性时,我们称之为一个方法。当一个方法被调用时,this被绑定到当前对象
- 构造函数式调用 如果函数是通过new关键字进行调用的,此时this被绑定到创建出来的新对象上。
- 上下文调用(借用方法模式) 上下文调用模式也叫方法借用模式,分为apply与call,使用方法:函数.call() 或者函数.apply()
- 几种特殊的this指向
定时器中的this指向了window,因为定时器的function最终是由window来调用的。
事件中的this指向的是当前的元素,在事件触发的时候,浏览器让当前元素调用了function
4.对象相关的扩展方法
- 属性的简写。ES6 允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值。
- Object.keys()方法,获取对象的所有属性名或方法名(不包括原型的内容),返回一个数组。
- Object.assign (),assign方法将多个原对象的属性和方法都合并到了目标对象上面。可以接收多个参数,第一个参数是目标对象,后面的都是源对象。 第一级属性深拷贝,以后级别属性浅拷贝
5.for of循环
是遍历所有数据结构的统一的方法。for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、Generator 对象,以及字符串。不能遍历对象,因为对象没有内置的迭代器
6.import和export
- ES6标准中,JavaScript原生支持模块(module)了。这种将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用。
- export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口。
- import用于在一个模块中加载另一个含有export接口的模块。
- import和export命令只能在模块的顶部,不能在代码块之中。
7.解构赋值
- ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
- 数组的解构赋值,数组中的值会自动被解析到对应接收该值的变量中,数组的解构赋值要一一对应 如果有对应不上的就是undefined
- 对象的解构赋值,对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
8.set数据结构
Set数据结构,类似数组,所有数据都是唯一的,没有重复的值,它本身是一个构造函数
属性和方法:
- size 数据的长度
- add() 添加某个值,返回 Set 结构本身。
- delete() 删除某个值,返回一个布尔值,表示删除是否成功。
- has() 查找某条数据,返回一个布尔值。
- clear() 清除所有成员,没有返回值。
- 应用:数组去重
- 注意:new Set(arr)的返回结果是object,可以使用Array.from(new Set(arr))或[...new Set(arr)]来返回数组。
9.map数据结构
Map结构是键值对集合(Hash结构)
在JS中的默认对象的表示方式为{},即一组键值对,但是键必须是字符串。
为了使用Number或者其他数据类型作为键,ES6规范引入了新的数据类型Map。
Map是一组键值对的结构,具有极快的查找速度。初始化Map需要一个二维数组,或者直接初始化一个空Map。
map和object的区别
- Obj的键只能用字符串、数字或者Symbol等简单数据类型当作键
- Map的键可以是任意的数据类型
- Map不存在同名碰撞问题,每个对象都是单独的一块内存地址
- Map实现了迭代器,可用for...of遍历,而Object不行
- Map可以直接拿到长度,而Object不行。
- 填入Map的元Map可以使用省略号语法展开,而Object不行。填入元素会保持原有的顺序,而Object无法做到。
10.Spread Operator 展开运算符(...)
可以展开字符串,数组,对象等
- 将字符串转换成数组
- 将集合转换成数据
- 两个数组或对象的合并
- 在函数中,用来代替arguments参数,
rest参数 …变量名称
rest 参数是一个数组 ,它的后面不能再有参数,不然会报错
promise对象
Promise是异步编程的一种解决方案,将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
promise构造函数是同步执行的,then方法是异步执行的。
它有三种状态,分别是pending-进行中、resolved-已完成、rejected-已失败。
Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。对于已经实例化过promise对象可以调用 promise.then() 方法,传递 resolve 和 reject 方法作为回调。then()方法接收两个参数:onResolve和onReject,分别代表当前 promise 对象在成功或失败时。
Promise的实现原理
首先promise有三种状态
Pending 对象实例创建时候的初始状态
Fulfilled 可以理解为成功的状态
Rejected可以理解为失败的状态
构造一个Promise实例需要给Promise构造函数传入一个函数。传入的函数需要有两个形参,两个形参都是function类型的参数。分别是resolve和reject。
Promise上还有then方法,then 方法就是用来指定Promise 对象的状态改变时确定执行的操作,resolve 时执行第一个函数(onFulfilled),reject时执行第二个函数(onRejected)
当状态变为resolve时便不能再变为reject,反之同理。
Promise的缺点:
- 无法取消,一旦新建就会立即执行,无法中途取消,
- 不设置回调函数的情况下,promise内部抛出的错误不会反映到外部
- 当处于pending状态时,无法得知目前进展到哪一个阶段,刚开始还是快结束
Promise.all()
将promise对象数组按照异步顺序全部完成后在then的第一个函数中传入完成的结果,这个list参数是arr这个promise对象数组中所有异步的then中返回的img按顺序组合成的一个数组。也可以理解为将arr中的每个promise对象完成后的then中的img按顺序加入到一个数组中,在全部完成后返回这个数组list
Promise.all(requestPromises).then(…).catch(…) 会在所有requestPromises都resolve时才会进then方法,并且把所有结果以一个数组返回。只要有一个失败,就会进catch。如果在单个请求中定义了catch方法,那么就不会进Promise.all的catch方法。因此,可以在单个的catch中将失败的promise放入一个list,待一轮请求完成后,再去请求失败的请求。
Promise.race()
的作用是将arr这个promise对象数组中最先执行完成的一个promise中then中返回的参数传入img中,也就是promise.race().then()的第一个函数的参数中。
New实例化函数的过程
使用new关键字调用函数(new ClassA(...))的具体过程
- 创建一个空对象
Var obj={};
- 设置新对象的constructor属性为构造函数的名称,将新对象的__proto__指向构造函数的prototype
Obj.__proto__==ClassA.prototype
- 使用新对象调用构造函数,将构造函数中this指向新实例对象,
ClassA.call(obj)
- 将初始化完毕的新对象地址,保存到等号左边的变量中。
- 若构造函数中返回this或返回值是基本类型(number,string,bool,null,undefined)或者无返回值,则返回新的实例对象,若是引用类型的值,则返回这个引用类型。
ES5中call,apply,bind的区别
- Js中,call和apply都是为了改变函数运行时的上下文,也就是为了改变函数体内部的this指向。
- 格式为function.call(object),function.apply(object),function.bind(object)()
- 其中调用call和apply的方法会立即执行。
- bind方法会创建一个新的函数,需要加上()才会执行,传入的参数放在()内
ES6与 CommonJS 模块化的区别
什么是js模块化?
js一开始并没有模块化的概念,直到ajax被提出,前端能够像后端请求数据,前端逻辑越来越复杂,就出现了许多问题:全局变量,函数名冲突,依赖关系不好处理。当时使用子执行函数来解决这些问题,比如经典的jquery就使用了匿名自执行函数封装代码,将他们全都挂载到全局变量jquery下边。
CommonJs
CommonJs通过nodeJs发扬光大,每个js文件就是一个模块,每个模块有单独的作用域。模块以module.exports为出口,输出一个对象。使用require方法读取文件,并返回其内部的module.exports对象。
CommonJs的问题在于,他的加载是同步的,这在服务端很正常,但是在充满了异步的浏览器里,就不适用了。为了适应浏览器,社区内部发生了分歧。
AMD(异步模块定义)
AMD规范通过define方法去定义模块,通过require方法去加载模块。RequireJS实现了这种规范。
AMD只有一个接口:define(id?,dependencies?,factory); 它要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中。要是没什么依赖,就定义简单的模块(或者叫独立的模块)
CMD(通用模块定义)
CMD是SeaJS 在推广过程中对模块定义的规范化产出。
AMD和CMD的区别:
- 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible(尽可能的懒加载,也称为延迟加载,即在需要的时候才加载)。
- CMD 推崇依赖就近(依赖项可以使用的时候定义),AMD 推崇依赖前置(依赖项必须在一开始就写好)。
- AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。
es6中的模块化
在es6没有出来之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种,前者用于服务器,后者用于浏览器,ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
es6中的模块化有一个比较大的特点,就是实现尽量的静态化。
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
模块功能主要由两个命令构成:export和import,export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
一般来说,一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取,如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。
Javascript 模块化主要有三种方案:
- CommonJS
- AMD / CMD
- ES6
ES6模块与CommonJS的区别:
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
- CommonJs模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化不会影响到这个值。
你可以看到明明common.js里面改变了count,但是输出的结果还是原来的。这是因为count是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动的值。将common.js里面的module.exports 改写成
这样子的输出结果是 1,2,2
而在ES6当中,写法是这样的,是利用export 和import导入的
ES6模块是动态引用,并且不会缓存,模块里面的便令绑定其所在的模块,而是动态地去加载值,并且不能重新复制。
**es6模块的特点:
- 静态化,必须在顶部,不能使用条件语句,自动采用严格模式
- treeshaking和编译优化,以及webpack3中的作用域提升
- 外部可以拿到实时值,而非缓存值(是引用而不是copy)
**es6模块和commonjs模块的区别:
- CommonJS 模块输出的是一个值的拷贝,一旦输出一个值,模块内部的变化不会影响到这个值,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口并在使用的时候才进行调用。
**es6模块和commonjs模块的相同点:
- 两者都可以对对象内部属性的值进行改变
回调函数:
回调函数:
一个函数被作为参数传递给另一个函数(在这里我们把另一个函数叫做“otherFunction”),回调函数在otherFunction中被调用。
/注意到click方法中是一个函数而不是一个变量
//它就是回调函数
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
//或者
function click() { // 它就是回调函数
alert("Btn 1 Clicked");
}
$("#btn_1").click(click);
回调函数并不会马上被执行。它会在包含它的函数内的某个特定时间点被“回调”(就像它的名字一样)
实现回调函数的基本原理
使用命名函数或者匿名函数作为回调
像之前的例子一样,第一种方法就是匿名函数作为回调(使用了参数位置定义的匿名函数作为回调函数)。第二种方式就是命名函数作为回调(定义一个命名函数并将函数名作为变量传递给函数)
什么是回调地狱
说起回调地狱,首先想到的是异步,在js中我们经常会大量使用异步回调,例如使用ajax请求
我们来看下面这段代码:
function a(functionb(){
c(function d(){
})
})
我们发现上面代码大量使用了回调函数(将一个函数作为参数传递给另个函数)并且有许多 })结尾的符号,使得代码看起来很混乱。
如何解决回调地狱呢
第一种使用ES6中的Promise,中文翻译过来承诺,意思是在未来某一个时间点承诺返回数据给你。
Promise有三种状态:pending/reslove/reject 。pending就是未决,resolve可以理解为成功,reject可以理解为拒绝。
同时Promise常用的三种方法 then 表示异步成功执行后的数据状态变为reslove catch 表示异步失败后执行的数据状态变为reject all表示把多个没有关系的Promise封装成一个Promise对象使用then返回一个数组数据。
但是如果过多的使用then也会照成新的执行流程问题。所以我们的另一位主角登场了,那就是ES6中的Generator(生成器)
Generator(生成器)是一种有效利用内存的机制,一边循环一边计算生成数值的机制。通过配合Promise可以更加优雅的写异步代码
Iterator(迭代器):当我们实例化一个生成器函数之后,这个实例就是一个迭代器。可以通过next()方法去启动生成器以及控制生成器的是否往下执行。
yield/next:这是控制代码执行顺序的一对好基友。
通过yield语句可以在生成器函数内部暂停代码的执行使其挂起,此时生成器函数仍然是运行并且是活跃的,其内部资源都会保留下来,只不过是处在暂停状态。
在迭代器上调用next()方法可以使代码从暂停的位置开始继续往下执行。
当然生成器不是最完美的,它的语法让人难以理解,所以ES7推出了async/await (异步等待),多么贴切。yield + Promise的写法需要我们对拿到的promise的决议进行人工处理(区分成功或失败),在ES7中提供了async/await帮我们省掉了这个步骤
- 明确概念
- async函数就是generator函数的语法糖。
- async函数,就是将generator函数的
*
换成async,将yield替换成await。
- async函数对generator的改进
- 内置执行器,不需要使用next()手动执行。
- await命令后面可以是Promise对象或原始类型的值,yield命令后面只能是Thunk函数或Promise对象。
- 返回值是Promise。返回非Promise时,async函数会把它包装成Promise返回。(Promise.resolve(value))
- 作用 异步编程的终极解决方案。
- 通俗理解(个人理解) async/await,就是异步编程回调函数写法的替代方法。(使代码以同步方式的写法完成异步操作)
原理(执行顺序) 函数执行时,一旦遇到await就会返回。等到触发的异步操作完成(并且调用栈清空),再接着执行函数体内后面的语句。 【个人理解】
- await语句后面的代码,相当于回调函数。(即:await的下一行开始,都视作回调函数的内容)
- 回调函数会被压入microtask队列,当主线程调用栈被清空时,去microtask队列里取出各个回调函数,逐个执行。
- await只是让当前async函数内部、后面的代码等待,并不是所有代码都卡在这里。遇到await就先返回,执行async函数之后的代码。
Js原型链:
**什么是原型链?
- 每个对象都可以有一个原型对象,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找...... 这个操作被委托在整个原型链上,这个就是我们说的原型链了。
- 原型链的尽头指向null,原型链上未定义的属性返回undefined
- prototype属性,它是函数所独有的,它是从一个函数指向一个对象,这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法,prototype属性可以给函数和对象添加可共享(继承)的方法、属性。
- 每一个JavaScript实例对象(除了null )都具有的一个属性,叫__proto__,这个属性会指向该对象的原型,是查找某函数或对象的原型链方式。
- 每个原型都有一个 constructor 属性指向关联的构造函数,实例原型指向构造函数
Js中实现继承的几种方式:
- 原型链继承(父类的实例作为子类的原型)
优点:
简单易于实现,父类的新增的实例与属性子类都能访问
缺点:
可以在子类中增加实例属性,如果要新增加原型属性和方法需要在new 父类构造函数的后面
无法实现多继承
创建子类实例时,不能向父类构造函数中传参数 - 借用构造函数继承(伪造对象,经典继承
) 复制父类的实例属性给子类
优点:
解决了子类构造函数向父类构造函数传递参数的问题
可以实现多继承(call或者apply多个父类)
缺点:
方法都在构造函数中定义,无法复用
不能继承原型属性/方法,只能继承父类的属性和方法 - 实例继承(原型式继承)
优点:
不限制调用方式,简单易实现
缺点:
不能多次继承 - 组合式继承
调用父类的构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用
优点:
函数可以复用,不存在引用属性问题,可以继承属性和方法,并且可以继承原型的属性和方法
缺点:
调用了两次父类,产生了两个实例 - ES6继承
ES5继承和ES6继承的区别
es5继承首先是在子类中创建自己的this指向,最后将方法添加到this中
Child.prototype=new Parent() || Parent.apply(this) || Parent.call(this)
es6继承是使用关键字先创建父类的实例对象this,最后在子类class中修改this
闭包:
- 什么是闭包: 闭包是指有权访问另外一个函数作用域中的变量的函数。 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配。当在一个函数内定义另外一个函数就会产生闭包。
- 为什么要用:
- 匿名自执行函数:我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。除了每次使用变量都是用var关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,可以用闭包。
- 结果缓存:我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
- 封装:实现类和继承等。
闭包的应用:
实现每隔一秒输出1,2,3,4....
实现方式
- ES6 let 块级作用域
- ES5 闭包 匿名函数and函数自动执行,(function(i){})(i) 其中第一个()返回一个匿名函数,第二()起到立即执行的作用
实现add(1)(2)(3)输出6
闭包的优点
- 可以重复使用变量,并且不会造成变量污染
- 全局变量可以重复使用,但是容易造成变量污染。局部变量仅在局部作用域内有效,不可以重复使用,不会造成变量污染。闭包结合了全局变量和局部变量的优点。
- 可以用来定义私有属性和私有方法。
闭包的缺点
- 比普通函数更占用内存,会导致网页性能变差,在IE下容易造成内存泄露(分配了一些‘顽固的’内存,浏览器无法进行回收,就会导致后面所用内存不足)。
Js内存泄漏:
内存泄漏是指由于疏忽或者错误造成程序未能释放已经不再使用的内存,内存泄漏并非指内存在物理内存上的消失,而是应用程序在分配某段内存之后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,造成了内存的浪费。
- 意外的全局变量,js对未声明变量会在全局最高对象上创建它的引用
- console.log
- 闭包
- DOM泄漏,我们一般将常用的DOM。我们会采用变量引用的方式会将其缓存在当前环境
- 被遗忘的timers
Js垃圾回收机制:
JS的内存生命周期:
- 分配你所需要的内存
- 使用分配到的内存(读、写)
- 不需要时将其释放、归还
js垃圾回收机制:
- 自动垃圾回收机制就是找出那些不再继续使用的值,然后释放其占用的内存空间。垃圾回收器每隔固定的时间段就执行一次释放操作。
- js最常用的是通过标记清除的算法来找到哪些对象是不再继续使用的,上面例子中的a = null 其实就是做了一个释放引用的操作,让a原本对应的值失去引用,脱离执行环境,这个值会在下一次垃圾收集器执行操作时被找到并释放。因此,在适当的时候解除引用,是为页面获的更好性能的一个重要方式。
标记清除
这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。 垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量及被其引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。
引用计数
另一种不太常见的垃圾回收策略是引用计数。引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。
Ajax,fetch,Axios的区别:
ajax使用步骤
- 创建XmlHttpRequest对象
- 调用open方法设置基本请求信息
- 设置发送的数据,发送请求
- 注册监听的回调函数
- 拿到返回值,对页面进行更新
可这里的readyState的状态码代表什么呢?这里记录如下:
0:未初始化,但是已经创建了XHR实例
1:调用了open()函数
2:已经调用了send()函数,但还未收到服务器回应
3:正在接受服务器返回的数据
4:完成响应
- 传统Ajax值得是XMLHttpRequest(XHR),最早出现的发送后端请求技术,隶属于原生js中,核心是使用XMLHttoRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱。
优点:局部更新,原生支持,不需要插件
缺点:可能破坏浏览器的后退功能,产生回调地狱 - Jquery ajax是对原生XHR的封装,此外还添加了对JSONP的支持。
优点:很多
缺点:还是没有解决回调地狱的问题。 - Fetch是ajax的替代品,是ES6中出现的,使用了ES6中的promise对象,代码结构比ajax简单,参数类似Jquery ajax,但是fetch不是ajax的进一步封装,而是原生js,没有使用XHR对象
优点:语法简洁,易于理解。基于promise实现,支持async/await。更加底层,丰富的API,脱离了XHR,是ES规范里的新实现方式
缺点:只对网络请求报错,400,500都会当做成功的请求。默认不带cookie,需添加配置项。不支持超时控制。不能监测请求的进度。 - Axios是一个基于promise用于浏览器和node.js的http客户端,本质上是对XHR的封装,不过是基于promise实现,符合ES最新规范。
优点:从浏览器中创建XHR,支持promise的API,浏览器端支持防止CSRF(跨站请求伪造),提供了并发请求接口,从node.js创建http请求,拦截请求和相应,转换请求和响应数据,自动转换json数据。
JS跨域:
跨域产生的原因:跨域是由于浏览器的同源策略引起的,是指页面请求的接口地址,必须与页面url地址处于同域(即域名,端口,协议相同)上,是为了防止某域名下的接口被其他域名下的网页非法调用,是浏览器对js施加的安全限制。
后端解决跨域的方式通常有两个:
JSONP:
利用script标签可跨域的特点,在跨域脚本中可以直接回调当前脚本的函数,即通过动态创建script,再请求一个带参的网址实现跨域通信。
在jQuery中如何通过JSONP来跨域获取数据?
第一种方法是在ajax函数中设置dataType为'jsonp':
第二种方法是利用getJSON来实现,只要在地址中加上callback=?
参数即可:
当你需要的跨域时候,创建一个script标签,加到head中,设置这个script的src值为其它域的url,最好加上一个随机参数,以防WEB缓存机制。如生成这样的一个标签:
<script type="text/javascript" src="http://abc.com/js.php?t=1234" ></script>
当然这应该通过document.createElement来完成。
CORS
服务器设置http响应头中Access-Control-Allow-Origin
值。
关于cookie如何在跨域请求携带的问题:
通过设置 withCredentials: true ,发送Ajax时,Request header中便会带上 Cookie 信息。
服务器端通过在响应 header 中设置 Access-Control-Allow-Credentials = true 来允许携带cookie。
Access-Control-Allow-Origin:""和Access-Control-Allow-Credentials", "true" 同时设置的时候Access-Control-Allow-Origin不能设置为"*",只能设置为具体的域名。
以上两个方法严重依赖后端的协助。
<?php
//加头部解决跨域问题
header('Access-Control-Allow-Origin: *');
如何用前端的方式实现跨域?
-
正向代理(forward)意思是一个位于客户端和原始服务器 (origin server) 之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标 (原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。
正向代理是为客户端服务的,客户端可以根据正向代理访问到它本身无法访问到的服务器资源。
正向代理对客户端是透明的,对服务端是非透明的,即服务端并不知道自己收到的是来自代理的访问还是来自真实客户端的访问。 -
反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
反向代理是为服务端服务的,反向代理可以帮助服务器接收来自客户端的请求,帮助服务器做请求转发,负载均衡等。
反向代理对服务端是透明的,对客户端是非透明的,即客户端并不知道自己访问的是代理服务器,而服务器知道反向代理在为他服务。
总结:
正向代理即是客户端代理,即中间服务器代理客户端,服务端不知道实际发起请求的客户端
反向代理即是服务端代理,中间服务器代理服务端,客户端不知道实际提供服务的服务端。
简单的对比
使用charles等正向代理方式比较简单,需要掌握的知识点也比较少。但相应的其可配置性较弱,仅适合中小型项目使用。
使用nginx的反向代理则相对复杂一些,需要了解基本的nginx配置。但其可配置性较强,支持URL的正则匹配,设置优先级等,适合复杂的项目使用。
document.domain + iframe跨域:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
location.hash + iframe跨域:a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。
window.name + iframe跨域:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。
深拷贝与浅拷贝:
深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
- 浅复制 —-只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。
- 深复制 —-在计算机中开辟了一块新的内存地址用于存放复制的对象。如果源对象发生了改变,复制的对象并不会发生变化。
通俗一点理解就是浅拷贝出来的数据并不独立,如果被复制的对象改变了,那么浅拷贝的对象也会改变,深拷贝之后就会完全独立,与拷贝对象断绝关系。
深拷贝的实现方式:
- JSON.parse(JSON.stringify()) 原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。这种方法虽然可以实现数组或对象深拷贝,但不能处理函数。
- 手写递归方法 递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
Js中的同步与异步:
首先js是一门单线程的语言,无论如何,做事情的只有一条线程,同步和异步的差别就在于这个线程上各个流程的执行顺序不同。
所有任务分成两种,一种是同步任务,同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务,异步任务是指不进驻主线程,而进入任务队列的任务,只有等主线程任务执行完毕,任务队列开始通知主线程,请求执行任务,该任务才会进入主线程执行。
具体来说,异步的执行机制如下,
- 所有同步任务都在主线程上执行,形成一个执行栈
- 主线程之外,存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件。
- 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件,对应哪些异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的三个步骤
任务队列就是一个事件的队列,也可以理解为消息的队列,IO设备完成一项任务,就在任务队列中添加一个事件,表示相关的异步任务可以进入执行栈了,主线程读取任务队列,就是读取其中的事件,任务队列中的事件,除了IO设备事件意外,还包括一些用户产生的时间,比如鼠标点击,页面滚动等等,相对耗时,只要制定过这些事件的回调函数,在这些事件发生时,就会进入任务队列,等待主线程读取。
Js宏任务与微任务与事件循环:
js中的任务分为宏任务和微任务,在挂起任务时,js引擎会将所有任务按照这个类别分别分到这两个队列中,首先执行主线程上的任务,执行完毕之后,会执行微任务队列中的所有任务,然后再取出宏任务队列中的第一个宏任务,按照主线程任务,所有微任务队列任务的顺序执行,完毕后取出微任务队列中的所有任务顺序执行,之后再取宏任务队列中的一个宏任务,执行主线程任务,并执行其所有微任务,如此循环,直到所有任务队列为空。
浏览器环境下:
宏任务一般有:setTimeout,setInterval,requestAnimationFrame
微任务一般有:process.nextTick,Promise.then catch finally
Js中的事件循环:
先执行一个宏任务,执行结束后,如果有可执行的微任务,则执行所有的微任务,并开始下一个宏任务,如果没有,则直接执行下一个宏任务,知道所有任务执行完毕。
例子:
首先浏览器执行js进入第一个宏任务进入主线程, 直接打印console.log('1')
遇到 setTimeout 分发到宏任务Event Queue中
遇到 process.nextTick 丢到微任务Event Queue中
遇到 Promise,new Promise 直接执行 输出 console.log('7');
执行then 被分发到微任务Event Queue中
第一轮宏任务执行结束,开始执行微任务 打印 6,8
第一轮微任务执行完毕,执行第二轮宏事件,执行setTimeout
先执行主线程宏任务,在执行微任务,打印'2,4,3,5'
整段代码,共进行了两次事件循环,完整的输出为1,7,6,8,2,4,3,5,
浏览器环境下和node.js环境下的宏任务和微任务的执行顺序是不一样的
Js中的事件委托:
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
事件委托的原理:事件委托是利用事件的冒泡来实现的,冒泡就是事件从最深的节点开始,然后逐步向上传播事件。举个例子,页面上有这么一个结点树div>ul>li>a,比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div添加点击事件,那么里面的ul,li,a触发点击事件的时候,都会冒泡到最外成的div上,所以都会触发,这就是事件委托,委托他们父级代为执行事件。
事件委托的应用:
点击不同的li显示其内容
传统方法与事件委托方法
传统方式的DOM操作较多,每次点击li都要遍历所有li的事件
而事件委托则可以只操作当前点击的dom
浏览器事件机制
事件传播的三个阶段:捕获,目标对象,冒泡。
- 其中捕获(Capture)是 事件对象(event object) 从 window 派发到 目标对象父级的过程。
- 目标(Target)阶段是 事件对象派发到目标元素时的阶段,如果事件类型指示其不冒泡,那事件传播将在此阶段终止。
- 冒泡(Bubbling)阶段 和捕获相反,是以目标对象父级到 window 的过程。
在任一阶段调用 stopPropagation 都将终止本次事件的传播。
绑定在被点击元素的事件是按照代码的顺序发生的,其他非绑定的元素则是通过冒泡或者捕获的触发。按照W3C的标准,先发生捕获事件,后发生冒泡事件。所以事件的整体顺序是:非目标元素捕获 -> 目标元素代码顺序 -> 非目标元素冒泡。
Js的事件绑定方式:
- 夹杂在html标签内
- on+"事件"
但如果想单击一次执行多个函数时,这种绑定方式就无法完成了:因为同一个事件,在执行多个函数时会发生覆盖 - element.addEventListener(事件名,函数,冒泡/捕获)
上面两个函数fA(),fB()都会执行,不会发生覆盖现象。
第一个是事件类型,不需要on前缀,但事件名需加 " " ;
第二个参数是发生这个事件的时候要调用的函数;
第三个是布尔值的true或false.(默认值是false)
false代码是以冒泡型事件流进行处理,一般都选用false.
true意味着代码以捕获型事件流进行处理,不是必须不推荐使用。
Js执行栈
Js执行上下文:
执行上下文就是JavaScript 在被解析和运行时环境的抽象概念,JavaScript 运行任何代码都是在执行上下文环境中运行的,执行上下文包括三个周期:创建——运行——销毁,重点说一下创建环节。
创建环节(函数被调用,但未未被执行)会执行三件事情
- 创建变量对象,首先初始化函数的arguments对象,提升函数声明和变量声明,从近到远查找函数运行所需要的变量。
- 创建作用域链,作用域就是一个独立的地盘,让变量不会相互干扰,当前作用域没有定义的变量,这成为 自由变量。自由变量会向上一直寻找,要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,如果最终没有就为undefined。这种层层之间的调用关系就构成了作用域链。
- 确定this指向,this、apply、call的指向
Js执行栈 JavaScript 引擎创建了执行栈来管理执行上下文,可以把执行栈认为成一个储存函数调用的栈结构,遵循先进后出的原则。
Js中有三种执行上下文
- 全局执行上下文,默认的,在浏览器中是window对象,并且this在非严格模式下指向它。
- 函数执行上下文,JS的函数每当被调用时会创建一个上下文。
- Eval执行上下文,eval函数会产生自己的上下文,这里不讨论。
Js执行栈工作过程
1 JavaScript引擎是单线程执行,所有代码都是排队执行。
- 一开始执行的是全局代码,首先创建全局的执行上下文,然后将该执行上下文压入执行栈中。
- 每当执行一个函数,就会创建该函数的执行上下文,然后将其压入执行栈的顶部,函数执行完成后,执行上下文从底部退出,等待垃圾回收。
- 浏览器js总是访问执行栈顶层的执行上下文。
- 全局上下文只有唯一的一个,它在浏览器关闭时出栈
进程和线程的区别:
- 进程是资源分配的最小单位,线程是CPU调度的最小单位
- 线程在进程下行进(单纯的车厢无法运行)
- 一个进程可以包含多个线程(一辆火车可以有多个车厢)
- 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
- 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
- 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
- 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上) 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"
- 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”
Js判断变量为数组的方法:
Instanceof
object instanceof constructor
alert(a instanceof Array); // true,
变量 instanceof 类型 返回的是布尔值
其中instanceof运算符用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上
instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false。
2. arr.__proto__===Array.Prototype
其中__proto__
为对象实例的属性,指向该对象的原型
Prototype为构造函数的属性,指向该构造函数的原型
3. arr.constructor===Array
其中constructor为实例原型的属性,指向该实例的构造函数
4. Object.prototype.toString().call(arr)
其中返回值为字符串’[Object Array]’
5. Array.isArray(arr)
ES6中的新方法,返回值为true或false
Js数组去重方法:
- Array.filter() + indexOf
- 使用 for...of + includes()
- 双重 for 循环
- Array.sort(),然后比较相邻元素是否相等,从而排除重复项。
- for...of + Object 利用对象的属性不能相同的特点进行去重
- ES6的new Set() 数组去重要么使用for...of + Object方式,要么使用ES6的 new Set()方式。从执行结果看for...of + Object的效率应该是最高的(只在当前量级的计算结果来看)。
Js防抖和节流:
Js防抖的基本思路:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。 JS中节流的基本思路是:规定一个期限时间,在该时间内,触发事件的回调函数只能执行一次,如果期限时间内回调函数被多次触发,则只有一次能生效。