浏览器(网页浏览器)
参考:百科:浏览器
词条:浏览器、内核、浏览器内核、javascript引擎
1.浏览器的组成
浏览器大致由 shell 和 内核 组成
shell 包括外壳,菜单,用户操作界面 等等
内核一般分为 渲染引擎 (排版渲染引擎、图形引擎) 和 javascript引擎 (JS解释器)
shell(计算机壳层):是指“为使用者提供操作界面”的软件(command interpreter,命令解析器)
shell 类似于DOS下的COMMAND.COM和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。
2.主流程 The main flow
渲染引擎在取得内容之后的基本流程:
解析html以 构建dom树->构建render树->布局render树->绘制render树
参考:https://blog.csdn.net/liyong1...
3.浏览器内核技术
浏览器内核主要包括以下三个技术分支:排版渲染引擎、 JavaScript引擎,以及其他
4.浏览器的内核(渲染引擎、浏览器引擎、Rendering Engine)
浏览器引擎(内核,渲染引擎)-用来查询及操作渲染引擎的接口
例如:webkit【WebKit 所包含的 WebCore 排版引擎 和 JavaScriptCore解析引擎】、
Trident、Gecko、Presto
排版渲染引擎-用来显示请求的内容
例如:WebCore
JavaScript引擎-用来“读懂”JavaScript代码 并 解释执行JS代码
例如:V8
5.JavaScript引擎
JavaScript引擎是一个专门处理JavaScript脚本的虚拟机,一般会附带在网页浏览器之中。
例如:V8,由Google丹麦开发,是Chrome浏览器的一部分。
6.JavaScript引擎工作原理
参考:https://blog.csdn.net/a419419...
6.1第一阶段: 语法检查
6.1.1词法分析
JavaScript解释器先把JavaScript代码(字符串)的字符流按照ECMAScript标准转换为记号流
6.1.2语法分析
JavaScript语法分析器在经过词法分析后,将记号流按照ECMAScript标准把词法分析所产生的记号生成语法树。通俗地说就是把从 程序中收集的信息存储到数据结构中,每取一个词法记号,就送入语法分析器进行分析。
6.2第一阶段: 运行阶段
6.2.1预解析
第一步:创建执行上下文。解析器将语法检查正确后生成的语法树复制到当前执行上下文中。
第二步:属性填充。解析器会对语法树当中的变量声明、函数声明以及函数的形参进行属性填充。
预解析阶段创建的执行上下文包括:变量对象、作用域链、this
变量对象(Variable Object):由vardeclaration、function declaration(变量声明、函数声明)、arguments(参数)构成。 变量对象是以单例形式存在。
作用域链(Scope Chain):variableobject + all parent scopes(变量对象以及所有父级作用域)构成。
this值:(thisValue):contentobject。this值在进入上下文阶段就确定了。一旦进入执行代码阶段,this值就不会变了。
6.2.2执行
注:圆括号()
是一种运算符,跟在函数名之后,表示调用该函数。
函数名称() 或者 参数名称.call()
js运行三部曲
1、语法分析
2、预编译
3、解释执行
预编译
1、创建AO对象(执行器上下文、执行环境)
2、找形参和变量声明、将变量和形参作为AO的属性名.值为undefined
3、将实参值和形参值统一
4、在函数体内找到函数声明,值赋予函数体
注:
变量对象初始化顺序:函数形参 --> 函数声明 --> 变量声明;(也是提升顺序)
[[Scope]]仅供js引擎操作
作用域\作用域链
function fn() {
// 创建Ao对象(Activation Object) 执行器上下文
// 伪代码
var AO = {
this: window, // 构造函数相关
'[[Scope]]': 'GO', // 所在环节的作用域*/
num: undefined,
};
var num = 123;
}
fn();
// 全局作用域
var GO = {
this: window,
window: (Object),
document: (Object),
fn: undefined
}
【对象】字面量(构造函数(普通函数(预编译)))和直接量
构造函数内部原理【实例、构造函数、对象】
原型链的连接点:__proto__
call/apply:改变this指向
call/apply区别:传参列表不同
// 模拟构造函数(构造函数原理)
function Object() {
// 1、在函数体最面前隐士的加上 this = {}
// 2、执行this.xxx = xxx;
// 3、隐式地返回this(引用类型)
var this = {
__proto__: Object.prototype, // 可以被修改
contructor: Object, // 构造函数
arguments: null // 形参 https://zhuanlan.zhihu.com/p/72469390
};
this.name = 'zhangcs';
return this;
}
// 原型
Object.prototype = {
name: '原型'
}
// 实例
new Object().name; // 指向属性 'zhangcs'
new Object().contructor; // 指向构造函数
new Object().__proto__; // 指向原型
// 改变this指向
var obj = {}
Object.call(obj, '参数'); // this = obj
Object.apply(obj, ['参数']); // 参数必须是数组
function Model(width) {
this.width = width;
}
function Style(color) {
this.color = color;
}
function Car(color, width) {
Model.call(this, width);
Style.apply(this, [color]);
};
var car = new Car('red', '200')
7.任务队列
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。