浏览器进程
Chrome.exe程序
- 用户双击点击
Chrome图标,运行Chrome.exe程序 - 开启
Chrome进程,为主进程 - 主进程开启辅助进程,例如网络服务,
GPU加速 - 新建网页,可能开启一个子进程
浏览器的功能
- 发起请求,下载
HTML,解析HTML,下载CSS,解析CSS,渲染界面,下载JS,解析JS,执行JS等 - 运行功能模块,例如:用户界面,渲染引擎,
JS引擎等,存储等。其中渲染引擎复制HTML和CSS的渲染,JS复制JS的渲染等。 - 功能模块是处于不用的线程,线程比进程更小
- 如果进程是车间,线程则是车间的流水线
JS是单线程
同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?- 所以,为了避免复杂性,从一诞生,
JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。 - 为了利用多核
CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
JS引擎
JS V8引擎
Node.js和Chrome使用V8引擎,C++编写Firefox使用的是SpiderMonkey,C++编写Safari使用的是JavaScriptCoreIE使用的是Chakra(JScript9)Edge用的是Chakra(JavaScript)
引擎的功能
- 编译:将
JS代码编译为机器能执行的字节码或者机器码 - 优化:提高运行速度
- 执行:执行上面的字节码或者机器码
- 垃圾回收机制:回收
JS的内存,再次利用
Stack栈数据结构
结构就是后进先出 Last-in,Fist-out,每个数据按顺序存放。例如,乒乓盒子中最顶层的球5,最后一个放置进去,却第一个被拿出来使用。如果想拿到最底部的乒乓球1,需要把上面的球全部取出来,让乒乓球1处于盒子顶层
Heap堆数据结构
结构是一种树状结构,每个数据按随机数据存放。存取数据的方式和书架,书非常相似。例如,我们只需要知道书的名字,就可以把书给取出来。JSON格式的数据中,存储的key-value是无序的。
Queue队列数据结构
队列是一种First-in,First-out的数据结构,这是事件循环(Event Loop)的基础结构
变量的存放
基本类型--> 存储于栈
基本类型一共有6种,分别为:Undefined、Null、Boolean、Number 、String、Symbol。因为这些类型在内存中占有固定的大小空间,通过按值来访问
引用类型--> 存储于堆
引用类型有对象object, 数组Array。当查询引用类型的变量时,先从栈中读取内存地址, 然后再通过地址找到堆中的值。因为这种值的大小不固定,因此不能把它们保存到栈内存中,但内存地址大小的固定的,因此保存在堆内存中,在栈内存中存放的只是该对象的访问地址。在计算机的数据结构中,栈的运算速度比堆快,因为对象object, 数组Array的结构复杂,可以扩展,添加属性,增删改查。 通过引用的方式,先查找栈,再找到堆中的 实际对象,不影响效率。
实际问题
问题1:
var a = 20;
var b = a;
b = 30;
// 这时a的值是多少?
// 答案:20
a、b都是基本类型,它们的值是存储在栈中的,a、b分别有各自独立的栈空间,所以修改了b的值以后,a的值并不会发生变化。
问题2:
var a = { name: '前端开发' }
var b = a;
b.name = '进阶';
// 这时a.name的值是多少
// 答案:进阶
a、b都是引用类型,栈内存中存放地址指向堆内存中的对象,引用类型的复制会为新的变量自动分配一个新的值保存在变量对象中,但只是引用类型的一个地址指针而已,实际指向的是同一个对象,所以修改b.name的值后,相应的a.name也就发生了改变。
问题3:
var a = { name: '前端开发' }
var b = a;
a = null;
// 这时b的值是多少
// 答案:`{ name: '前端开发' }`
首先要说明的是null是基本类型,a = null之后只是把a存储在栈内存中地址改变成了基本类型null,并不会影响堆内存中的对象,所以b的值不受影响。
问题4:
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
a.x // 这时 a.x 的值是多少
b.x // 这时 b.x 的值是多少
答案: 知乎答案与讲解
内存空间管理
JavaScript的内存生命周期是:
- 分配你所需要的内存
- 使用分配到的内存(读、写)
- 不需要时将其释放、归还
JavaScript有自动垃圾收集机制,最常用的是通过标记清除的算法来找到哪些对象是不再继续使用的,使用a = null其实仅仅只是做了一个释放引用的操作,让a 原本对应的值失去引用,脱离执行环境,这个值会在下一次垃圾收集器执行操作时被找到并释放。
在局部作用域中,当函数执行完毕,局部变量也就没有存在的必要了,因此垃圾收集器很容易做出判断并回收。但是全局变量什么时候需要自动释放内存空间则很难判断,因此在开发中,需要尽量避免使用全局变量。
window全局对象
在全局作用域中声明的变量、函数都是window对象的属性和方法。
window对象是相对于web浏览器而言的,依赖于浏览器,在浏览器中全局对象指的就是window对象window对象和winodw变量是两个东西,window变量存放在Stack栈内存,存有指向window对象的地址。而window对象存在堆内存中。可以修改为var x = winow,则X指向window对象。console和console对象,object和object对象同理