副队长JavaScript教程(47)-对象的原理与内存模型、Object 与 null 点此查看所有教程、项目、源码导航文章目录1. 背景2. JS 对象的基本原理与内存存储模型2.1 对象的本质2.2 JS 的两块内存栈内存、堆内存2.3 简单类型存储演示2.4 对象引用类型存储演示2.5 怎么真正复制一个独立的对象2.6 属性的增删改查在内存中的表现3. Object 根构造函数3.1 Object 是什么3.2 Object 常用静态工具方法4. null 和对象的关系4.1 null 的含义4.2 经典历史坑typeof null4.3 null 和 undefined 的对比5. 对比汇总6. 小结1. 背景前面一系列文章我们学习了自定义对象、对象属性、对象方法、for...in遍历还有Math、Date、Array这些内置对象。到这里我们已经会使用对象了。但是想要理解底层逻辑就要搞懂JS 里面对象的底层原理、内存存储模型Object是什么以及null和对象的关系。本篇是 JS 对象系列的收尾文章偏向原理理解为后面原型、原型链打下扎实基础。务必注意本章内容偏原理和模型如果暂时无法理解没有关系对初学者开发简单项目影响很小。此处先写出来是为了便于加深认识。2. JS 对象的基本原理与内存存储模型2.1 对象的本质在 JavaScript 中对象本质上就是一个键值对key-value的无序集合。key 我们称之为属性名value 称为属性值属性值可以是任意 JS 数据数字、字符串、布尔、函数、数组、其他对象等等。varstudent{name:张三,age:20,sayHi:function(){console.log(你好);}};我们可以简单把对象理解成一张查找表表格属性名 key属性值 valuename“张三”age20sayHi函数当我们写student.name访问属性时JS 引擎拿着name这个 key去这张表里检索找到对应的 value 返回。如果查找的 key 不存在不会报错直接返回undefined。2.2 JS 的两块内存栈内存、堆内存想要看懂对象存储先要分清 JS 运行时两块核心内存栈内存Stack空间小、读写速度快。用来存放简单类型的值、变量标识、引用地址。简单类型number、string、boolean、undefined、null。堆内存Heap空间大速度相对慢一点。所有引用类型的数据全部存放在堆里面包括普通对象、数组。重点区分简单类型变量里直接存数据本身。对象引用类型变量里不存对象本身只存对象在堆内存中的地址。2.3 简单类型存储演示varnum110;varnum2num1;num220;console.log(num1);// 10内存过程栈中开辟空间num1存放值10var num2 num1把10这个值复制一份给到num2修改num2的值只是修改栈里num2自己的数据二者互不干扰。简单类型赋值是值拷贝。2.4 对象引用类型存储演示varobj1{name:张三};varobj2obj1;obj2.name李四;console.log(obj1.name);// 李四内存过程拆解执行{ name: 张三 }JS 引擎在堆内存开辟一块空间存放这个对象的完整键值对这块堆内存会产生一个唯一内存地址比如我们简化写成0x1001栈内存里的变量obj1保存的就是这个地址0x1001var obj2 obj1不是复制对象只是复制栈里面的地址此时obj1和obj2里面存着同一个地址指向堆中同一个对象通过obj2修改对象属性修改的是堆里面共享的那一份数据所以obj1读取的时候也跟着变化。对象的赋值是地址拷贝也叫引用传递。2.5 怎么真正复制一个独立的对象如果想要两个互不干扰的对象不能直接obj2 obj1需要新建对象把属性拷贝过去varobj1{name:张三};// 新建一个全新对象堆里开辟新空间varobj2{name:obj1.name};obj2.name李四;console.log(obj1.name);// 张三互不影响2.6 属性的增删改查在内存中的表现对象创建之后堆里面的这张键值表是可以动态修改的这也是 JS 对象灵活的地方varuser{name:小明};// 新增属性在堆的键值表里新增一条记录user.age18;// 修改属性找到已有key覆盖valueuser.name小红;// 删除属性从键值表里移除这条记录deleteuser.age;补充这里的动态增删和很多强类型语言Java/C不一样它们的对象结构定义好之后不能随意新增字段这是 JS 对象很鲜明的特点。3. Object 根构造函数3.1 Object 是什么Object是 JS 里面所有对象的根构造函数。我们日常写的字面量{}本质就是new Object()的简写形式二者创建出来的实例是同一类对象。// 两种写法效果基本等价varobj1{};varobj2newObject();我们前面学习的Array、Date的实例溯源上去它们的原型链最终都会走到Object.prototype。一句话理解JS 中几乎所有对象都是 Object 的后代。3.2 Object 常用静态工具方法Object除了可以当构造函数 new 实例还自带很多静态工具方法专门用来操作对象。varstudent{name:张三,age:20};// Object.keys取出对象自身所有属性名放到数组返回console.log(Object.keys(student));// Object.values取出对象自身所有属性值放到数组返回console.log(Object.values(student));对比之前的for...infor...in会遍历原型链上继承来的属性Object.keys()只会拿到对象自身的属性开发中更干净、更常用。重要区分不要混淆Math是一个已经创建好的静态对象直接使用Object是构造函数可以 new 产生实例同时挂载了一堆静态工具方法。4. null 和对象的关系4.1 null 的含义null是独立的简单数据类型它的语义是这里预期存放一个对象但是目前没有对象。相当于一个 “空对象占位符”。varobjnull;内存层面理解变量obj在栈内存中存的是一个特殊标记代表不指向堆里面任何对象。4.2 经典历史坑typeof nullconsole.log(typeofnull);// object这里是 JS 初代版本留下的历史 bug并不是说 null 就是对象。✅ 一定要牢牢记住结论null本身属于简单类型不是对象只是 typeof 检测结果写错了。4.3 null 和 undefined 的对比vara;console.log(a);// undefinedvarbnull;console.log(b);// nullundefined变量已经声明但是没有赋值或者访问对象一个不存在的属性。含义未定义。null开发者手动赋值主动标记 “此处没有对象”。清空对象变量时推荐使用 null。5. 对比汇总对象引用类型底层是堆内存中的键值对映射表栈变量保存堆的内存地址赋值是地址拷贝多个变量可以共用同一个对象。栈内存存简单类型值、引用地址堆内存存放对象、数组这类引用数据。Object所有对象的根构造函数字面量{}等价new Object()自带keys/values等工具方法。null简单类型用来表示 “没有对象”记住typeof null object是历史遗留 bug。6. 小结JS 对象本质是堆内存里动态的键值对集合支持运行时新增、修改、删除属性灵活性很高。简单类型保存在栈直接拷贝值对象保存在堆栈里只存地址赋值拷贝的是地址这是引用类型最核心的特点。Object是对象的根所有普通对象都源于 Object。null是空对象占位标记属于简单类型和真正的对象要区分开。