js中的堆栈内存和闭包理解

要理解堆栈,先理解js的数据类型
1.基本类型(也叫值类型):number,string,boolean,null,undefined
2.引用类型:object,function
3.特殊类型:symbol

熟悉了基本类型和引用类型后来看看什么叫栈内存吧!
栈内存stack:
1.提供一个供js执行的环境(js都是在栈中执行的)
2.存储js的基本数据类型,由于基本数据类型比较简单,所以都是直接在栈内存中开辟一个位置存储的。
=> 当栈内存被销毁,存储的那些基本值也跟着销毁了

堆内存heap:
1.存储引用类型值的(对象:键值对,函数:代码字符串)
=>当前堆内存被销毁时,里面的引用值也被销毁
=>堆内存的释放:当堆内存没有被任何的变量或者其他东西所占用,浏览器会在空闲的时候,自主的进行内存回收,把占用的堆内存销毁(谷歌浏览器是这样,但是ie不同)
xxx=null 通过空对象指针可以让其不再指向,那么原有被占用的堆内存就没有被东西占用,浏览器会销毁它

了解了堆栈的概念来做一下两道面试题吧!
在这里插入图片描述
alert(a) 的结果是’0’,字符串的0(因为alert弹出的都是字符串);
过程:
首先要明白,所有变量和值的关联都是通过指针来实现的,在执行上下文的时候,遇到变量赋值,
步骤都是:1.创建变量 2.创建值 3. 关联
所以在一个执行栈中如图所示:
在这里插入图片描述
然后就是b++,由于一个变量只能关联一个值,(但是一个值可以关联多个变量),所以
在这里插入图片描述
alert(o.a)是’10’;过程:

在这里插入图片描述
和上个步骤基本差不多,不过这里是通过创建堆,然后通过指针关联到指定地址而已。AAAFFF0000是十六进制的堆地址.
在这里插入图片描述
首先要理解a = b = 1的意思是 a = 1 ; b = 1;所以里面的 a.x = a = { n: 2 }也可以理解了。
这是前面两个let定义赋值
在这里插入图片描述
然后要再理解一个赋值的相关操作就是:
赋值的话,一个等号是从右到左,但是这个是赋值,连等的话如a.x=a=1,即分为a.x=1;a=1;是先a.x=1再a=1的,所以这个是从左到右的(如果这里的a和a.x换了顺序结果又会变化了)。
然后看下图:这里是先执行a.x = {n;2}的操作,要注意只要引用类型赋值,就会重新创建一个堆,所以这里是又创建了一个堆来存储n:2,然后x指向这个新堆的十六进制地址.
在这里插入图片描述
接下来是执行a = {n : 2},
在这里插入图片描述
所以可以看到,a指向新堆后,里面没有x,故此时的x.a是undefined,其实注意点就是:
1.引用类型赋值会创建新堆! 2.步骤是先创建值(堆)再进行指向。这些都是js的底层执行机制

然后让我们再了解一下GO/VO/AO/EC及作用域和执行上下文
GO:Global Object全局对象.
当浏览器打开每一个网页之初,都会默认创建一个globalObject对象,将我们常用的一些属性和方法都挂载上去,以供我们调用,都是一些内置的方法。所以我们经常有window.xxx来调用一些东西,this也是指最高全局变量window,而window经常被省略。
在这里插入图片描述
ECStack:Execution Context Stack 执行环境栈
EC:Execution Context 执行环境(执行上下文)
VO: Varibale Object 变量对象
AO: Activation Object 活动对象

VO可以理解为AO的集合,AO为VO的一个分支,一个特殊对象,一般函数执行的时候的函数叫做AO
Scope:作用域创建的函数的时候就赋予了
Scope Chain : 作用域链

下面再做两道题
在这里插入图片描述
这里要注意:
1.不用let/var/const定义的为全局变量
2.let a = b = 13;的意思是 let a = 13;b = 13,也就是说这里的b = 13是全局变量。

