Day.19 「认识 JavaScript 记忆体堆叠、传值 与 传址」 —— JavaScript 物件 与 记忆体

「认识 JavaScript 记忆体堆叠、传值 与 传址」 —— JavaScript 物件 与 记忆体

我们的变数在我们开启网站时,都会存放在记忆体内,当我们关闭网站时,记忆体也会将这些变数释放。

记忆体的堆叠

JavaScript 变数都是保存在 Stack 中

Stack

而基本型别的值会直接储存在 Stack 中,值与值之间独立存在。

var a = 1;
var b = a;  // b = 1
a++;        // a = 2
console.log("a = " + a + ", b = " + b);  // "a = 2, b = 1"

Stack 示意图

Heap

物件型别的会保存在 Heap 中
每新增一个新物件(new Object),都会在 Heap 中开辟一个新空间。
而物件变数保存的会是指向新空间的地址(物件的引用复制),
如果新变数复制的是同一个物件,当一个物件属性修改,另一个也会受到影响!

var obj = new Object;   // 开辟新地址
obj.name = "毛毛";       // { name: "毛毛" }
var obj2 = obj;         // 复制地址
obj2.name = "鲑鱼";      // 更改 obj2 { name: "鲑鱼" }
console.log( obj.name );// 因为参考地址一样,obj 也被修改 { name: "鲑鱼" }

Heap 示意图

传值(Pass by value)

所以根据上面的记忆里内存,可以了解到单纯的基本型别存入的就是单纯的值。
基本型别复制到别的变数,这个过程称为传值,复制过去後就是独立的变数。
而变数的比较就是 Stack 值的比较,这比较简单好懂。

var a = 1;
var b = 1;
console.log( a === b );  // true

传址(Pass by reference)

而比较复杂的物件呢?上面也有看到物件存在 Stack 中的是 Heap 地址。
物件型别复制到别的变数,这个过程称为传址,复制过去後,使用的都是同一个物件(地址)
而变数的比较是 Stack 值得比较,如果是新增物件的话,Stack 存入的地址会不一样,所以会回传 false

var obj = new Object;        // 开辟新物件地址
obj.name = "毛毛";            // { name: "毛毛" }
var obj2 = obj;              // 直接复制地址引用同个物件 { name: "毛毛" }
var obj3 = { name: "毛毛" };  // 开辟新物件地址 { name: "毛毛" }
console.log( obj === obj2 ); // true   引用同一个物件
console.log( obj === obj3 ); // false  虽然物件看起来一样,但引用地址不一样

Pass by reference 示意图

共享(Pass by sharing)

来罗!让人头昏昏眼花花的概念,物件遇上函式作用域就更加暧昧更加复杂了,上一篇认识了函式中的函式作用域,所以我们知道

当 参数 是 基本型别

基本型别的参数,其实就等於在函式作用域宣告变数传值,传值独立的关系,自然影响不到外面的变数,除非使用 return 回传。

function change (a, b) {
  var c = b;
  b = a;
  a = c;
  console.log( "a = "+ a, "b = "+b);
}
var x = 1;
var y = 2;
change(x, y);  // "a = 2" "b = 1"
console.log( "x = "+ x, "y = "+ y ); // "x = 1" "y = 2"

当 参数 是 物件型别

物件型别的参数,就变成在函式作用域宣告变数并传址,传址因为是引用同一个物件,所以对属性更动时,就会影响到外面的物件变数!

function rename(obj) {
  obj.name = "鲑鱼";  // 修改物件内的"属性"
}

var person = { name: "毛毛" }
console.log( person.name ); // "毛毛"
rename( person );
console.log( person.name ); // "鲑鱼"

没错!竟然修改了函式作用域外的东西了,因为指向的是同一个物件!

function rename(obj) {
  obj = { name = "鲑鱼" } // 在作用域里面新增物件地址
}

var person = { name: "毛毛" }
console.log( person.name ); // "毛毛"
rename( person );
console.log( person.name ); // "毛毛"

但如果是直接对整个物件修改,就会发现作用域外面不会被影响了,因为它等同於物件实字新增了一个新物件地址,这个值无法传递到外面。

总结

以上物件可以套用到阵列,这样我们就认识物件型别与基本型别不同的地方了,有点抽象!传值传址的概念在写 JavaScript 逻辑时,非常重要,未来在学框架时,这些基础越扎实,框架就学的越快越轻松!

参考资料


<<:  【day13】DashboardFragment X CardView

>>:  Day28 修复老照片

Day 03 Python 入门

因为考虑到才第三篇就开始飙车直接上 Flask 会不会太快,加上这系列有一小部分原因(大约50%?)...

群辉ds920+nas网路储存设备简易开箱,满足家庭影音需求

Synology DS920+nas网路储存设备 是一款适合家庭使用的 4 盘位 NAS。除了拥有4...

强型闯入DenoLand[30] - Web API 实作篇

强型闯入DenoLand[30] - Web API 实作篇 在介绍完 Denon 的使用方法以及...

Day 4 - Just In Time (JIT) 即时模式

JIT 即时模式 继上一篇提到开启 JIT 模式有许多优点,今天威尔猪就来浅谈这个有点厉害的新即时编...

Day05【Web】Websocket、Polling 与 SSE

WebSocket HTTP 协议中,只能由 Client 端发出请求 如果要由 Server 端主...