1、解析规则:

1、html字符串被浏览器接收后一句一句读取并解析
2、如果解析到link标签,便发送请求获取css;
3、解析到script标签,发送请求获取js后并执行相应的代码
4、解析到img后会请求图片资源
5、在解析html过程中构建dom树,解析css等过程中构建渲染树,递归布局后进行页面绘制

2、开始解析html

//解析器通常会把工作分配给两个组件:分词程序负责把输入的html切分成合法的序列;解析程序按照句法规则去分析然后构建句法树。
<html>
	<body>
		<p>hello taya</p>
		<div><p>hiiiiii</p></div>
	</body>
</html>

该html解析成DOM树后为:

  • HTMLHtmlElement
  • HTMLBodyElement
  • HTMLParagraphElement
  • Text
  • HTMLDivElement
  • HTMLParagraphElement
  • Text

3、css选择器的读取顺序是从右向左、选择器性能解释

#taya div.ty span{ color:red }

它的读取顺序是:span => div class:ty => id:taya
先找到全局的span,然后从第一个开始向上寻找class为ty的div,然后再寻找id为taya的元素,如果没有匹配项则放弃本条路径,从下一个span开始寻找。
如果是自左向右,便变成了深度遍历并伴随着大量的回溯节点,非常消耗性能
这也是为什么在性能方面:id选择器 > 类选择器 > 元素选择器

4、js解析

1、js是单线程,只能按照先后顺序执行2、浏览器解析是异步的,当需要请求外部资源时是不会影响html加载的,但是如果遇到js文件,则会将解析挂起,因为js中可能会有针对DOM的操作(js优化我们需要尽可能地去避免)3、js解析器会先进行预解析,即找到所有的变量、函数等进行初始化赋值为undefined,把函数取出来成为一个函数块,存放到仓库中,然后再解析js代码,和仓库进行匹配

根据以上的机制,能解释很多词法作用域的问题:

1:
console.log(a)
let a = 1
console.log(a)
//结果: undefined   1

2: //如果使用let,这个例子就没有用了,会报错。
console.log(taya)       //function taya(){console.log("4")}
var taya = "sdsd"
console.log(taya)      //sdsd
function taya(){console.log("11")}
console.log(taya)      //sdsd
var taya = "3"
console.log(taya)      //3
function taya(){console.log("4")}
console.log(taya)      //3
解析:在js预解析时,如果变量和函数重名,只会保留函数块,所以第一个taya输出为函数
在解析时,只有+ - * / % =等字符时才能更改仓库中的值,所以后续的taya输出不为函数了

3:
var a = 1;
function fc(){
	console.log(a)
	a = 2
}
fc()
console.log(a)
输出:1   2
解析:执行fc()函数时,发现内部并没有定义任何变量,第一行输出a,仓库内是空的,便向外部寻找,找到了全局变量a。

4:
var a = 1;
function fc(a){
	console.log(a)
	a = 2
}
fc(1)
console.log(a)
//输出:1   1