前端面试题
1.0 跨域问题
概念
- 跨域问题是由于javascript语言安全限制中的同源策略造成的.
- 简单来说,同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合
解决方法
-
1.0 利用Vue+axios
-
因为axios中只能使用get和post方法来进行请求数据,没有提供jsonp等方法进行跨域访问数据
-
方法:配置代理
-
既然使用axios直接进行跨域访问不可行,我们就需要配置代理了
-
代理可以解决的原因:因为客户端请求服务端的数据是存在跨域问题的,而服务器和服务器之间可以相互请求数据,是没有跨域的概念(如果服务器没有设置禁止跨域的权限问题),也就是说,我们可以配置一个代理的服务器可以请求另一个服务器中的数据,然后把请求出来的数据返回到我们的代理服务器中,代理服务器再返回数据给我们的客户端,这样我们就可以实现跨域访问数据。
-
具体方案
-
一、安装所需中间件和插件等,比如axios,http-proxy-middleware等
-
二、配置BaseUrl
-
访问豆瓣网跨域,如下解决
-
axios.get("http://api.douban.com/v2/movie/top250") .then(res=>{ console.log(res) }) .catch(err=>{ console.log(err) })
-
在main.js中,配置数据所在服务器的前缀(即固定部分)
-
axios.defaults.baseURL = ‘/api’ //关键代码
-
-
三、配置代理
- 在config文件夹下的index.js文件中的proxyTable字段中,作如下处理
-
dev: { env: require('./dev.env'), port: 8090, autoOpenBrowser: true, assetsSubDirectory: 'static', assetsPublicPath: '/', proxyTable: { '/api': { target:'http://api.douban.com/v2', // 你请求的第三方接口 changeOrigin:true, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题 pathRewrite:{ // 路径重写, '^/api': '' // 替换target中的请求地址,也就是说以后你在请求http://api.douban.com/v2/XXXXX这个地址的时候直接写成/api即可。 } } }, }
-
四、在具体使用axios的地方,修改url如下即可
-
axios.get("/movie/top250").then((res) => { res = res.data if (res.errno === ERR_OK) { this.themeList=res.data; } }).catch((error) => { console.warn(error) })
-
-
原理:因为我们给url加上了前缀/api,我们访问/movie/top250就当于访问了:localhost:8080/api/movie/top250(其中localhost:8080是默认的IP和端口)。
在index.js中的proxyTable中拦截了/api,并把/api及其前面的所有替换成了target中的内容,因此实际访问Url是http://api.douban.com/v2/movie/top250。
至此,纯前端配置代理解决axios跨域得到解决。
-
-
-
-
2.0 jsonp
-
3.0 document.domain + iframe (iframe的使用主要是为了ajax通信)
-
4.0 使用window.name + iframe
-
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的.
-
但实际情况中我们也不能这样跳来跳去,所以可以用隐藏的iframe来实现数据的获取
<!-- 本地 --> <script type="text/javascript"> window.name = 'myName'; </script>
<!-- 远程 --> <iframe id="myIframe" src="http://localhost:8080/demoff/a.html" style="display:none;" onload="getData()"></iframe> <script type="text/javascript"> // 初始iframe加载后即执行 function getData(){ var myIframe = document.getElementById('myIframe'); // 第一次iframe加载后即可拿到window.name值,然后设置其src为同域的文件(随意) myIframe.src = './index.php'; // 设置好同域之后,就可以操作iframe的数据了,即可拿到该name值 myIframe.onload = function(){ var name = myIframe.contentWindow.name; alert(name); }; } </script>
-
-
-
5.0 使用 window.postMessage方法
-
这个东西是HTML5引入的
-
window.postMessage(message,targetOrigin)
- 调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;
第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。
需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
- 调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;
-
还是举个栗子:(假设现在是远程提供数据,本地获取数据)
-
<!-- 远程 --> <body> <!-- 一个 iframe 作为中间件 --> <iframe id="myIframe" src="http://localhost:8080/demoff/a.html" style="display:none;" onload="setData()"></iframe> <script type="text/javascript"> // 初始iframe加载后即执行 function setData(){ var myIframe = document.getElementById('myIframe'); var win = myIframe.contentWindow; //win.postMessage('My name is null','*'); win.postMessage('My name is null','http://localhost:8080/'); } </script> </body> <!-- 本地 --> <body> <script type="text/javascript"> window.onmessage = function(e){ e = e || window.event; alert(e.data); }; </script> </body>
-
-
2.0 性能优化与缓存
前端回流与重绘
-
render tree: 渲染树
-
DOM节点分类
- 其实DOM节点可以分为 可视化节点 和 非可视化节点,
像 div、p 等这种结构性的标签节点可被称为可视化节点,
而 script、meta 等这种在页面上显示不出来的节点则被称为非可视化节点
- 其实DOM节点可以分为 可视化节点 和 非可视化节点,
-
render树的生成
- 浏览器获取 HTML 文件,然后对文件进行解析,形成 DOM Tree
与此同时,进行 CSS 解析,生成 Style Rules
接着将 DOM Tree 与 Style Rules 合成为 Render Tree
元素在页面中布局,然后绘制 - render 树就是根据 可视化节点 和 css 样式表 结合诞生出来的树
- 注意:PS: display: none 的元素会出现在 DOM树 中,但不会出现在 render 树中
- 浏览器获取 HTML 文件,然后对文件进行解析,形成 DOM Tree
-
-
回流
- 当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。
-
重绘
- 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
-
回流必将引起重绘,而重绘不一定会引起回流
- 回流比重绘的代价要更高,回流的花销跟render tree有多少节点需要重新构建有关系
-
距离区分
- 比如:只有颜色改变的时候就只会发生重绘而不会引起回流
- 当页面布局和几何属性改变时就需要回流
比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变
前端性能优化
-
1.0 减少请求数量
-
【合并】
-
如果不进行文件合并,有如下3个隐患
- 1、文件与文件之间有插入的上行请求,增加了N-1个网络延迟
2、受丢包问题影响更严重
3、经过代理服务器时可能会被断开
- 1、文件与文件之间有插入的上行请求,增加了N-1个网络延迟
-
但是,文件合并本身也有自己的问题
1、首屏渲染问题
2、缓存失效问题- 所以,对于文件合并,有如下改进建议
1、公共库合并
2、不同页面单独合并
- 所以,对于文件合并,有如下改进建议
-
-
【图片处理】
-
雪碧图
- CSS雪碧图是以前非常流行的技术,把网站上的一些图片整合到一张单独的图片中,可以减少网站的HTTP请求数量,但是当整合图片比较大时,一次加载比较慢。随着字体图片、SVG图片的流行,该技术渐渐退出了历史舞台
-
使用字体图标来代替图片
-
Base64
- 将图片的内容以Base64格式内嵌到HTML中,可以减少HTTP请求数量。但是,由于Base64编码用8位字符表示信息中的6个位,所以编码后大小大约比原始值扩大了 33%
-
-
【减少重定向】
-
重定向
- 重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向
-
尽量避免使用重定向,当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载,降低了用户体验
如果一定要使用重定向,如http重定向到https,要使用301永久重定向,而不是302临时重定向。因为,如果使用302,则每一次访问http,都会被重定向到https的页面。而永久重定向,在第一次从http重定向到https之后 ,每次访问http,会直接返回https的页面
-
-
【使用缓存】
- 使用cach-control或expires这类强缓存时,缓存不过期的情况下,不向服务器发送请求。强缓存过期时,会使用last-modified或etag这类协商缓存,向服务器发送请求,如果资源没有变化,则服务器返回304响应,浏览器继续从本地缓存加载资源;如果资源更新了,则服务器将更新后的资源发送到浏览器,并返回200响应
-
【不使用CSS @import】
- CSS的@import会造成额外的请求
-
【避免使用空的src和href】
- a标签设置空的href,会重定向到当前的页面地址
form设置空的method,会提交表单到当前的页面地址
- a标签设置空的href,会重定向到当前的页面地址
-
-
2.0 减小资源大小
-
【压缩】
-
1、 HTML压缩
- HTML代码压缩就是压缩在文本文件中有意义,但是在HTML中不显示的字符,包括空格,制表符,换行符等
-
2、CSS压缩
- CSS压缩包括无效代码删除与CSS语义合并
-
3、JS压缩与混乱
- JS压缩与混乱包括无效字符及注释的删除、代码语义的缩减和优化、降低代码可读性,实现代码保护
-
4、图片压缩
- 针对真实图片情况,舍弃一些相对无关紧要的色彩信息
-
-
【webp】
- 在安卓下可以使用webp格式的图片,它具有更优的图像数据压缩算法,能带来更小的图片体积,同等画面质量下,体积比jpg、png少了25%以上,而且同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性
-
【开启gzip】
- HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来。一般对纯文本内容可压缩到原大小的40%
-
-
3.0 优化网络连接
-
【使用CDN】
- CDN全称是Content Delivery Network,即内容分发网络,它能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度
-
【使用DNS预解析】
-
当浏览器访问一个域名的时候,需要解析一次DNS,获得对应域名的ip地址。在解析过程中,按照浏览器缓存、系统缓存、路由器缓存、ISP(运营商)DNS缓存、根域名服务器、顶级域名服务器、主域名服务器的顺序,逐步读取缓存,直到拿到IP地址
DNS Prefetch,即DNS预解析就是根据浏览器定义的规则,提前解析之后可能会用到的域名,使解析结果缓存到系统缓存中,缩短DNS解析时间,来提高网站的访问速度 -
方法
-
方法是在 head 标签里面写上几个 link 标签
<link rel="dns-prefecth" href="https://www.google.com"> <link rel="dns-prefecth" href="https://www.google-analytics.com">
对以上几个网站提前解析 DNS,由于它是并行的,不会堵塞页面渲染,这样可以缩短资源加载的时间
-
-
-
【并行连接】
- 由于在HTTP1.1协议下,chrome每个域名的最大并发数是6个。使用多个域名,可以增加并发数
-
【持久连接】
- 使用keep-alive或presistent来建立持久连接,持久连接降低了时延和连接建立的开销,将连接保持在已调谐状态,而且减少了打开连接的潜在数量
-
【管道化连接】
- 在HTTP2协议中,可以开启管道化连接,即单条连接的多路复用,每条连接中并发传输多个资源,这里就不需要添加域名来增加并发数了
-
-
4.0 优化资源加载
-
【资源加载位置】
-
通过优化资源加载位置,更改资源加载时机,使尽可能快地展示出页面内容,尽可能快地使功能可用
- 1、CSS文件放在head中,先外链,后本页
2、JS文件放在body底部,先外链,后本页
3、处理页面、处理页面布局的JS文件放在head中,如babel-polyfill.js文件、flexible.js文件
4、body中间尽量不写style标签和script标签
- 1、CSS文件放在head中,先外链,后本页
-
-
【资源加载时机】
1、异步script标签
-
-
defer: 异步加载,在HTML解析完成后执行。defer的实际效果与将代码放在body底部类似
-
- defer属性指明本元素所含的脚本不会修改DOM,因此代码能安全的延迟执行。
-
- 在script标签有个属性叫defer,翻译成中文就是推迟的意思。这个属性只能用在外部文件形式的script标签中,这样该标签引入的JavaScript代码能够提前加载,但是会延迟到整个页面文档都解析完毕之后再运行。如果有多个带有defer属性的script标签,它们会按照顺序进行执行。
-
defer和async都是可选的,且只对外部脚本文件有效
- 区别
-
- defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的
-
- async 则是一个乱序执行的主,对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics。
-
async: 异步加载,加载完成后立即执行
-
- 这里我们学习script标签的另一个属性:async,翻译过来就是异步的意思,该属性同样也只适用于外部脚本。带有该属性的JavaScript代码在下载的时候不会阻塞页面的渲染,与defer属性一样。不同的是当脚本文件下载完成后,浏览器会立即停止页面渲染并执行JavaScript代码。如果有多个带有async的script,它们的执行顺序与出现的次序无关,哪个JavaScript代码先下载完就先执行哪个。
-
2、模块按需加载
-
- 在SPA等业务逻辑比较复杂的系统中,需要根据路由来加载当前页面需要的业务模块
按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。 webpack 提供了两个类似的技术,优先选择的方式是使用符合 ECMAScript 提案 的 import() 语法。第二种则是使用 webpack 特定的 require.ensure
- 在SPA等业务逻辑比较复杂的系统中,需要根据路由来加载当前页面需要的业务模块
-
3、使用资源预加载preload和资源预读取prefetch
-
- preload让浏览器提前加载指定资源,需要执行时再执行,可以加速本页面的加载速度
-
- prefetch告诉浏览器加载下一页面可能会用到的资源,可以加速下一个页面的加载速度
-
4、资源懒加载与资源预加载资源延迟加载也称为懒加载,延迟加载资源或符合某些条件时才加载某些资源
资源预加载是提前加载用户所需的资源,保证良好的用户体验 资源懒加载和资源预加载都是一种错峰操作,在浏览器忙碌的时候不做操作,浏览器空间时,再加载资源,优化了网络性能 -
5.0 减少重绘回流
-
【样式设置】
- 1、避免使用层级较深的选择器,或其他一些复杂的选择器,以提高CSS渲染效率
- 2、避免使用CSS表达式,CSS表达式是动态设置CSS属性的强大但危险方法,它的问题就在于计算频率很快。不仅仅是在页面显示和缩放时,就是在页面滚动、乃至移动鼠标时都会要重新计算一次
- 3、元素适当地定义高度或最小高度,否则元素的动态内容载入时,会出现页面元素的晃动或位置,造成回流
- 4、给图片设置尺寸。如果图片不设置尺寸,首次载入时,占据空间会从0到完全出现,上下左右都可能位移,发生回流
- 5、不要使用table布局,因为一个小改动可能会造成整个table重新布局。而且table渲染通常要3倍于同等元素时间
- 6、能够使用CSS实现的效果,尽量使用CSS而不使用JS实现
-
【渲染层】
- 1、此外,将需要多次重绘的元素独立为render layer渲染层,如设置absolute,可以减少重绘范围
- 2、对于一些进行动画的元素,使用硬件渲染,从而避免重绘和回流
-
【DOM优化】
-
1、缓存DOM
- 由于查询DOM比较耗时,在同一个节点无需多次查询的情况下,可以缓存DOM
- const div = document.getElementById(‘div’)
-
2、减少DOM深度及DOM数量
- HTML 中标签元素越多,标签的层级越深,浏览器解析DOM并绘制到浏览器中所花的时间就越长,所以应尽可能保持 DOM 元素简洁和层级较少。
-
3、批量操作DOM
- 由于DOM操作比较耗时,且可能会造成回流,因此要避免频繁操作DOM,可以批量操作DOM,先用字符串拼接完毕,再用innerHTML更新DOM
-
4、批量操作CSS样式
- 通过切换class或者使用元素的style.csstext属性去批量操作元素样式
-
5、在内存中操作DOM
- 使用DocumentFragment对象,让DOM操作发生在内存中,而不是页面上
-
6、DOM元素离线更新
- 对DOM进行相关操作时,例、appendChild等都可以使用Document Fragment对象进行离线操作,带元素“组装”完成后再一次插入页面,或者使用display:none 对元素隐藏,在元素“消失”后进行相关操作
-
7、DOM读写分离
- 浏览器具有惰性渲染机制,连接多次修改DOM可能只触发浏览器的一次渲染。而如果修改DOM后,立即读取DOM。为了保证读取到正确的DOM值,会触发浏览器的一次渲染。因此,修改DOM的操作要与访问DOM分开进行
-
8、事件代理
- 事件代理是指将事件监听器注册在父级元素上,由于子元素的事件会通过事件冒泡的方式向上传播到父节点,因此,可以由父节点的监听函数统一处理多个子元素的事件
- 利用事件代理,可以减少内存使用,提高性能及降低代码复杂度
-
9、防抖和节流
- 使用函数节流(throttle)或函数去抖(debounce),限制某一个方法的频繁触发
-
10、及时清理环境
- 及时消除对象引用,清除定时器,清除事件监听器,创建最小作用域变量,可以及时回收内存
-
-
-
6.0 性能更好的API
-
1、用对选择器
-
选择器性能排序
- id选择器(#myid)
类选择器(.myclassname)
标签选择器(div,h1,p)
相邻选择器(h1+p)
子选择器(ul > li)
后代选择器(li a)
通配符选择器(*)
属性选择器(a[rel=“external”])
伪类选择器(a:hover,li:nth-child)
- id选择器(#myid)
-
-
2、使用requestAnimationFrame来替代setTimeout和setInterval
- 希望在每一帧刚开始的时候对页面进行更改,目前只有使用 requestAnimationFrame 能够保证这一点。使用 setTimeout 或者 setInterval 来触发更新页面的函数,该函数可能在一帧的中间或者结束的时间点上调用,进而导致该帧后面需要进行的事情没有完成,引发丢帧
-
3、使用IntersectionObserver来实现图片可视区域的懒加载
- 传统的做法中,需要使用scroll事件,并调用getBoundingClientRect方法,来实现可视区域的判断,即使使用了函数节流,也会造成页面回流。使用IntersectionObserver,则没有上述问题
-
4、使用web worker
- 客户端javascript一个基本的特性是单线程:比如,浏览器无法同时运行两个事件处理程序,它也无法在一个事件处理程序运行的时候触发一个计时器。Web Worker是HTML5提供的一个javascript多线程解决方案,可以将一些大计算量的代码交由web Worker运行,从而避免阻塞用户界面,在执行复杂计算和数据处理时,这个API非常有用
-
但是,使用一些新的API的同时,也要注意其浏览器兼容性
-
-
7.0 webpack优化
-
【打包公共代码】
- 使用CommonsChunkPlugin插件,将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这会带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件
webpack 4 将移除 CommonsChunkPlugin, 取而代之的是两个新的配置项 optimization.splitChunks 和optimization.runtimeChunk
通过设置 optimization.splitChunks.chunks: “all” 来启动默认的代码分割配置项
- 使用CommonsChunkPlugin插件,将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这会带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件
-
【动态导入和按需加载】
- webpack提供了两种技术通过模块的内联函数调用来分离代码,优先选择的方式是,使用符合 ECMAScript 提案 的 import() 语法。第二种,则是使用 webpack 特定的 require.ensure
-
【剔除无用代码】
- tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup
JS的tree shaking主要通过uglifyjs插件来完成,CSS的tree shaking主要通过purify CSS来实现的
- tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup
-
【长缓存优化】
-
1、将hash替换为chunkhash,这样当chunk不变时,缓存依然有效
-
2、使用Name而不是id
- 每个 module.id 会基于默认的解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变
-
-
【公用代码内联】
- 使用html-webpack-inline-chunk-plugin插件将mainfest.js内联到html文件中
-
3.0 http与https过程
https
-
HTTPS使用SSL(Secure Socket Layer)和TLS(Transport Layer Security)这两个协议
-
SSL速度慢的原因
- 和使用HTTP相比,网络负载肯可能会变慢2~100倍。除去TCP连接、发送HTTP请求/响应以外,还必须进行SSL通信,因此整体上处理通信量不可避免会增加。
- 另一点是SSL必须进行加密处理。在服务器和客户端都需要进行加密和解密的运算处理。因此从结果上讲,比起HTTP会更多地消耗服务器和客户端的硬件资源,导致负载增强。
-
HTTPS的安全通信机制
- 1.0 认证服务器。浏览器内置一个受信任的CA机构列表,并保存了这些CA机构的证书。第一阶段服务器会提供经CA机构认证颁发的服务器证书,如果认证该服务器证书的CA机构,存在于浏览器的受信任CA机构列表中,并且服务器证书中的信息与当前正在访问的网站(域名等)一致,那么浏览器就认为服务端是可信的,并从服务器证书中取得服务器公钥,用于后续流程。否则,浏览器将提示用户,根据用户的选择,决定是否继续。当然,我们可以管理这个受信任CA机构列表,添加我们想要信任的CA机构,或者移除我们不信任的CA机构。
- 2.0 协商会话密钥。客户端在认证完服务器,获得服务器的公钥之后,利用该公钥与服务器进行加密通信,协商出两个会话密钥,分别是用于加密客户端往服务端发送数据的客户端会话密钥,用于加密服务端往客户端发送数据的服务端会话密钥。在已有服务器公钥,可以加密通讯的前提下,还要协商两个对称密钥的原因,是因为非对称加密相对复杂度更高,在数据传输过程中,使用对称加密,可以节省计算资源。另外,会话密钥是随机生成,每次协商都会有不一样的结果,所以安全性也比较高。
- 3.0 加密通讯。此时客户端服务器双方都有了本次通讯的会话密钥,之后传输的所有Http数据,都通过会话密钥加密。这样网路上的其它用户,将很难窃取和篡改客户端和服务端之间传输的数据,从而保证了数据的私密性和完整性。
4.0 页面加载顺序
1.0
js具有阻塞特性,当浏览器在执行js代码时,不能同时做其它事情,即
2.0
当浏览器从服务器接收到了HTML文档,并把HTML在内存中转换成DOM树,在转换的过程中如果发现某个节点(node)上引用了CSS或者IMAGE,就会再发1个request去请求CSS或image,然后继续执行下面的转换,而不需要等待request的返回,当request返回后,只需要把返回的内容放入到DOM树中对应的位置就OK。但当引用了JS的时候,浏览器发送1个js request就会一直等待该request的返回。因为浏览器需要1个稳定的DOM树结构,而JS中很有可能有代码直接改变了DOM树结构,比如使用document.write 或 appendChild,甚至是直接使用的location.href进行跳转,浏览器为了防止出现JS修改DOM树,需要重新构建DOM树的情况,所以就会阻塞其他的下载和呈现.
3.0
由于,JS的这种阻塞特性,每次遇到
- 显见,所有
5.0 关于CSS+HTML的一些知识
CSS
-
CSS优先级及权重计算
-
分类
-
通常可以将css 的优先级由高到低分为6组
-
1.0 第一优先级
- 无条件有限的属性只需要在属性后面使用! important。它会覆盖页面内任何位置定义的元素样式。
-
2.0 第二优先级
- 在HTML中给元素标签加style,即内联样式。该方法会造成CSS难以管理,所以不推荐使用。
-
3.0 第三优先级
- 有一个或多个id选择器来定义。例如,#id{margin: 0}会覆盖.classname{margin: 3px}
-
4.0 第四优先级
- 有一个或多个类选择器、属性选择器、伪类选择器定义。如.classname{margin: 3px}会覆盖div{margin: 6px}
-
5.0 第五优先级
- 有一个或多个类型选择器定义。如div{margin: 6px}覆盖*{margin: 10px;}
-
6.0 第六优先级
- 通配选择器,如*{margin: 6px;}
-
-
如果两个规则的特殊性相同,那么后定义的规则优先
-
-
权重计算
-
4个等级的定义
-
特殊的
- !import,优先级 Infinity
-
第一等级
- 代表内联样式,如style=“”,权值为1000
-
第二等级
- 代表ID选择器,如#content,权值为100
-
第三等级
- 代表类,伪类和属性选择器,如.content,权值为10
-
第四等级
- 代表标签选择器和伪元素选择器,如div p,权值为1
-
注意:通用选择器(*),子选择器(>),和相邻同胞选择器(+)并不在这个等级中, 所以他们的权值为0
-
-
-
解决优先级不起作用
- 如果遇到了似乎没有起作用的css规则,很可能是出现了特殊性冲突。这时可以在选择其中添加他的一个父元素的ID,从而提高它的特殊性,如果这能解决问题,就说明样式表中其他地方很可能有更特殊的规则,他覆盖了你的规则,此时需要检查代码,解决特殊性冲突,让代码尽可能的简洁。
-
-
CSS盒模型
-
什么是CSS盒模型
-
定义
- CSS假定所有的HTML文档元素都生成一个描述该元素在HTML文档布局中所占空间的矩形元素框,可以形象地将其看作是一个盒子。通过定义一系列与盒子相关的属性,可极大地丰富和促进各个盒子乃至整个HTML文档的表现效果和布局结构。
-
组成
- content(内容区):元素的宽和高;
border(边框区):盒子的边缘;
padding(填充区):为了使页面布局看起来美观大方,盒子里的内容区会用padding来解决父元素和子元素的位置关系;
margin(外边界区):控制同辈元素之间的位置关系。
- content(内容区):元素的宽和高;
-
-
HTML
-
标签分类
-
HTML标签的类型可分为三种
-
块级标签
-
块状标签:标签独占一行,可指定宽、高。
-
特性:
- 1.能够识别宽高
2.margin和padding的上下左右均对其有效
可以自动换行
3.多个块状元素标签写在一起,默认排列方式为从上至下
4.可以使用margin:0 auto居中对齐
- 1.能够识别宽高
-
常用的块状元素有
div、p、h1…h6、ol、ul、dl、table、address、blockquote、form
-
-
行内标签(也称内联标签)
-
行内标签
- 内联(行级)标签:标签在一行内,宽度与高度由内容决定,只有在内容超过HTML的宽度时,才会换行。
-
特性:
- 1.无法设置宽高
2.margin上下无效,只有左右有效果,padding都有效果,会撑大空间;box-sizing:border-box;无效,因为该属性针对盒模型。
3.不会自动换行
- 1.无法设置宽高
-
常用的内联元素有:
a、span、i、em、strong、label、q、var、cite、code
-
-
内联块状标签
-
内联块状标签:
- 内联块状元素(inline-block)就是同时具备内联元素、块状元素的特点,代码display:inline-block就是将元素设置为内联块状元素。(css2.1新增),img、input标签就是这种内联块状标签。
-
-
-
特性:
-
-
- 不会自动换行
-
- 能够设置宽高
-
- 默认排列方式为从左到右
-
- 可以使用text-align:center使内容相对于父盒子水平居中对齐,例如img标签,可以使用text-align:center,相对父盒子居中对齐、margin:0 auto无效
-
- 水平排列,但所有元素默认会有1个空格的间隙,因为元素之间在html中书写有回车换行,浏览器解析会将其解析成一个空格。
-
- 常用的内联块状元素有: img、input
-
标签相互转换
- display:inline;转换为行内元素
display:linline-block;转换为行内块元素
display:block;转换为块级元素
- display:inline;转换为行内元素
-
H5第一行的重要性
-
在HTML文档初,往往会有这么一句话
- 不用区分大小写
-
意义
- 它是html5标准网页声明,全称为Document Type HyperText Mark-up Language,意思为文档种类为超文本标记性语言或超文本链接标示语言,现在是这个简洁形式,支持html5标准的主流浏览器都认识这个声明。表示网页采用html5,<!DOCTYPE> 声明位于文档中的最前面的位置,处于 标签之前。此标签可告知浏览器文档使用哪种 HTML 或 XHTML 规范。
- 的重要性
-
声明文档的解析类型(document.compatMode),避免浏览器的怪异模式
-
document.compatMode:
- BackCompat:怪异模式,浏览器使用自己的怪异模式解析渲染页面。
CSS1Compat:标准模式,浏览器使用W3C的标准解析渲染页面。 - 这个属性会被浏览器识别并使用,但是如果你的页面没有DOCTYPE的声明,那么compatMode默认就是BackCompat,
这也就是恶魔的开始 – 浏览器按照自己的方式解析渲染页面,那么,在不同的浏览器就会显示不同的样式。
如果你的页面添加了那么,那么就等同于开启了标准模式,那么浏览器就得老老实实的按照W3C的
标准解析渲染页面,这样一来,你的页面在所有的浏览器里显示的就都是一个样子了。
- BackCompat:怪异模式,浏览器使用自己的怪异模式解析渲染页面。
-
-
6.0 JS的继承方法
继承的六种方式
-
前言
-
// 定义一个动物类 function Animal (name) { // 属性 this.name = name || 'Animal'; // 实例方法 this.sleep = function(){ console.log(this.name + '正在睡觉!'); } } // 原型方法 Animal.prototype.eat = function(food) { console.log(this.name + '正在吃:' + food); };
-
-
1、原型链继承
-
核心: 将父类的实例作为子类的原型
-
实现代码
-
function Cat(){ } Cat.prototype = new Animal(); Cat.prototype.name = 'cat'; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.eat('fish')); console.log(cat.sleep()); console.log(cat instanceof Animal); //true console.log(cat instanceof Cat); //true
-
-
-
-
- 特点:优点
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到,也即原型链上的方法属性
- 简单,易于实现
- 特点:优点
-
-
-
- 缺点
1.0 可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在new Animal()这样的语句之后执行。
2.0 无法实现多继承
3.0 来自原型对象的所有属性被所有实例共享
4.0 创建子类实例时,无法向父类构造函数传参
重点:new 出来的所有实例对象都指向原型对象,所以在实例对象中添加改变原型对象中的属性和方法会改变所有的创建的实例对象相当于浅拷贝,修改数据所有发生改变
出现数据污染
-
-
2、构造函数继承
-
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类
- 利用call()改变this指向
-
实现代码
-
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
-
特点:
优点
1.0 解决了1中,子类实例共享父类引用属性的问题2.0 创建子类实例时,可以向父类传递参数
3.0 可以实现多继承(call多个父类对象缺点
1.0 实例并不是父类的实例,只是子类的实例2.0 (主要)只能继承父类的实例属性和方法,不能继承原型链上的属性/方法
3.0 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能(最致命的) -
-
-
3、实例继承
-
核心:为父类实例添加新特性,作为子类实例返回
-
代码实现
-
function Cat(name){ var instance = new Animal(); instance.name = name || 'Tom'; return instance; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // false
-
特点:
优点
不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
缺点:
1.0 实例是父类的实例,不是子类的实例2.0 不支持多继承
-
-
-
4、 拷贝继承
-
代码实现
-
function Cat(name){ var animal = new Animal(); for(var p in animal){ Cat.prototype[p] = animal[p]; } } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
-
特点
-
优点
- 支持多继承
-
缺点
- 1.0 效率较低,内存占用高(因为要拷贝父类的属性)
- 2.0 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
-
-
-
-
5、组合继承(推荐)
-
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
- 原型链和构造函数继承整合
-
代码实现
-
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } Cat.prototype = new Animal(); // 提醒,组合继承也是需要修复构造函数指向的。 Cat.prototype.constructor = Cat; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // true
-
-
特点
-
优点
- 1.0 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
2.0 既是子类的实例,也是父类的实例
3.0 不存在引用属性共享问题
40 可传参
5.0 函数可复用
- 1.0 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
-
缺点:
- 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
-
-
优化方法
-
不想执行两次
-
Cat.prototype = new Animal();改为:Cat.prototype = Animal.prototype
- 指向一个,少调用一次Animal
-
构造函数指向有问题
- Cat.prototype = Object.create(Animal.prototype)
- 然后修改指向才可以在一条原型链上:Cat.prototype.constructor = Cat;
-
-
-
6、寄生组合继承
- 核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点代码实现
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
Cat.prototype.constructor = Cat; // 需要修复下构造函数
特点:
优点
- 堪称完美
- 缺点
实现较为复杂
Js中的类
-
类的三大特性
-
封装
-
继承
-
多态
- JavaScript 的弱类型特征,不存在编译时要确定类型,天然支持多态。
-