一、HTML和CSS
页面布局的方法
1、浮动布局(布局简单,兼容性好,但是浮动元素脱离文档流)
2、绝对定位布局(布局快速,元素脱离文档流)
3、弹性盒子布局(自应性好,兼容性差)
4、多列布局(宽度自适应)
5、table布局(兼容性好)
HTML5新特性
1、拥有更多的语义化的标签
比如:header footer nav section article aside dialog
2、更好的表单
input输入框有更多的输入类型:
color(选取颜色)
data(日期选择器)
email(e-mail 地址的输入域)
month(选择月份)
number(数值输入域)
range(一定范围内数字值的输入域)
search(用于搜索)
tel(定位电话号码字段)
time(时间)
url(URL输入域)
HTML5新增的表单属性
placehoder 输入框默认提示,在用户输入后消失。
required 是一个布尔值,要求输入域不能为空。
pattern 描述一个正则表达式用于验证input元素的值
min和max 设置元素最大最小值
step 为type为number的输入域规定合法的数字间隔
multiple 是一个 boolean 属性。规定上传元素中可选择多个值
autofocus 是一个 boolean 属性。规定在页面加载时,域自动地获得焦点。
3、视频和音频
<audio controls>
<source src="horse.ogg" type="audio/ogg">
<source src="horse.mp3" type="audio/mpeg">
您的浏览器不支持 audio 元素。
</audio>
<video width="320" height="240" controls>
<source src="movie.mp4" type="video/mp4">
<source src="movie.ogg" type="video/ogg">
您的浏览器不支持Video标签。
</video>
4、Canvas绘图
canvas标签定义图形,比如图表和其他图像,您必须使用脚本来绘制图形
5、地理定位
HTML5 Geolocation API 用于获得用户的地理位置,鉴于该特性可能侵犯用户的隐私,除非用户同意,否则用户位置信息是不可用的
window.navigator.geolocation {
getCurrentPosition: fn 用于获取当前的位置数据
watchPosition: fn 监视用户位置的改变
clearWatch: fn 清除定位监视
}
navigator.geolocation.getCurrentPosition(
function(pos){
console.log('用户定位数据获取成功')
console.log('定位时间:',pos.timestamp)
console.log('经度:',pos.coords.longitude)
console.log('纬度:',pos.coords.latitude)
console.log('海拔:',pos.coords.altitude)
console.log('速度:',pos.coords.speed)
}, //定位成功的回调
function(err){
console.log('用户定位数据获取失败')
} //定位失败的回调
)
6、拖放API
拖放是一种常见的特性,即抓取对象以后拖到另一个位置,在 HTML5 中,拖放是标准的一部分,任何元素都能够拖放
7、web Storage
使用HTML5可以在本地存储用户的浏览数据。早些时候,本地存储使用的是cookies。但是Web 存储需要更加的安全与快速. 这些数据不会被保存在服务器上,但是这些数据只用于用户请求网站数据上.它也可以存储大量的数据,而不影响网站的性能。数据以 键/值 对存在, web网页的数据只允许该网页访问使用。
客户端存储数据的两个对象为:
localStorage - 没有时间限制的数据存储
sessionStorage - 针对一个 session 的数据存储, 当用户关闭浏览器窗口后,数据会被删除。
不管是 localStorage,还是 sessionStorage,可使用的API都相同,常用的有如下几个(以localStorage为例):
保存数据:localStorage.setItem(key,value);
读取数据:localStorage.getItem(key);
删除单个数据:localStorage.removeItem(key);
删除所有数据:localStorage.clear();
得到某个索引的key:localStorage.key(index);
8、webSocket
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送
9、Web Worker
当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成。
web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 web worker 在后台运行
dragstart:事件主体是被拖放元素,在开始拖放被拖放元素时触发,。
darg:事件主体是被拖放元素,在正在拖放被拖放元素时触发。
dragenter:事件主体是目标元素,在被拖放元素进入某元素时触发。
dragover:事件主体是目标元素,在被拖放在某元素内移动时触发。
dragleave:事件主体是目标元素,在被拖放元素移出目标元素是触发。
drop:事件主体是目标元素,在目标元素完全接受被拖放元素时触发。
dragend:事件主体是被拖放元素,在整个拖放操作结束时触发
css盒模型
css盒子的四个组成区域相对应,每个盒子有四个边界:内容边界 Content edge、内边距边界 Padding Edge、边框边界 Border Edge、外边框边界 Margin Edge。
css盒模型分为两种两种标准:一种是标准模型,一种是IE模型
从上图中发现,标准盒模型的宽高就是内容(content)的宽高,而IE盒模型的宽高则是由内容(content)+内边距(padding)+边框(border)的宽高的总和。
二、DOM事件类
事件流
事件流描述的是从页面中接受事件的顺序,事件流分为冒泡流和捕获流
事件冒泡
IE事件流叫做事件冒泡,就是事件开始是由最具体的元素(目标元素)接收,然后逐级向上传播到较为不具体的节点(文档),如图所示
事件捕获
事件捕获是从Document接收到事件,然后逐级向下传播到目标元素,如图所示
事件对象event
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。
下图是事件对象常用的属性方法
事件委托
事件委托是利用事件冒泡,只指定一个事件处理程序来管理某一类型的所有事件。
优点:减少事件注册,节省内存、简化了dom节点更新时,相应事件的更新。
缺点:事件委托基于冒泡,对于不冒泡的事件不支持
三、HTTP协议类
http与https
http:超文本传输协议,用于从WWW服务器传输超文本到本地浏览器的传输协议。
https:是以安全为目标的HTTP通道,即HTTP下加入SSL层,HTTPS的安全基础是SSL。
区别
http传输的数据是明文的,https传输的数据是经过SSL加密的
https协议需要ca证书,费用较高。
使用的链接方式,端口也不同,一般http的端口是80,https的端口是443
https的工作原理
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤:
1、客户使用https url访问服务器,则要求web服务器建立ssl链接
2、web服务器接收到客户端的请求之后,会将网站的证书(证书数中包含了公钥),传给客户端。
3、客户端和web服务器开始协商ssl链接的安全等级
4、客户端浏览器通过双方协商一致的安全等级,建立会话密钥,然后通过网站的公钥来加密会话密钥,并传送给网站。
5、web服务器通过自己的私钥解密出会话密钥
6、web服务器通过会话密钥加密与客户端之间的通信。
http报文的组成部分
http报文由请求报文和响应报文组成
http请求方式
1、GET (获取资源)
2、POST (传输资源)
3、PUT (更新资源)
4、DELETE (删除资源)
5、HEAD (获得报文首部)
GET与POST的区别
GET在浏览器回退是无害的,而POST会再次提交请求
GET请求会被浏览器主动缓存,而POST不会,除非手动设置
GET请求在URL中传送的参数是有长度限制的,而POST没有限制
GET参数通过URL传递的,POST放在Request body中
GET请求参数会被完整的保留在浏览器历史记录中,而POST中的参数不会被保留
GET请求只能进行url编码,而POST支持多种编码方式
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息
对参数的数据类型,GET只接受ASCII字符,而POST没有限制
http状态码
1xx: 指示信息-表示请求已接收,继续处理
2xx:成功-表示请求已被成功接收
3xx:重定向-要完成请求必须进行更进一步的操作
4xx:客户端错误-请求有语法错误或请求无法实现
5xx:服务端错误-服务器未能实现合法的请求
http常见的状态码
200 OK:客户端请求成功
206 Partial Content: 客户发送一个带有Range(范围)头的GET请求,服务器完成了它
301 Moved Permanently:所有页面已经转移到新的url
302 Found:所请求的页面已经临时转移至新的url
304 Not Modified:客户端有缓存的文档并发出一个条件性的请求,服务器告诉客户,原来缓冲的文档还可以继续使用
403 Forbidden:对请求页面的访问被禁止
404 Not Found:请求资源不存在
500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用
503 Server Unavailable:请求未完成,服务器宕机
四、javascript篇
原型链
1、创建对象的方法
//第一种方式:字面量
var o1 = {name: 'o1'};
var o2 = new Object({name: 'o2'});
//第二种方式:通过构造函数
var M = function (name) {
this.name = name;
};
var o3 = new M('o3');
//第三种方式:Object.create
var p = {name: 'p'};
var o4 = Object.create(p);
2、原型对象
每个函数被创建的时候都会有一个prototye属性,这个属性会指向函数的原型对象。默认情况下每个原型对象又都会获取一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针(指向构造函数)
//创建构造函数
function Person (name) {
this.name = name;
}
//在Person函数的原型对象上增加一个方法
Person.prototype.sayName = function (){
console.log(this.name)
}
//Person的原型对象中的constructor属性指向Person函数
Person.prototype.constructor===Person //true
//创建child实例
var child = new Person ('xiaoming');
child.sayName() //xiaoming
3、原型链
function Foo1(){
this.name1 = '1';
}
function Foo2(){
this.name2 = '2';
}
Foo2.prototype = new Foo1();
function Foo3(){
this.name3 = '3';
}
Foo3.prototype = new Foo2();
var foo3 = new Foo3();
console.dir(foo3)
console.dir(foo3.name1); //1
3个函数通过__proto__链接起来形成原型链,继承就是通过这个原理来的
4、instance of VS constructor
instanceof 原理:检查左边对象与右边对象是否在同一条原型链上。
constructor原理:取对象的proto属性指向的prototype对象上的constructor字段。
5、new运算符的原理
创建一个空对象,它的proto等于构造函数的原型对象(可以用Object.create()完成)
构造函数以第1步创建的对象做为上下文,是否会返回一个对象
若第2步返回了对象,则使用该对象作为新实例,否则用第1步创建的对象作为新实例
var myNew = function (func) {
var o = Object.create(func.prototype)
var i = func.call(o)
return typeof i === 'object' ? i : o
}
6、继承
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); //cat
console.log(cat.eat('fish')); //cat正在吃:fish
console.log(cat.sleep()); //cat正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
特点:
父类原型上的属性方法,子类都能访问到
实现简单
缺点:
1、来自原型对象的所有属性被所有实例共享
2、创建子类实例时,无法向父类构造函数传参
2、构造函数继承
核心:就是子类构造函数内部调用父类构造函数(使用call或者apply方法)
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡觉
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
特点:
1、创建子类实例时,可以向父类传递参数
2、可以实现多继承(call多个父类对象)
缺点:
1、只能继承父类的属性方法,不能继承原型对象的属性方法
2、每个子类都有父类函数的属性方法,影响性能
3、组合继承
核心:通过原型链实现对原型属性和方法的继承,通过构造函数来实现对实例属性的继承。
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); //Tom
console.log(cat.sleep());//Tom正在睡觉!
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
特点:
1、避免了原型链和构造函数的缺陷,融合了两者的优点
缺点:
- 1、调用了两次父类构造函数
4、寄生组合式继承
核心:通过寄生方式,砍掉父类的实例属性
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); //Tom
console.log(cat.sleep()); //Tom正在睡觉!
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
特点:
- 解决组合继承调用两次父类的缺陷
7、封装
https://blog.csdn.net/CCwm0129/article/details/80421092
闭包
闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放
this的指向
JS中的this不是指向定义他的位置,而是在哪调用它它就指向哪里,箭头函数中的 this 是依赖于所在函数的上下文的,类似于继承的关系。
五、通信类
什么是同源策略?
同源策略限制从一个源加载的文档或脚步如何与来自另一个源的资源进行交互(协议、域名、端口这3个构成一个源)。这是一个用于隔离潜在恶意文件的关键的安全机制
限制的内容
Cookie、LocalStorage和IndexDB无法读取
DOM无法获取
AJAX请求不能发送
跨域通信的几种方式
JSONP
Hash
postMessage
WebSocket
CORS
1、JSONP
原理:利用script标签的异步加载来实现的,script标签src属性中的链接可以访问跨域的js脚本,利用这个特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。
举个例子:
function test(data){
//打印后端传过来的数据
console.log(data) //data为{a:1,b:2}
}
var url="http://www.x.com/test?callback=test"//向x.com/test告诉他要调用的函数名是“test”
//后台拦截到callback,知道要生成一个调用方法,方法名是test,并传递参数,后台处理生成如下(数据虚构)
test({a:1,b:2})
//然后前端通过script标签去访问并执行,上面的东西
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
//然后就会调用页面的test方法,这就是jsonp的实现原理。
把js封装成一个通用组件
function myjsonp(URL,callback,callbackname){
//给系统中创建一个全局变量,叫做callbackname,指向callback函数
//定义
window[callbackname] = callback;
//创建一个script节点
var oscript = document.createElement("script");
//和image不一样,设置src并不会发出HTTP请求
oscript.src = URL;
oscript.type = "text/javascript";
//script标签的请求是在上树的时候发出,请求的是一个函数的执行语句
document.head.appendChild(oscript);
//为了不污染页面,瞬间把script拿掉
document.head.removeChild(oscript);
}
//使用
myjsonp("http://sclub.jd.com/productpage/p-1217508-s-0-t-3-p-1.html?callback=abcdefg",function(data){
console.log(data);
},"abcdefg");
2、hash
原理:原理是利用location.hash来进行传值。
//利用hash,场景是当前页面A通过iframe或frame嵌入了跨域的页面B
//在A中的伪代码如下:
var B = document.getElementsByTagName('iframe');
B.src = B.src + '#' + 'data';
//在B中的伪代码如下:
//onhashchange事件是用来监听当前页面的hash有没有改变
window.onhashchange = function () {
var data = window.location.hash;
}
3、postMessage
举例来说,父窗口http://a.com向子窗口http://b.com发消息,调用postMessage方法就可以了
a页面
<iframe id="frame1" src="http://127.0.0.1/JSONP/b.html" frameborder="1"></iframe>
document.getElementById('frame1').onload = function(){
//contentWindow返回的是iframe的window对象,所以后面可以接着调用document方法
var win = document.getElementById('frame1').contentWindow;
win.postMessage("我是来自a页面的","http://127.0.0.1/JSONP/b.html")
}
b页面
window.onmessage = function(e){
e = e || event;
console.log(e.data);//我是来自a页面的数据
console.log(e.source);//发送消息的窗口对象
console.log(e.origin);//发送消息窗口的源
}
4、WebSocket
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现
5、CORS
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
参考资料:http://www.ruanyifeng.com/blog/2016/04/cors.html
六、安全类
CSRF
1、基本概念
CSRF,即(Cross-site request forgery),
中文名为跨站请求伪造。是一种挟持用户在当前已登录的Web应用程序上执行非本意的操作的一种攻击方式。
2、攻击原理
举个例子:当用户通过身份认证登录了网站A,这时候网站A的cookie会保存在浏览器中,然后用户又访问了网站B,这时候网站B会有一个引诱点击,这个引诱点击往往是个链接,这个链接指向的就是网站A的API接口,因为浏览器会自动上传cookie,当网站A对身份重新确认发现是合法用户,所以就执行这个接口的动作。
综上所诉:要完成一个CSFR攻击,受害者必须依次完成两个步骤
1、登录受信任的网站A,并在本地生成Cookie
2、在不登出A的情况下,访问了危险网站B
3、防御措施
Token验证:CSRF原理中访问漏洞接口的时候浏览器只上传了cookie,没有手动的上传一个token。这个token是用户登录注册甚至只是访问网站A,服务器会自动向用户本地存储一个token,在用户访问各个接口的时候,如果没带这个token,服务器就不会通过验证。所以当用户点击引诱链接,这个链接只会自动携带cookie,但是不会自动携带token
Referer验证 (Referer指的是页面来源):服务器通过判断页面来源是不是自己站点的页面来源,如果是就执行接口动作,如果不是一律拦截。
XSS
1、基本概念
XSS就是跨域脚本攻击
2、攻击原理
反射型:发出请求时,XSS代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应代码一起传给浏览器,最后浏览器解析执行XSS代码。整个过程像一次反射,故叫反射型XSS
储存型:也叫持久型XSS,主要是将XSS代码发送到服务器(不管是数据库、内存还是文件系统等。),然后在下次请求页面的时候就不用带上XSS代码了
3、防范措施
对重要的cookie设置httpOnly, 防止客户端通过document.cookie读取cookie。服务端可以设置此字段
是服务端要进行过滤
七、算法篇
排序(快速排序、冒泡排序、选择排序)
递归
排序
1、快速排序
原理:
在数据集之中,选择一个元素作为“基准”。
所有小于“基准”的元素,都移到“基准”的左边,所有大于“基准”的元素,都移到“基准”的右边。
对“基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
var quickSort = function(arr) {
//如果数组长度小于等于1无需判断直接返回即可
if (arr.length <= 1){ return arr; }
var pivotIndex = Math.floor(arr.length / 2);
//取基准点的值,splice(index,1)函数可以返回数组中被删除的那个数
var pivot = arr.splice(pivotIndex, 1)[0];
console.log(pivot)
var left = [];
var right = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] < pivot){
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
//递归执行以上操作,对左右两个数组进行操作,直到数组长度为<=1;
return quickSort(left).concat([pivot], quickSort(right));
}
2、冒泡排序
原理:
1.比较相邻的两个元素,如果前一个比后一个大,则交换位置。
2.第一轮的时候最后一个元素应该是最大的一个。
3.按照步骤一的方法进行相邻两个元素的比较,这个时候由于最后一个元素已经是最大的了,所以最后一个元素不用比较
function bSort(arr) {
var len = arr.length;
for (var i = 0; i < len-1; i++) {
for (var j = 0; j < len - 1 - i; j++) {
// 相邻元素两两对比,元素交换,大的元素交换到后面
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
return arr;
}
//举个数组
myArr = [20,18,27,19,35];
//使用函数
bSort(myArr)//[18,19,20,27,35]
3、选择排序
原理:
- 把每一个数都与第一个数比较,如果小于第一个数,就把它们交换位置;这样一轮下来,最小的数就排到了最前面;重复n-1轮,就实现了选择排序
function selectSort (arr) {
var len = arr.length;
var temp;
for(var i=0; i<len-1; i++){
for(var j=i+1; j<len; j++){
if(arr[i]>arr[j]){
//互换位置
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
return arr;
}
var oldArr = [3,9,4,5,2,8];
selectSort(oldArr); //[2, 3, 4, 5, 8, 9]
递归
https://www.cnblogs.com/huangshikun/p/6677916.html
八、渲染机制
https://blog.csdn.net/buzhibujuell/article/details/68952370
九、js运行机制
js的单线程机制
概念:简单地说就是同一时间只能做一件事,当有多个任务时,只能按照一个顺序一个完成了再执行下一个
任务队列
任务队列可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
1、所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
2、主线程之外,还存在一个"任务队列",只要异步任务有了运行结果,就在"任务队列"之中放置一个事件
3、一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行
4、主线程不断重复上面的第三步
举个例子:
console.log(1) //这个是同步任务,放入主线程里
setTimeout(function(){ //这个是异步任务,放入任务队列中,等同步任务执行完毕,才能执行
console.log(2)
},0)
console.log(3) //这个是同步任务,在主线程里
//运行结果是132
Event Loop
主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)
异步任务的执行顺序准确的划分方式是:
宏任务(macro-task):包括整体代码script,setTimeout,setInterval
微任务(micro-task):Promise,process.nextTick
举个例子:
setTimeout(function(){
console.log('定时器开始啦')
});
new Promise(function(resolve){
console.log('马上执行for循环啦');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('执行then函数啦')
});
console.log('代码执行结束');
//执行结果是:马上执行for循环啦 > 代码执行结束 > 执行then函数啦 > 定时器开始啦
//执行原理是:
首先执行script下的宏任务,遇到setTimeout,将其放到宏任务的【队列】里
遇到 new Promise直接执行,打印"马上执行for循环啦"(Promise函数体里面是同步任务)
遇到then方法,是微任务,将其放到微任务的【队列里】
打印 "代码执行结束"
本轮宏任务执行完毕,查看本轮的微任务,发现有一个then方法里的函数, 打印"执行then函数啦"
到此,本轮的event loop 全部完成。
下一轮的循环里,先执行一个宏任务,发现宏任务的【队列】里有一个 setTimeout里的函数,执行打印"定时器开始啦"
十、页面性能
提升页面性能的方法有哪些?
资源压缩合并,减少HTTP请求
非核心代码异步加载
利用浏览器缓存
使用CDN
预解析DNS
非核心代码异步加载
1、异步加载的方式
动态脚本加载
script标签使用defer属性
script标签使用async属性
2、异步加载的区别
defer是在HTML解析之后才会执行,如果多个,按照加载的顺序依次执行
async是在加载之后立即执行,如果多个,执行顺序和加载顺序无关
浏览器缓存
浏览器缓存:“分为强制缓存和协商缓存”,当客户端向浏览器发送请求的时候,如果没有缓存则直接请求服务器。如果有则根据缓存状态来是否从缓存中获取对应的文件
作用:
减少冗余的数据传输
减少服务器负担
加快客户端加载网页的速度
1、强制缓存
利用Expires和Cache-Control两个字段来控制
expires:是缓存过期时间(即资源过期时间),是个绝对值。所以如果客户端更改时间,会导致缓存混乱。所以http:1.1增加了cache-control:max-age 字段,它是一个相对的时间
Cache-Control与Expires可以在服务端配置同时启用,同时启用的时候Cache-Control优先级高。
Control:max-age=3600,代表着资源的有效期是3600秒
2、协商缓存
协商缓存涉及到两组header 字段。
Etag和If-None-Match
Last-Modified和If-Modified-Since
Etag和If-None-Match
他们返回的是一个校验码,是个hash值。Etag能够保证资源都是唯一的,是客户端请求服务器的时候服务器返回的。如果资源变化了Etag也会发生变化。当再次请求服务器的时候服务器根据浏览器上送的If-None-Match(之前服务器返回的etag值)值来判断是否命中缓存。
Last-Modified和If-Modified-Since
浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。
当浏览器再次请求该资源时,request的请求头中会包含If-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。
如果命中缓存,则返回304,并且不会返回资源内容,并且不会返回Last-Modify。
Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。