Python与JavaScript 内存存储与深浅拷贝
Python 与 JavaScript 变量内存存储机制对比含缓存优化原理JavaScript 和 Python 是目前最热门的两大脚本语言二者的变量赋值、数据存储、参数传递看似行为相似但底层内存模型完全不同。很多开发中遇到的经典问题为什么整数赋值后互不影响列表 / 数组赋值后会联动修改Python 为什么 100 is 100 恒成立而 300 is 300 不一定字符串相同内存地址却不一样本质都是 内存存储模型 语言缓存优化机制 导致的差异。本文系统性对比 Python / JS 变量内存存储、赋值逻辑、传参机制、垃圾回收、专属缓存优化小整数池、字符串驻留帮你彻底吃透底层原理。核心底层本质差异最关键Python万物皆对象Python 中所有数据都是堆中的对象数字、字符串、布尔、 元组、列表、函数全部是对象。变量没有类型、不存数据只是对象的引用标签内存地址指针真正的数据、类型、内存地址全部保存在堆对象中核心区分可变对象、不可变对象JavaScript原始值 引用对象 双模型JS 采用两套完全独立的存储规则基本原始简单数据类型Number、String、Boolean、Null、Undefined、Symbol、BigInt值直接存在栈内存引用复杂数据类型Object、Array、Function数据存在堆内存栈中仅存堆的内存地址变量直接存储「值」或「地址」变量本身有明确存储内容内存分区与基础赋值逻辑对比内存分区通用规则所有高级语言内存分为两块栈内存空间小、读写快、自动释放存放变量、基础值、引用地址堆内存空间大、持久存储需要垃圾回收存放复杂对象数据Python 赋值底层逻辑Python 没有任何数据存在栈中栈永远只存指针a 10堆内存创建整数对象 10栈内存变量 a 存储该对象的内存地址b ab 和 a 指向同一个堆对象核心特点赋值永远是「引用拷贝」不拷贝数据本身。JavaScript 赋值底层逻辑原始简单类型栈内存直接存储数值 10赋值是完整的值拷贝两个变量完全独立。leta10;letba;a20;// b 仍然是 10引用复杂类型栈中存储堆数组的地址赋值拷贝地址多个变量共享堆数据。leta[1,2];letba;a.push(3);// b 同步变为 [1,2,3]Python可变 / 不可变数据不可变数据整数、浮点数、字符串、布尔、元组、复数、冰冻集合不可变对象无法修改原有堆数据重新赋值会新建对象、更换引用。a100ba a200# 新建200对象a更换指向b不变print(b)# 100可变数据列表、字典、 普通集合Python List、Dict 为堆中可变对象修改数据是直接修改堆内存原数据所有指向该对象的变量数据会同步变化函数参数传递机制Python统一「传引用」传不可变对象函数内重新赋值会新建对象外部数据不变传可变对象函数内修改原堆数据外部同步改变JavaScript统一「按值传递」原始类型拷贝真实数值内外完全隔离引用类型拷贝堆地址可通过地址修改堆原数据重点Python 专属内存优化机制Python 为了减少频繁创建、销毁对象的性能开销设计了两大缓存机制小整数池、字符串驻留这是 JS 不具备的显式机制。小整数池整数缓存机制原理Python 解释器启动时提前在堆中预创建 -5 ~ 256 所有整数对象常驻内存、永不销毁。开发中最常用的小整数全部复用缓存对象无需重复开辟内存极致优化性能。代码验证# 在交互环境测试a100b100print(id(a)id(b))# True共用同一个对象c300d300print(id(c)id(d))# False超出缓存池新建对象补充说明脚本文件运行时会触发常量折叠编译优化大于 256 的相同整数也可能地址一致这不是小整数池机制属于编译优化和运行时缓存无关字符串驻留String Interning原理字符串是不可变对象Python 将高频重复的规则化字符串缓存到全局驻留池相同内容的字符串只存储一份实现内存复用、加速比对。自动驻留规则只有满足以下条件才会自动进入驻留池仅包含字母、数字、下划线合法标识符格式无空格、无特殊符号、无中文代码演示# 自动驻留s1user_123s2user_123print(id(s1)id(s2))# True# 不满足规则不驻留s3hello worlds4hello worldprint(id(s3)id(s4))# False手动强制驻留通过 sys.intern() 可强制任意字符串加入缓存池importsys asys.intern(hello world)bsys.intern(hello world)print(id(a)id(b))# True两大核心优势节省内存接口 Key、变量名、固定文本大量复用避免冗余存储比对极速is 直接比对内存地址远快于 逐字符比对JS 与 Python 核心差异总结表对比维度PythonJavaScript存储核心万物皆堆对象变量只存引用地址原始值存栈对象存堆双存储模型赋值逻辑永远拷贝对象引用原始值拷贝值引用类型拷贝地址整数优化拥有 -5~256 小整数缓存池无整数缓存机制字符串优化严格规则的自动驻留池 手动驻留V8 隐式驻留无公开固定规则参数传递传对象引用统一按栈内值传递垃圾回收引用计数 分代 GC标记清除算法数据区分可变 / 不可变对象原始类型 / 引用类型终极一句话总结Python没有变量存数据所有数据都是堆对象一切赋值都是引用传递靠小整数池和字符串驻留极致优化内存性能JavaScript基础值栈上直接存储复杂数据堆存储分层管理内存无显式常量缓存池机制。Python与JavaScript 赋值、深浅拷贝Python 赋值、深浅拷贝可变类型和不可变类型划分不可变类型:数值、字符串、元组.可变类型:列表、字典、集合总结:在不改变地址值的情况下,内容可以改就是可变类型,不可以改就是不可变类型地址赋值、浅拷贝、深拷贝规则纯不可变类型:普通赋值、浅拷贝、深拷贝, 地址都不变原值修改不影响拷贝后的值可变类型:普通赋值, 地址不变; 浅拷贝、深拷贝, 地址会变普通赋值、浅拷贝, 原值修改 影响 拷贝后的值; 深拷贝,原值修改不影响拷贝后的值深拷贝内部逻辑遍历要拷贝对象的每一层若当前层是可变对象list / dict / 自定义实例开辟新堆内存完整复制一份若当前层是纯不可变对象tuple / int / str为了性能优化直接返回原对象引用不新建内存。注意点# 不可变类型 值相同 内存中只存一份a3b3print(id(a))# 4545460504print(id(b))# 4545460504print(id(3))# 4545460504# 如果深拷贝遇到纯不可变对象tuple/int/str/float不会新建内存直接复用原对象地址a_tuple((1,2,3),)b_tuplecopy.deepcopy(a_tuple)print(id(a_tuple)id(b_tuple))# True# 深拷贝拷贝非纯不可以变类型地址会变a_tuple([[1,2,3],[4,5,6]],)b_tuplecopy.deepcopy(a_tuple)print(id(a_tuple)id(b_tuple))# Falseprint(id(a_tuple[0])id(b_tuple[0]))# Falseprint(id(a_tuple[0][0])id(b_tuple[0][0]))# Falseprint(id(a_tuple[0][0][0])id(b_tuple[0][0][0]))# True deepcopy对不可变对象的复用策略导致的JavaScript 赋值、深浅拷贝浅拷贝方式扩展运算符 …最常用Object.assign()深拷贝方式Lodash 深拷贝 _.cloneDeepJS中主要是针对引用复杂数据类型方式新外层对象第一层数据独立嵌套对象独立底层原理直接赋值❌ 无新对象❌ 共享引用❌ 共享引用复制内存地址共用堆对象浅拷贝✅ 新建外层✅ 基本类型独立❌ 嵌套共用引用只递归一层深层仅复制地址深拷贝✅ 全新外层✅ 全部层级独立✅ 所有嵌套全新递归创建每一层对象完全隔离