明白这两点,这道题便可以解出来了, fn()的结果是 13,13,
console.log(a,b)的结果是12,13,b之所以是13是因为后面被改掉了。
在这里插入图片描述
过程不细说了,答案如下:
4,8,8,1

另外再看:
在栈中,即简单变量储存这块,var b = a;把a的存储的值放到一个新位置上,是直接操作值的,让新位置上的值和b保持关联,但此时的a和b是没有关系的!要记住是新开发的位置进行存储,而不是跟堆一样可以两个变量指向同一个值!
在这里插入图片描述

而在堆中可以由图看到,var ary2 = ary1,因此两个ary指向同一地址的堆,当ary中的值改变时,ary1也变化了!

下面再了解一下堆栈内存释放:
栈内存:
一般情况下,当函数执行完成,所形成的的私有作用域(栈内存)都会自己被释放掉,同时栈内存中存储的值也都会被释放,但也有特殊不销毁的情况:
1.函数执行完成,当前形成的栈内存中,某些内容被栈内存意外的变量占用了,此时的栈内存不能释放,因此一旦释放外面内容就会丢失
2.全局栈内存只有在页面关闭的时候才会被释放掉

如果当前栈内存没有被释放,那么之前在栈内存中存贮的基本值也不会被释放,能够一直保存下来.

堆内存:
让所有引用堆内存空间地址的变量赋值为null即可(没有变量占用堆内存,浏览器会在空闲时候将它释放掉)

下面再来看一下闭包
闭包的定义有很多,如MDN的:函数和对周围状态(词法环境)的引用捆绑在一起构成闭包;也就是说,闭包可以让你从内部函数访问外部函数作用域。在js中,每当函数被创建,就会在函数生成时生成闭包。
其实大概意思都差不多:基本都是从闭包的功能进行说明的,闭包主要是为了保护里面的变量和属性不被外界所污染。
闭包的例子有:
柯理化函数:
在这里插入图片描述
惰性函数:
在这里插入图片描述
闭包在项目中的实战应用: 真实项目中为了保证js的性能(堆栈内存的性能优化),应该减少闭包的使用(不销毁的堆栈内存是消耗性能的)
1.闭包具有保护作用:保护私有变量不受外界的干扰;在项目开始时,尤其是团队协作开发时,应当尽可能的减少闭包的使用,但是为防止相互之前的冲突,也就说所谓的全局变量污染经常将全局变量转化为私有变量,即
弄成立即执行函数
在这里插入图片描述

2.闭包具有保存作用:形成不销毁的栈内存,把一些值保存使用

下面从 [[Environment]] 周围状态 的角度理解一下闭包
那什么是Environment呢,跟prototype原型对比理解一下:
在这里插入图片描述
再看一下ECMA的解释:
[[Environment]]: 使得函数“关闭”的词法环境,在函数代码运行时作为外部环境来使用。。
emm每个字都认识,但是连起来就看不懂了,没关系,外面继续往下看!
首先了解一下词法环境对象:
只关心前两个即可在这里插入图片描述
这个公文包即一个词法环境对象,可以理解为作用域
在这里插入图片描述
理解为作用域后就很好理解了,fn函数的变量等其他东西都存在里面
在这里插入图片描述
当前函数的词法环境对象可以访问到外层函数的词法环境对象,直到全局的词法环境对象,相当于作用链。
在这里插入图片描述
然后再看下这道题:
在这里插入图片描述
结果想必大家都是知道的,需要注意的是什么呢:
函数实例被创建时会生成一个词法环境对象,也就是如图:从fout函数依次到外面寻找就像图画的一样
在这里插入图片描述
同理b的也是一样;
在这里插入图片描述
!重点是什么呢:
fa和fb是两个不同的函数,就是因为它们的词法环境对象不同,即它们的作用域也不同,虽然说内容一样,但在意义上是不一样的。

死锁问题的出现与解决:
在这里插入图片描述
在这里插入图片描述

解决就是通过闭包来解决啦:先i定义一个函数使用闭包,让其可以访问到外部的变量
在这里插入图片描述

文章是学习b站视频后的学习笔记

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值