栈空间和堆空间
JS是什么类型的语言
在声明变量之前需要先定义变量类型,这种在使用之前就需要确认其变量数据类型的语言称为静态语言
这种类型的语言有C语言
而JS是动态语言,指的是在运行过程中需要检查数据类型,也就是在声明变量之前不需要确认其数据类型
除了动态语言,JS还是一门弱类型的语言,弱类型语言指的是支持隐式类型转换的语言
JS的数据类型
现在我们知道,JS是弱类型、动态的语言:
- 弱类型:不需要告诉JS引擎某个变量是啥数据类型,JS引擎在运行代码的时候会自己计算出来
- 动态:可以使用同一个变量保存不同类型的数据
JS的数据类型有8种:undefined、null、number、string、boolean、object、symbol、bigInt
除了对象是引用数据类型,其他都是基本数据类型(原始类型)
内存空间
JS的内存空间分为三种:代码空间、栈空间、堆空间
代码空间主要是存储可执行代码的,这里先不做介绍
栈空间和堆空间
这里看一段代码:
function foo(){
var a = " 极客时间 "
var b = a
var c = {name:" 极客时间 "}
var d = c
}
foo()
栈空间就是存储执行上下文的
由于变量a和b都会被保存在执行上下文中,而执行上下文又被压入栈中,所以也可以认为变量a和b存放在栈中
但是c和d的处理就不一样了,变量c和d不会将对象直接存放到变量环境中,而是分配到堆内存中,分配后该对象会有一个在堆中的地址,然后再将这个地址写进c和d的变量值中
也就是说,对象类型存放在堆空间中,栈空间中只是保留了对象的引用地址
这里分为栈和堆两个内存空间是因为:
栈是用来维护程序执行期间上下文的状态的,如果栈空间太大,所有数据都存放在栈空间中,则会影响上下文切换效率,从而影响整个程序的执行效率
所以一般栈空间不会太大,主要存放原始数据类型的一些数据,而引用数据类型一般都存放在堆中,因为堆内存比较大,但是这样分配内存和回收内存都会占用一定的时间
再谈闭包
我们再从内存角度了解一下闭包
function foo() {
var myName = " 极客时间 "
let test1 = 1
const test2 = 2
var innerBar = {
setName:function(newName){
myName = newName
},
getName:function(){
console.log(test1)
return myName
}
}
return innerBar
}
var bar = foo()
bar.setName(" 极客邦 ")
bar.getName()
console.log(bar.getName())
可能在分析执行上下文的时候会认为,myName和test1应该存在于foo的变量环境和词法环境中,foo函数执行完后,应该会把执行上下文清理掉,那为什么这里还保持着引用?
其实是因为JS引擎会对内部函数做一次快速的词法扫描,发现用用了外部函数的变量,则判断是一个闭包,会在堆空间中创建一个空间存放闭包对象
所以foo函数被销毁后,闭包还是存放在堆空间中不会被销毁,所以才可以保持引用关系
总的来说,产生闭包的核心有两步* *:第一步是需要扫描内部函数;第二步是把内部函数引用的外部变量保持到堆中