一切运行都在内存里
开机
- 操作系统在C盘里(macOS 的在根目录下多个目录里)。
- 当你按下开机键,主板通电,开始读取固件。
- 固件就是固定在主板上的存储设备,里面有开机程序。
- 开机程序会将文件里的操作系统加载到内存中运行。
操作系统(以Linux为例)
- 首先加载操作系统内核。
- 然后启动初始化进程,编号为1,每个进程都有编号
- 启动系统服务:文件、安全、联网。
- 等待用户登录:输入密码登录或SSH登录。
- 登录后,运行shell,用户就可以和操作系统对话了。
- Bash是一种shell,图形化界面可认为是一种shell。
打开浏览器
chrome. exe
- 双击Chrome图标,就会运行chrome.exe文件。
- 开启Chrome进程,作为主进程(Main Process)。
- 主进程会开启一些辅助进程,如网络服务、GPU加速。
- 每新建一个网页,就有可能会开启一个子进程。但有的时候,相同域名会并为一个进程。
浏览器的功能
- 发起请求,下载HTML,解析HTML,下载CSS,解析CSS,渲染界面,下载JS,解析JS,执行JS等。
- 渲染界面:把HTML和CSS的内容下载、解析、整合并显示到屏幕的过程。
- 解析是理解代码的意思,执行是实现代码的效果。
- 功能模块:用户界面、渲染引擎、JS引擎、存储等。
- 渲染引擎:渲染HTML和CSS。
- JS引擎:解析并执行JS。
- 上面功能模块一般各处于不同的线程(String)(比进程更小)。如果进程是车间,那么线程就是车间里的流水线。
- 每个页面会开启一个JS引擎和渲染引擎。
- JS渲染通过线程通信,让渲染引擎来替自己完成线程。也因此由于夸线程带来的延迟,会有“DOM操作慢”的说法。
- JS只能单线程执行。
JS引擎
JS引擎举例
- Chrome用的是C++语言编写的V8引擎。
- 网景用的是SpiderMonkey引擎,Firefox后来沿用了这一引擎。
- Node.js用的是V8引擎。
- 最新的IE用的是V8引擎。
JS引擎的主要功能
- 编译:把JS代码翻译为机器能执行的字节码或机器码。
- 优化:改写代码,使其更高效。也因为JS引擎有内置优化功能,所以“其他人类给出的JS优化建议几乎都是扯淡”。
- 执行:执行上面的字节码或者机器码。
- 垃圾回收:把JS用完的内存回收,方便之后再次使用。
执行JS代码
准备工作
- 浏览器提供API:Window、document、setTimeout、Object、console、Array等。
- 这些API都挂在Window上,通过
window.方法名来调用,window在stack中默认为#90地址。
- 一般而言,方法名首字母大写的方法,都会有prototype属性,如Object()和Array()。
- 这些功能都不是JS自身具备功能,它们被统称为运行环境runtime env(environment)。
- 一旦把JS放进页面,就开始执行JS。
- JS代码在哪运行(放到哪里)?
内存图

- Stack:栈内存。每个数据顺序存放,先进后出,用于存放非object对象,例:
var a = 1。
- Heap:堆内存。每个数据随机存放,先进先出,用于存放object对象,例:
var person = {name: 'Frank', child:{name: 'Jack'}}。
- 在JS中,非object只有布尔、数字、字符串。Array、function等都是object。
- object对象每次改变,都可能需要重新分配存储空间,如果使用栈内存的顺序排列的方式的话,太过低效了。
- Stack&Heap
- 专门用来存放数据内容。
- Stack&Heap并不存放变量名,变量值存放在Stack&Heap里。简而言之,在Stack&Heap之外,生成一个变量名表,在Stack存放非object内容和object的地址,在Heap里存放object的内容。
- 每种浏览器的分配规则不同。
- 上图的区域并不完整,还缺少调用栈、任务队列等。
- 指向/引用:指通过属性名来保存一个地址。
变量和对象
- window变量和window对象是两个东西。
- window变量是一个存放内存地址的容器,存放的是window对象的地址。
- window对象是Heap中存放的数据。
- 因此可以通过多次赋值的方式,例:
var x = window,使多个window变量指向一个window对象。
- 同理,console变量和console对象不是同一个东西;Object变量和Object函数对象不是同一个东西。
原型链
- 例:
window.Object.prototype、window.Array.prototype
- JS在创建窗口的时候,就已经创建了Object和Array等对象的prototype。
new Object()的元素有一个隐藏的属性“__proto__”,内存储了Object.prototype对象的地址。当元素调用方法时,如果自身没有这个方法,会从Object.prototype中寻找这个方法。
- 所有对象都有“__proto__”属性。
- Array的“__proto__”为Object。
- Object的“__proto__”为null。
- prototype存储了对应对象的共同属性,没有prototype会极大加大声明对象的工作量。
拓展
JS三座大山
- this
- 原型prototype
- AJAX
JS中方法重写
var obj1 = {}
var obj2 = {}
obj1.toString === obj2.toString
obj1.toString = 'function'
obj1.toString === obj2.toString
- obj1和obj2的toString都是通过自身元素对象,再索引到prototype对象。
- 而
obj1.toString = 'function'则是在obj1内部重写一遍toString的方法,因此不会影响到obj2的toString的指向。
“__proto__”和“prototype”的区别
- 两者都存着原型的地址。
- “prototype”挂在函数上。
- “__proto__”挂在每个新生成的对象上。
