一、js数据类型
1.1 静态语言和动态语言
C语言、java、typescript等语言在定义变量时需同时定义其数据类型,比如int a = 1。这种需要在使用前定义其数据类型的语言为静态语言,在代码执行前会进行类型检查。
相反,需要在程序运行过程中检查数据类型的语言称为动态语言。
1.2 弱类型和强类型语言
在java中,我们不能把一个int类型的变量赋给String类型的变量,这会触发编译时报错。除非进行显式转换。这种必须指定类型,不支持隐式类型转换的语言为强类型语言。 相反,不需指定类型,支持隐式类型转换的语言为弱类型语言。在js中可以任意相互赋值不同类型的变量。变量的类型在运行时确定,或在需要时进行类型转换。
因此,javascript是弱类型的动态语言。无需声明变量类型且支持隐式类型转换。
let a = 1
a = 'cola'
a = null
a = { face: 'no' }
1.3 原始类型和引用类型
js中的数据类型共八种:null,undefined,number,bigint,string,boolean,symbol和object。
除了object为引用类型,其余其中都为原始类型。
我们可以通过typeof运算符查看数据的类型(除了null):
判断是否为null,只能使用 "===null"。详细内容可见这里。
二、内存空间
在js执行过程中,主要有代码空间、栈空间和堆空间三种。
- 代码空间主要存储可执行代码。
- 栈空间为前几篇文章中提到的调用栈,用于存储执行上下文。栈空间是连续的。
- 堆空间用于存储引用类型的值。堆存储没什么规律,只会用一块足够大的空间来存储变量。
上代码:
function testFunc() {
let a = 'cola'
let b = a
let c = { face: 'no' }
let d = c
}
testFunc()
(1)当a和b被赋值后,调用栈状态为:
在testFunc函数执行上下文中,a和b都被赋值为"cola",原始类型的数据被保存在执行上下文中,执行上下文被压入栈,可以认为原始类型数据保存在栈中。
(2) 当c被赋值时,由于js引擎判断右边的值为引用类型,此时js引擎会把该对象分配到堆空间中,执行上下文中存储的为该对象在堆中的地址。此时调用栈状态为:
从上图可以看到,引用类型的值存放在堆空间中,js访问数据时,通过引用对象的地址进行访问。
js引擎通过栈维护程序执行期间上下文的状态,通过在栈中存放原始数据类型数据,堆空间中存放引用类型的数据,维持栈空间不会太大。如果所有数据都存储在栈中,栈空间太大会影响上下文切换的效率,进而影响整个程序执行的效率。
(3) 当d被赋值时,由于js引擎判断右边的值为引用类型,也会为d分配该对象的引用地址。
所以
d=c的操作,是把c的引用地址赋值给d,如果修改该对象,c和d都会受到影响。
因此,原始类型的赋值,是复制值;引用类型的赋值,是复制引用地址。
(4) 当testFunc函数执行结束,js引擎离开当前的执行上下文,下移到上个执行上下文的地址(本程序中为全局执行上下文)。testFunc函数执行上下文空间会被全部回收。
三、闭包的存储
在谈闭包时,有这段代码:
function func() {
let myName = "李三岁"
let num1 = 1
let num2 = 2
let innerFunc = {
getName:function(){
return myName
},
setName:function(newName){
console.log(num1)
myName = newName
}
}
return innerFunc
}
var tem = func()
tem.setName("李逍遥")
console.log(tem.getName())
在程序执行过程中,func函数执行上下文销毁时,变量myName和num1并没有被销毁,而是保存在内存中。在内存空间中,闭包的形成和存储为:
- js引擎执行func函数时,编译创建其空的函数执行上下文。
- 编译过程中,遇到getName内部函数,发现内部函数引用了外部func函数的myName变量。由于内部函数引用外部函数中的变量,js引擎判断这是一个闭包。于是在堆空间中创建一个"closure(func)"的对象,myName作为其属性存储。
- 接着遇到setName函数,发现该内部函数还引用外部函数中的num1,js引擎把num1也添加到"closure(func)"对象中。
当执行到
return innerFunc时func的调用栈状态为:执行到该函数时,闭包就已经产生了。虽然func函数执行结束,其执行上下文被销毁。但是返回的getName和setName方法都有"closure(func)"对象的引用,所以当调用tem.setName或getName方法时,创建的函数执行上下文中就包含"closure(func)"对象。
文章参考:time.geekbang.org/column/intr… developer.mozilla.org/zh-CN/docs/…