前言:这篇文希望可以让自己更了解原始型态与物件型态在记忆体的Stack跟Heap上的变化,并了解物件内部的属性及值在不同情况下的移动路径。
记忆体(memory)分两区:
Stack:主要是放置变数及函式陈述式(具名函式)的名字、原始型(primitive type)的地方。
Heap Memory:主要放置函式陈述式(具名函式)的function()、Reference Type。
变数、函式、物件在记忆体中的位置及堆叠变化
图片来源:V8 Memory usage(Stack & Heap)
1. 当变数被赋予纯值(七个原始型)时:会在记忆体上的Stack区标记储存的地址及值。
var myNumber = 23
var myNumber = 23 var newVar = myNumber console.log(myNumber); //23 console.log(newVar); //23
var myNumber = 23; var newVar = myNumber; myNumber = myNumber + 1; console.log(myNumber); //24 console.log(newVar); //23
2.当变数被赋予物件型态时:会在记忆体上的Stack区标记储存的地址、值(为放在Heap Memory内容的address)。
var myArray = [];
如果新变数重新更新了旧物件里面的属性之值,旧变数赋值给新变数,而值是Heap Memory内容的address,值(address)都一样,但是Heap Memory的内容已经发生改变,导致旧变数物件的属性之值也跟着一起更新。
var person = { name: '小名', money: '500', }; console.log(person.name); //'小名'
var person = { name: '小名', money: '500', }; var person2 = person; console.log(person.name); //'小名' person2.name = '杰伦'; //更新了物件里属性:name 里面的值:'小名'->'杰伦' console.log(person.name); //'杰伦' console.log(person2.name); //'杰伦' console.log(person2 === person); //true
var person = { name: '小名', money: '500', }; var person2 = person; person2= { name: '小名', money: '500', }; console.log(person.name); //'小名' console.log(person2.name); //'小名' console.log(person2 === person); //false
console.log(person2 === person); //false
解:
因为person2变数重新创造了一个物件,导致Heap Memory内容的address跟person不同,即person有1个adress,person2有1个adress(2),adress不同代表值不同,所以false。
3.例子1:
var family = { //变数family;值是adress01 name: '小明家', //属性name;值是小明家;存在heap memory的aderss01。 members: { //属性members;值是adress02 father: '老爸', //属性father;值是老爸;存在heap memory的aderss02。 mom: '老妈', ming: '小明' }, }; var member = family.members; //将值adress02赋予新变数member。 console.log(member, family.members);// member === family.members
var family = { //变数family;值是adress01 name: '小明家', //属性name;值是小明家;存在heap memory的aderss01。 members: { //属性members;值是adress02 father: '老爸', //属性father;值是老爸;存在heap memory的aderss02。 mom: '老妈', ming: '小明' }, }; var member = family.members; //将值adress02赋予新变数member。 member = { ming: '大明', }; //新变数member重新设立新的物件,值是adress03 console.log(member, family.members); //大明,小明
var family = { //变数family;值是adress01 name: '小明家', //属性name;值是小明家;存在heap memory的aderss01。 members: { //属性members;值是adress02 father: '老爸', //属性father;值是老爸;存在heap memory的aderss02。 mom: '老妈', ming: '小明' }, }; var member = family.members; //将值adress02赋予新变数member。 member.ming = '大明'; //将属性ming的值变更为大明 console.log(member, family.members); // 大明,大明 console.log(member === family.members); //true,因为值都是adress02
例子2:
var a = { x: 1 }; var b = a; a.y = a = { x: 2 }; console.log(a.y); console.log(b);
1. var a = { x: 1 };var b = a;
####2. a.y = a = { x: 2 };
**注意!!**此步骤是同时进行,意思为 a = { x: 2 }与a.y = { x: 2 }是同时进行。
a = { x: 2 }: 创立新的物件,adress是adress02,并且赋予值(adress02)给变数a。
a.y = { x: 2 }:创立新的物件,adress是adress02;在变数a,值为adress01的物件里创立属性为y且值为adress02。
最后会变成:
3. console.log(a.y);
因为变数a的值是adress02,adress02里面的属性只有x,无y,所以为undifined。
4. console.log(b);
变数b的值是adress01,adress01里面的属性有x且值是1,y的值里面含另一个物件,里面有属性x且值是2。
例子3:
var a = { x: 1 }; var b = a; a.x = { x: 2 }; a.y = a = { y: 1 }; console.log(a); //{y: 1} console.log(b); //{x: {x: 2}, y: {y: 1}}
简单拆解:
var a = { x: 1 }; var b = a;
a.x = { x: 2 };
a.y = a = { y: 1 };
最后变成:
最后就是连连看。
参考文章:
JavaScript’s Memory Model:https://medium.com/@ethannam/javascripts-memory-model-7c972cd2c239JavaScript's Memory Management Explained:https://blog.openreplay.com/javascript-s-memory-management-explainedVisualizing memory management in V8 Engine (JavaScript, NodeJS, Deno, WebAssembly):https://deepu.tech/memory-management-in-v8/六角学院:JavaScript 必修篇 - 前端修练全攻略:https://www.hexschool.com/courses/js-plus.html