内存图与 JS 世界

芝麻凛 2021年08月10日 117次浏览

内存图与 JS 世界

操作系统常识

一切都运行在内存里

开机

  • 操作系统在C盘里(macOS的在根目录下多个目录里)
  • 当你按下开机键,主板通电,开始读取固件
  • 固件就是固定在主板上的存储设备,里面有开机程序
  • 开机程序会将文件里的操作系统加载到内存中运行

操作系统(以Linux为例)

  • 首先加载操作系统内核
  • 如何启动初始化进程,编号为1,每个进程都有编号
  • 启动系统服务:文件、安全、联网
  • 等待用户登录:输入密码登录/ ssh登录
  • 登录后,运行shell,用户就可以和操作系统对话了
  • bash 是一种shell,图形化界面可认为是一种shell

打开浏览器

Chrome.exe

  • 你双击Chrome图标,就会运行chrome.exe文件
  • 开启Chrome进程,作为主进程
  • 主进程会开启一些辅助进程,如网络服务、GPU加速
  • 每次新建一个网页,就有可能开启一个子进程

浏览器的功能

  • 发起请求,下载HTML,解析HTML,下载CSS,解析CSS,渲染页面,下载JS,执行JS等
  • 功能模块:用户界面、渲染引擎、JS引擎、存储等
  • 上面功能模块一般各自处于不同的线程(比进程更小的单位)
  • 如果进程是车间,那么线程就是车间里面的流水线

JS是单线程的,DOM是跨线程的操作会慢一点。

JS引擎

JS引擎举例

  • Chrome用的是V8引擎,C++编写
  • 网景用的是SpiderMonkery,后被Firefox使用,C++
  • Safari用的是JavaScriptCore
  • IE用的是Chakra(JScript9)
  • Edge用的是 Chakra(JavaScript)
  • Node.js 用的是 V8引擎

主要功能

  • 编译:把JS代码翻译为机器能执行的字节码或机器码
  • 优化:改写代码,使其更高效
  • 执行:执行上面的字节码或机器码
  • 垃圾回收:把JS用完的内存回收,方别之后再次使用

执行JS代码

准备工作

  • 提供API:window、document、setTimeout
  • 上面这些东西都不是JS自身具备的动能
  • 我们将这些功能称为运行环境runtime env
  • 一旦把JS放进页面,就开始执行JS

总结

  • JS代码在哪里运行?
  • 答:内存

内存图

瓜分内存

image20210814212617709.png

红色区域

作用

  • 红色专门用来存放数据,目前只研究该区域
  • 红色区域并不存变量名
  • 每种浏览器的分配规则并不一样
  • 上图的区域并不完整,还有[调用栈]、[任务队列]等区域

Stack 和 Heap

  • 红色区域分为Stack栈和Heap 堆
  • Stack区特点:每个数据顺序存放
  • Heap区特点:每个数据随机存放

Stack和Heap举例

代码

var a = 1
var b = a
var person = {name:'frank',child:{name:'jack'}}
var person2 = person

规律

  • 数据分两种 非对象 和 对象
  • 非对象都存在 Stack
  • 对象都存在 Heap
  • = 号总是会把右边的东西复制到左边(不存在什么传值和传址)

区分值和地址:不会画内存图的人才需要做这件事

对象被篡改了

代码

var person = {name:'frank',child:{name:'jack'}}
var person2 = person
person2.name = 'ryan'
console.log(person.name) // 'ryan'

当你执行JS的时候浏览器准备了些什么?

提供了window

还有什么

要有console

  • 于是有了console,并挂到window上

要有document

  • 于是有了document,并且挂到window上

要有对象

  • 于是有了Object,并挂到window上
  • var person = {} 等价于 var person = new Object()

要有数组(一种特殊对象)

  • 于是就有了Array,并且挂到window上
  • var a = [1,2,3] 等价于 var a = new Array(1,2,3)

要有函数(一种特殊的对象)

  • 于是就有了Function,并且挂到window上
  • function f{} 等价于 var f = new Function()

提问

  • 为什么有 var a = [], 还要提供 var a = new Array()呢
  • 答:因为后者是正规写法,但是没人用。前者不正规,但是好用
  • 为什么有function f() {},还要提供var f = new Function 写法呢
  • 答:同上

挂在window上的东西可以在任何地方直接用。

把window用内存图画出来

image20210814221805923.png

简单画法

image20210814221901427.png

细节

关于window

  • window 变量和 window对象是两个东西
  • window 变量是一个容器。存放window对象的地址
  • window对象是Heap里的一坨数据
  • 不信的话,可以让var x = window, 那么这个x就指向了window对象,window变量就没用了
  • 不要这样写,会把别人绕晕

同理

  • console 和 console对象不是同一个东西
  • Object 和 Object函数对象不是同一个东西
  • 前者是内存地址,后者是一坨内存

JS三座大山之原型链

图里的prototype是干什么用的

打印出来看看:console.dir(window.Object.prototype) ,window可以省略

代码

  • var obj = {}
  • obj.toString()

为什么不报错?为什么可以运行,空对象为什么有toString()?

  • obj有一个隐藏属性
  • 隐藏属性存储了Object.prototype对象的地址
  • obj.toString() 发现 obj 上没有toString
  • 就去隐藏属性对应的对象里面找
  • 于是就找到了Object.prototype.toString

还有一个问题

代码

var obj2 = {}

obj2.toString()

obj 和 obj2 有什么联系

相同点:都可以调用.toString()

不同点:地址不同 obj != obj2,可以拥有不同的属性,反过来说是什么意思呢?

XXX.prototype存储了 XXX 对象的共同属性,这就是原型

原型有什么好处

如果没有原型

声明一个对象

var obj = {
	toString: window.Object.prototype.toString,
	hasOwnPropertyOf: window.Object....
}
obj.toString()
var obj2 = {
    toString: window.Object.prototype.toString,
	hasOwnPropertyOf: window.Object....
}

这样就会很累~

原型让我们无需重复声明共有属性,省代码,省内存。

每个对象都有一个隐藏属性,指向原型(对象)

如果没有这个隐藏属性,那么obj就不知道自己共有属性在哪。

这个隐藏属性的名字叫做:__proto__

prototype 和__proto__区别是什么?

  • 都存着原型的地址
  • 只不过prototype挂在函数上
  • __proto__挂在每个新生成的对象上

以下代码为什么不报错?为什么可以运行?

var arr = [1,2,3]
arr.join('-')

  • arr 有一个隐藏属性
  • 隐藏属性存储了Array.prototype对象的地址

JS原型链世界

image20210814234039892.png