前端升级打怪路:内存图和JS世界(十)

265 阅读9分钟

英语小课堂

科班知识小补充:

开机(电脑发生了什么)

  1. 按下开机键
  2. 主板通电
  3. 开始读取固件(固件就是固定在主板上的存储设备,里面有开机程序被写死了)
  4. 开机程序会将文件里的操作系统加载到内存中运行(屏幕从黑到蓝)
  5. 操作系统在C盘里( macos的在根目录下多个目录里)

操作系统(操作系统发生了什么)

  1. 首先加载操作系统內核
  2. 然后启动初始化进程,编号为1,每个进程都有编号
  3. 启动系统服务:文件、安全、联网
  4. 等待用户登录:输入密码登录/SSh登录
  5. 登录后,运行shell,用户就可以和操作系统对话了(登录展示的界面叫shell)
  6. bash是一种shell,图形化界面可认为是一种shell

浏览器(点击浏览器发生了什么)

  1. 你双击Chrome图标,就会运行chrome. exe文件开启
  2. 开启Chrome进程,作为主进程(可在Chrome更多工具中查看各个进程)
  3. 主进程会开启一些辅助进程,如网络服务、GPU加速
  4. 每新建一个网页,就有可能会开启一个子进程
  5. 在每个网页中都有相同的功能(用户界面、渲染引擎、JS引擎、存储等)
  6. 这些功能是线程

浏览器的功能

  • 面试常问的题:请问当用户在地址栏输入地址后网页发生哪些过程?
    • 发起请求
    • 下载HTML,解析HTML(HTML的DOM树)
    • 下载CSS,解析 CSS(CSS的DOM树)
    • 渲染界面(HTML和CSS合成渲染树render tree)
    • 下载JS,解析JS,执行JS(这三者是同时执行的,边读边在网页产生效果)
  • 浏览器有功能模块
    • 用户界面
    • 存储
    • 渲染引擎(浏览器一开始就有的)
      • 把HTML和CSS合成渲染
      • 注意:HTML,CSS,JS的下载全部都是有网络模块控制
    • JS引擎(JS是后面加入的)
      • 解析并执行
    • 每个网页都有功能模块,功能模块是线程(比进程小)
    • 进程到线程的分散图
  • 引用浏览器进程?线程?傻傻分不清楚!部分内容
    • 进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。(进程可以比作:大工厂中的车间)
    • 线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元。(线程比作:车间的工人,即一个车间可以允许由多个工人协同完成一个任务)
    • 注意:线程是不能再开线程的,除非通过其他技术手段。
    • 进程是车间,那么线程就是车间的工人
    • Javascript是单线程的,那么为什么Javascript要是单线程的?
      • 这是因为Javascript这门脚本语言诞生的使命所致:JavaScript为处理页面中用户的交互,以及操作DOM树、CSS样式树来给用户呈现一份动态而丰富的交互体验和服务器逻辑的交互处理。如果JavaScript是多线程的方式来操作这些UI DOM,则可能出现UI操作的冲突;
      • 如果Javascript是多线程的话,在多线程的交互下,处于UI中的DOM节点就可能成为一个临界资源,假设存在两个线程同时操作一个DOM,一个负责修改一个负责删除,那么这个时候就需要浏览器来裁决如何生效哪个线程的执行结果。当然我们可以通过锁来解决上面的问题。但为了避免因为引入了锁而带来更大的复杂性,Javascript在最初就选择了单线程执行。
    • 进程与线程的关系
      • 进程是操作系统分配资源的最小单位,线程是程序执行的最小单位。
      • 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线
      • 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)
      • 调度和切换:线程上下文切换比进程上下文切换要 快得多
  • 线程的通信
    • 面试问:HTML和CSS是渲染引擎渲染的,但是JS引擎只能解析和执行JS代码没有渲染的能力,那么JS的渲染呢?
    • 答:线程与线程之间是可以跨线程通信的,JS引擎与渲染引擎跨线程通信,但是线与线之间的通信肯定比线程内部自身的渲染慢(这个操作也叫做:DOM操作慢)

JS引擎

  • JS引擎举例
    • Chrome用的是V8引擎,C++编写
      • 小贴士:为什么叫C++编写呢?
      • 因为C++在所以语言中他特别快,属于性能王者
    • 网景(已倒闭)用的是 Spidermonkey引擎,后被Firefox使用,C++编写
    • Safari用的是Javascriptcore引擎
    • IE用的是 Chakra(script9)引擎
    • Edge用的是 Chakra(Javascript)引擎
    • 最新IE已经开始使用V8引擎
    • Node.js用的是V8引擎
  • 主要功能
  • 编译:把JS代码翻译为机器能执行的字节码或机器码
  • 优化:改写代码,使其更高效(注意:只有听JS引擎让你写的方式写,只有他才能让你更高效)
  • 执行:执行上面的字节码或者机器码
  • 垃圾回收:把JS用完的内存回收,方便之后再次使用
  • JS代码在内存中运行

