本文从浏览器输入url开始到离开页面,来讲解浏览器的原理
主要顺序如下
将url解析为:协议、域名、资源路径
如解析https://blog.csdn.net/qq_38217940/article/details/125349105
协议:https
域名:blog.csdn.net
资源路径:/qq_38217940/article/details/125349105(访问页面的时候,路径下面是一个index.html)
步骤如下:
简单了解几个名词解释:
为什么是三次不是两次?
为了防止服务器端开启一些无用的连接增加服务器开销以及防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
栗子:
假如我到你家作客,两次握手:
我:我可以到你家玩吗?
你:可以的。
…30分钟后
我:我开飞机来的,你家怎么没机场?
三次握手:
我:我可以到你家玩吗?
你:可以的。
我:我开飞机过去,你家有机场可以停吗?
你:我出门都是御剑的,没有机场,你别来了。
为什么不是四次或者更多?因为没必要
为什么是四次挥手?
FIN跟ACK不同时触发,中间确认一次是为了确保是否有数据正在传输,确保正在传输的数据传输完。
栗子:
你:我走了
我:好的,我看看你有没有东西没带走。
我:没啥东西落下,你走吧。
你:好的,我走了
两次挥手的情况:
你:我走了
我:你走吧
…你飞机没开走
三次挥手的情况:
你:我走了
我:你走吧
你:再见
…你飞机没开走
重绘: 当渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观、风格,而不会影响布局的操作,比如 background-color,我们将这样的操作称为重绘。
当渲染树中的一部分(或全部)因为元素的规模尺寸、布局、隐藏等改变而需要重新构建的操作,会影响到布局的操作,这样的操作我们称为回流。
任何会改变元素几何信息(元素的位置和尺寸大小)的操作,都会触发回流。
回流必定触发重绘,重绘不一定回流,回流的性能消耗比较大,所以要避免回流
var ul = document.createElement('ul');
var fragment = document.createDocumentFragment();
for(var i=1; i<101;i++){
var li = document.createElement('li')
var liText = document.createTextNode(i);
li.appendChild(liText);
fragment.appendChild(li);
}
ul.appendChild(flag);
document.body.appendChild(ul);
// 创建div的也一样道理,不过就得多加一层div了
let div = document.createElement('div')
进程的概念
浏览器是多进程的,每一个页签都是单独的进程,每一个页签都包含以下进程:
浏览器的线程,指的是渲染进程,它包括
js单线程特点:
使用Web Workers开启js多线程,可以用于处理会阻塞js线程的事务,提升性能。具体看这个吧。
机制:js是单线程的,按照代码顺序执行,执行完之后看微队列,有微任务则执行微任务,再看宏队列,有宏任务则执行宏任务。
看下面三个问题:
(1)假如js执行过程中有定时器到时间了,是先执行定时器还是先执行完代码?
(2)点击事件的代码执行过程中,定时器时间到了,是先执行定时器还是先执行完点击事件里面的代码?
(3)假如js执行过程中点击事件触发了,是先执行点击事件还是继续执行代码?
不用怀疑,只要js在执行,不管什么事件,不管什么任务,都不能打断它,
实际上js执行的时候页面是属于一个卡住的状态,压根就不存在代码正在执行还能触发点击事件的情况,只不过通常我们不会故意写代码把页面卡住(除非写得很烂)。不信f12打开下面的代码,看看还能不能点击得动页面
for(var i=0;i< 1000000000, i++){
console.log(1)
}
宏任务跟微任务是什么?简单来说就是异步任务。
宏任务包括
setTimeout、setInterval、setImmediate (Node独有)、ajax
requestAnimationFrame (浏览器独有)、I/O、UI rendering (页面渲染)
微任务包括
promise、async、await
宏任务跟微任务的区别(作者个人看法,读者自行分辨):
(1)宏任务包含的内容都是在单独的线程上的,而微任务都是在js线程上的
(2)两个线程消耗的性能肯定比一个线程消耗的多,所以微任务性能消耗会小一点
执行顺序
宏任务跟微任务都是先入先出,主事件循环结束后,先执行微任务再执行宏任务。
具体理解看代码
console.log('1');
setTimeout(function() {
console.log('10');
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
},10)
setTimeout(function() {
console.log('7');
new Promise(function(resolve) {
console.log('8');
resolve();
}).then(function() {
console.log('9')
})
})
var a = new Promise(function(resolve) {
console.log('2');
resolve();
}).then(function() {
console.log('5')
})
var b = new Promise(function(resolve) {
console.log('3');
resolve();
}).then(function() {
console.log('6')
})
cosonle.log('4')
// 输出1,2,3,4,5,6,7,8,9,10,11,12
// async是立即执行,没啥好说的
// await按照Promise来理解,里面的相当于resolve之前,函数后为then,比如把a改成下面这样,执行顺序不变,这里就不多介绍了
async function a(){
// 这里要立即执行
await function(){
console.log('2')
}()
console.log('5');
}
a();
背景小知识
事件冒泡和事件捕获分别由微软和网景公司提出,目的在于解决页面中的事件流问题,即元素间事件触发的时序。当然,分久必合,在微软和网景之间火热争论之后,最后采用了 W3C 的折中方案——先捕获后冒泡。
名词解释
一般情况下 js默认执行事件冒泡(说的是addEventListener第三个参数默认是false),当addEventListener第三个参数是true的时候就是监听的事件捕获。
事件流分为以下三个阶段
(1)事件捕获
(2)目标接受事件
(3)事件冒泡
也叫事件代理。利用事件冒泡原理,将子级触发的事件绑定在父级身上。
事件委托优点:
(1)减少多次绑定,提高程序性能
(2)动态添加的子元素也能自动获取事件
<ul id='list'>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
const ul = document.querySelector('#list')
ul.addEventListener('click',function(e){
const target = e.target
if(target.nodeName === 'li'){
console.log(target.nodeName)
}
},false)
不被需要的就是垃圾
标记回收算法
浏览器进行垃圾回收的时候,会暂停 JavaScript 脚本,等垃圾回收完毕再继续执行。
对于普通应用这样没什么问题,但对于 JS 游戏、动画对连贯性要求比较高的应用,如果暂停时间很长就会造成页面卡顿。
什么时候进行垃圾回收很重要。
闭包的官方定义(MDN)
一个函数和对其周围状态的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
为什么要说闭包?首先要知道闭包的作用
闭包的应用场景
作者没用到过,原因是面向对象编程很少回需要用到闭包,从列出来的三点闭包的作用就可以知道,对象的封装、继承、多态本身具备。(个人看法)
WeakSet 、weakMap里面的引用,都不计入垃圾回收机制,因此,WeakSet 、weakMap适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 、weakMap里面的引用就会自动消失。他们都是用来解决内存泄漏问题的。(作者太菜了,还没用过,在node开发上面会很有用)
1、减少使用全局变量,如尽量减少window上挂载对象
2、用完的对象记得回收,避免内存泄漏(垃圾没有被及时回收就是内存泄漏),如
3、内存泄漏会导致页面卡顿甚至崩溃,当页面奔溃时,很可能就时因为内存泄漏,而内存泄漏就要检查哪些垃圾没有回收,检查时可以看有哪些全局的变量是需要但是忘记回收的
1、load跟unload,加载跟关闭,一般在onload之后执行js,有些恶心的网站在unload的时候弹alert,阻止你关闭页面
2、onpopstate、hashchange、statechange当窗口历史记录(url)改变时触发,可以实现路由,感兴趣可以看这个