学习链接
鉴定一下网络热门面试题:JavaScript哪些变量在堆区哪些在栈区?
数据类型
动态类型
JavaScript,被称为“动态类型”(dynamically typed)的编程语言,意思是虽然编程语言中有不同的数据类型,但是你定义的变量并不会在定义后,被限制为某一数据类型。
JavaScript 是一种弱类型、动态语言。
- 弱类型,意味着不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。
- 动态,意味着可以使用同一个变量保存不同类型的数据。
八种类型
JavaScript 中共有八种基本的数据类型。
- 七种原始(基本)数据类型:
number
用于任何类型的数字:整数或浮点数,在#card=math&code=%C2%B1%282%5E%7B53%7D%20-%201%29&id=TB1fJ) 范围内的整数。
bigint
用于任意长度的整数。string
用于字符串:一个字符串可以包含 0 个或多个字符,所以没有单独的单字符类型。boolean
用于逻辑值:true/false
null
用于未知的值 —— 具有单个值null
的独立类型,表示“空”或“不存在”undefined
用于未定义的值 —— 具有单个值undefined
的独立类型,表示“未分配(未定义)”symbol
用于唯一的标识符。
- 一种复杂(引用)数据类型:
object
用于更复杂的数据结构。
变量存储上的区别
行为类似其他语言中的堆栈内存,但实际上所有变量都在堆上,自行分配。
原始数据类型和引用数据类型在内存中的存储:
- 声明变量时不同的内存地址分配:
- 原始类型的值存储在栈区中,在栈区中存放的是对应的值
- 引用类型对应的值存储在堆区中,在栈区中存放的是指向堆区的地址
- 不同的类型数据导致赋值变量时的不同:
- 原始类型赋值,是生成相同的值,两个变量对应不同的地址
- 引用类型赋值,是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆区中同一个地址
JavaScript 所有变量都在堆区上。
栈区/堆区 ≠ 栈/堆
在多数的现代操作系统当中,一个进程的内存通常分为 5 个区来管理
- 代码区:存储编译好的机器码
- 全局/静态区:内存大小相对来说是固定的,存的一般是在整个进程的生命周期中都不会销毁的数据
- 常量区:与全局区类似,但不允许写
- 栈区:存储函数调用的参数和函数里面的局部变量,CPU 会提供一套操作栈区的指令,故性能好,大小在编译时候就指定好,如果函数调用的层数过多,就会超出栈的大小即 Stack Overflow
- 堆区:提供很大一块的内存,操作系统会提供一套API管理和分配堆区的内存,标记某块被占用,占用多大,通常用双链表来管理堆
- 虚拟机的存在
现代 JavaScript 使用虚拟机,在堆上申请一大块内存,自行管理。
虚拟机为了执行自己的指令,通常会划分一部分内存,当作虚拟机的栈,函数调用层数过多,就会超出栈的大小,但报错不是 Stack Overflow,而是Maximum call stack size exceeded
系统报 Stack Overflow 时会直接崩掉,JS 爆栈时本身还有自己能够输出错误的能力 - JS 中的函数闭包
函数执行时压栈,执行完之后出栈,按理说里面的变量应该就被销毁了,但 JS 中的闭包则证明不是在系统的栈区中存储
数据类型检测
typeof
可以通过 typeof
运算符查看存储在变量中的数据类型(以上八种)。
- 通常用作
typeof x
,但typeof(x)
也可行。 typeof
是一个操作符,不是一个函数。这里的括号不是typeof
的一部分。它是数学运算分组的括号。- ```javascript typeof 1 // ‘number’ typeof 1n // ‘bigint’ typeof “1” // ‘string’ typeof true // ‘boolean’ typeof Symbol() // ‘symbol’ typeof undefined // ‘undefined’
typeof null // ‘object’
typeof {} // ‘object’ typeof function() {} // ‘function’
`typeof` 运算符返回值的类型,但有两个例外:
```javascript
typeof null == "object" // JavaScript 编程语言的设计错误
typeof function(){} == "function" // 函数被特殊对待
typeof null
的结果为"object"
。这是官方承认的typeof
的错误,这个问题来自于 JavaScript 语言的早期阶段,并为了兼容性而保留了下来。null
绝对不是一个object
。null
有自己的类型,它是一个特殊值。typeof
的行为在这里是错误的。typeof alert
的结果是"function"
,在 JavaScript 语言中没有一个特别的 “function” 类型。
函数隶属于object
类型。但是typeof
会对函数区分对待,并返回"function"
。这也是来自于 JavaScript 语言早期的问题。从技术上讲,这种行为是不正确的,但在实际编程中却非常方便。
Object.prototype.toString.call()
可区分 null
类型。
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call(1n) // '[object BigInt]'
Object.prototype.toString.call("1") // '[object String]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(Symbol()) // '[object Symbol]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call(function () {}) // '[object Function]'
与 typeof
的某些比较
typeof new Number() // 'object'
typeof new String() // 'object'
typeof new Boolean() // 'object'
Object.prototype.toString.call(new Number()) // '[object Number]'
Object.prototype.toString.call(new String()) // '[object String]'
Object.prototype.toString.call(new Boolean()) // '[object Boolean]'
数据类型转换
单独一篇。