栈 stack 堆 heap
数据类型
- 基础数据类型
- 引用数据类型
- 特殊情况 -> 闭包中的数据
如果是基础类型,那栈中存的是数据本身。
如果是引用类型,那栈中存的是堆内存地址的引用。
栈 stack
先进后出,后进先出 的数据结构称为栈
• 栈存储在一级缓存
• 基本数据类型用栈存储
• undefined
• null
• boolean
• string
• number
• bigint
• symbol
栈溢出问题
不同浏览器对调用栈的大小是有限制,超过将出现栈溢出的问题。下面这段代码可以检验不用浏览器对调用栈的大小限制。
var i = 0; function recursiveFn () { i++; recursiveFn(); } try { recursiveFn(); } catch (ex) { console.log(`我的最大调用栈 i = ${i} errorMsg = ${ex}`); }
谷歌浏览器: 15721
QQ 浏览器:31451
搜狗浏览器:31479
微软 Edge 浏览器:13456
堆 heap
• 堆存储在二级缓存中。
• 引用数据类型用堆存储。
• 闭包变量是存在堆内存中的。
• Object
• Function
• Array
• Date
• RegExp
与其他语言不同,JS 的引用数据类型,比如数组 Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JavaScript 不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。看一下下面的图,加深理解。
因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从栈中获取了该对象的地址引用(或者地址指针),然后再从堆内存中取得我们需要的数据
存储在堆内存中,是大小不定,复杂可变的。 Object 类型数据的 指针 存储在栈内存空间, 指针实际指向的值存储在堆内存空间。
函数 方法 闭包
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;
当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。
对于原始类型,数据本身是存在栈内,对于对象类型,在栈中存的只是一个堆内地址的引用。
看起来没有错误,但实际上是有问题的。可以考虑一下闭包的情况,如果变量存在栈中,那函数调用完栈顶空间销毁,闭包变量不就没了吗?
红框部分,与上述一致,同时也反应出了之前提及的问题:例子中 JavaScript 的变量并没有存在栈中,而是在堆里,用一个特殊的对象(Scope)保存。
function demo() { var myName = "掘金社区" let test1 = 1 const test2 = 2 var innerFun = { getName:function(){ console.log(test1) return myName }, setName:function(newName){ myName = newName } } return innerFun } var test = demo() test.setName("掘金世界") test.getName() console.log(test.getName())
思考
问:为什么会有堆内存、栈内存之分?
答:通常与垃圾回收机制有关。为了使程序运行时占用的内存最小;且和弱类型语言有关,数据类型不确定;
问:闭包的变量使用,存储地方?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 原型 原型链 继承
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论