【JS全解】内存图与JS世界

123 阅读5分钟

一切运行都在内存里

开机

  • 操作系统在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代码在哪运行(放到哪里)?
    • 内存。

内存图

Pasted image 20220624103316.png

  • 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.prototypewindow.Array.prototype
  • JS在创建窗口的时候,就已经创建了Object和Array等对象的prototype。
  • new Object()的元素有一个隐藏的属性“__proto__”,内存储了Object.prototype对象的地址。当元素调用方法时,如果自身没有这个方法,会从Object.prototype中寻找这个方法。
  • 所有对象都有“__proto__”属性。
    • Array的“__proto__”为Object。
    • Object的“__proto__”为null。
  • prototype存储了对应对象的共同属性,没有prototype会极大加大声明对象的工作量。

拓展

JS三座大山

  1. this
  2. 原型prototype
  3. AJAX

JS中方法重写

var obj1 = {}
var obj2 = {}
obj1.toString === obj2.toString // true
obj1.toString = 'function'
obj1.toString === obj2.toString // flase
  • obj1和obj2的toString都是通过自身元素对象,再索引到prototype对象。
  • obj1.toString = 'function'则是在obj1内部重写一遍toString的方法,因此不会影响到obj2的toString的指向。

“__proto__”和“prototype”的区别

  • 两者都存着原型的地址。
  • “prototype”挂在函数上。
  • “__proto__”挂在每个新生成的对象上。

Pasted image 20220624131537.png