变量的本质和垃圾回收

本文最后更新于:2024年11月17日 晚上

1
2
3
4
5
6
7
8
9
10
11
12
13
var b1 =1 , b2 = b1;
b2 =2 ; //RHS right hand search 右查询读的是内存中的值
//由于基本类型是不可修改的,所以这里是先申请了栈里的一块空间,然后赋值为2,然后b指向那个新的地址,旧地方的地址会被垃圾回收给清除掉
console.log(b1,b1); // 1 2

var r1 = {
a:1,
},
r2 = r1; //栈里面的数据

r2.a = 2 ;

console.log(r1.a,r2.a); //2 2

从本质上理解变量

程序的本质是一个状态机(数据的集合),程序本质是只能访问栈,不能直接访问堆,但是可以通过标识符间接的访问堆,因为栈的访问速度快(因为栈的大小是确定的,可以直接找到变量的地址)

1.Primitive Type(原始类型)

值不可以改变的类型,是一个二进制的字符串,基本数据类型一般存在栈(FILO)里,还有引用类型的标识符

  1. NULL
  2. Undefined
  3. Boolean
  4. Number
  5. BigInt
  6. String
  7. Symbol

Stack

1.结构性强,内存连续

2.寻址速度快

3.数据稳定,每一个类型的大小都是确定的

4.容量小

2.Reference Type(引用类型)

Heap

  1. 树的结构,有大根堆和小根堆
  2. 存储的大小不一
  3. 容量大
  4. 不同数据间内存不连续

Object

Object 是 JavaScript 的一种 数据类型 。它用于存储各种键值集合和更复杂的实体。Objects 可以通过 Object() 构造函数或者使用 对象字面量 的方式创建。

对象是存在堆heap里的,因为对象的大小是不确定而且是可变的,所以不能放在栈里

特殊的字符串

字符串是无法修改的,是基本数据类型,但是其大小又是不确定的。修改值只会重新开辟一块内存空间

浏览器的垃圾回收机制有:标记清除和引用计数

当一个内存单元没有被使用的时候,会被标记为空,下次的数据就可以直接覆盖到上面

深拷贝和浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var person1 = {
age:28,
hobby:"学习",
son:{
age:3,
hobby:"drink milk",
firdens:['jack','luck']
}
}

var clone = person1; //读stack
clone.age = 18;
console.log(person1.gae); // 18

//浅拷贝
function clone2(obj){
var clonePerson = {}
//为什么不用clonePerson.key? 其实persone.age 会被进一步编译为 persone["age"]
//如果用clonePerson.key --> clonePerson["key"] ,但是clonePerson里面是没有这个键,意思就是 person[age],这里面的age是个变量
for(var key in obj){ //for in 会读原型链可枚举的属性
clonePerson[key] = obj[key];
}
return clonePerson;
}
//这里为什么是浅拷贝呢,因为这个里面的son其实拿到的还是地址,所以如果person1改了的话,clone还是会受到影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 深拷贝
function clone2(obj){
if(typeof obj !== "object" || obj === null){
return obj;
}

var clone = Array.isArray(obj)? [] : {};
for(var key in obj){
if(typeof obj[key] === 'object'){
clone[key] = clone2(obj[key]);
}else{
clone[key] = obj[key];
}
}
return clone;

}

垃圾回收机制

1
2
3
4
5
var foo = {a:1,b:{x:2}};
var bar = foo.b;
bar.c = foo;

console.log(bar.c.a);

原型链

理解new关键字的作用机制

理解 [[GET]]

浏览器

早期的浏览器是一个单进程的,有很多的线程,最重要的就是Page Thread

现代浏览器架构 (多进程)

主进程:

  1. 浏览器界面
  2. 用户交互
  3. 管理子进程
  4. 提供存储功能

网络进程:负责网络资源的请求和接收

GPU进程

插件进程

渲染进程(内核)

渲染进程是运行在沙箱中的,保证用户的安全,主要是渲染引擎JS解析引擎

浏览器内核执行机制

浏览器内核也就是渲染进程分别由渲染引擎和JS解析引擎构成

渲染引擎用来渲染页面,JS解析引擎用来解析JS代码,但是两个进程是不能直接通信的,就需要一个中间人来搭建桥梁,也就是事件循环

渲染引擎

  • 解析HTML,生成用于构建页面的信息
  • 如果遇到Script 标签,则停止解析,就会挂起解析HTML

JS执行机制

  1. 单线程执行

JS单线程不可怕,可怕的是他与渲染引擎是互斥的,会造成页面卡顿,无响应等

那么如何解决呢? 异步编程就是一种办法

同步: 在主线程中执行的代码

异步: 不在主线程执行,而是通过任务队列通知主线程

  1. 定时器
  2. 网络请求
  3. 与用户的交互
  4. 。。。。。

任务队列

  • 延时队列: 用户存放计时器线程包装的回调任务 优先级: 中
  • 交互队列: 用于存放用户操作事件产生后的事件处理任务 优先级: 高
  • 微任务队列: 优先级: 最高

只有同步任务执行完了,才会执行异步任务,那么怎么判断同步任务执行完成了呢?

也就是看栈,如果执行栈空了,就说明同步任务执行完了,可以去执行任务队列中的任务了

任务队列

1
2
3
4
5
6
7
8
9
10
11
var flag = true;

while(flag){
setTimeOut(function(){
flag = false;
},10);
}

//请问这个循环会结束吗?

//答案是不会的,因为JS是单线程的,为了j所以就有了异步和同步,只有执行完同步才会执行异步的任务,这里面会一直执行循环,也就是同步的任务栈中的任务是一直有新的任务压栈的,是不会为空的,所以异步任务的任务队列是执行不到的

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!