内存图

  • 作用
    • 红色专门用来存放数据,前端最重要的区
    • 红色区域不存变量名
    • 变量名存在【不知什么区】
    • 注意:每种浏览器的分配规则并不一样
    • 图中的区域并不完整还有【调用栈】,【任务队列】等区域未显示
  • Stack和Heap
    • 红色区域分为Stack栈和Heap堆
    • Stack区特点:每个数据顺序存放(必须顺序存放不准跳格子)
    • Heap区特点:每个数据随机存放(每次数据都是随机存放的,若是再次添加数据时空间不够大,则删除原先的数据,重新找一个够大的空间存放)

代码举例

  • 代码
var a = 1
var b = a 
var perspon = {name : 'frank',child:{name : 'jack'}}
  • person2.name = 'ryan'person.name是多少?
    • 答:是'ryan',因为personperson2他们都存了里一个相同的地址#108#108地址被改变了,那么personname也被改变了.

  • 代码运行结果

  • 规律

    • 数据分两种:非对象和对象
    • 非对象都存在 StackStack 就存放了一个最终地址
    • 对象都存在Heap
    • =号总是会把右边的东西复制到左边(不存在什么传值和传址)

(JS世界)准备了什么?

  • 一行代码在运行前,JS是什么样的呢?
    • 答:提供API: window/ document/ settimeout
    • API这些东西都不是JS自身具备的功能
    • 我们将这些功能称为运行环境 runtime env
    • 一旦把JS放进页面,就开始执行JS
  • 这些API中window具体又有些什么呢?
    • console【控制台】并且挂到window
    • document【文档】挂到window
    • 有对象Obiect并且挂到 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()
    
  • 问:这些为什么都window挂在上?
    • 答:因为方便,挂在window上的东西可以在任何地方直接用

widow在内存图中样子

  • 重点简写图中:俗称的(指向,引用,指针)都是之指 (保存地址)
  • 注意
    • window变量和window对象是两个东西
    • window变量是一个容器,存放window对象的地址
    • window对象是Heap里的一坨数据
  • 同理
    • console变量和console对象也不是同一个东西
    • Obiect变量和Obiect函数对象也不是同一个东西
    • 前者是内存地址,后者是一坨内存数据

原型链(prototype)

  • 打印结构
    console.dir(window.Object.prototype)
    
  • 代码1
    var Obj={}
    obj.toString()
    
  • 代码2
    var Obj2={}
    obj.toString()
    
  • 代码3
    arr.hasOwnproperty( )
    

  • 问:代码1为什么可以运行?不报错?
    • obj有一个隐藏属性_proto_
    • 隐藏属性_proto_存储中了Object. prototype对象的地址
    • 在代码中obj.toString()发现obj上没有toString这个属性,就去隐藏属性_proto_对应的对象里面Object. prototype找于是就找到了
    • Object. prototype. toString
  • 问:那么window.Object.prototype.toString是怎么获取obj的内容的呢?
    • 那是因为obj.toString()等价于obj.toString.call(obj)
    • 同时obj.toString.call(obj)等价window.Object.prototype.toString.call(obj)
    • 这句话把obj传给toString了。
  • 代码1和代码2的相同与不同?
    • 相同
      • 都可以调用. tostring()
    • 不同
      • 代码1的地址是#555,代码2的地址是#666,两者的地址都是不同的单独的
    • 注意:根据地址不同引出以下问题
      • 如代码1和代码2他们都调用了共同的.tostring(),那么Obj2.tostring()='XXX',那么代码1的.tostring()会被改变吗?
      • 通过下图我们可知对Obj的修改只能改一层,而Obj中的toString连起来是有2层的,故而修改Obj2.tostring只能在他自身添加一个toString='XXX',对代码1的Obj没有影响。
  • 结论:通过代码1,代码2,代码3
    • 我们可知XX.prototype存储了,XXX对象的共同属性:这个就是原型,也叫共有属性
    • 每个对象都有一个隐藏属性_proto_:指向原型(对象)prototype
  • prototype_proto_区别是什么?
    • 两者都存着原型的地址
    • 只不过prototype挂在函数上
    • _proto_挂在每个新生成的对象上
  • 问:代码3arr.hasOwnproperty( )为什么不报错?为什么可以运行?
    • 答:arr有一个隐藏属性
    • 隐藏属性存储了Array prototype对象的地址
    • arr.hasOwnproperty( )发现arr上没有hasOwnproperty
    • 就去隐藏属性对应的对象里面找
    • 于是就找到了Arry. prototype.hasOwnproperty

作业

假设有如下代码

var obj = {}
var arr = [1,2,3]

请在纸上画出 obj、arr 与 window、Object 和 Array 的关系

WechatIMG21.jpeg

资料引用:
饥人谷方方老师
本文参考了饥人谷内部课程