学习链接

现代 JavaScript 教程

数据类型-存储区别

鉴定一下网络热门面试题:JavaScript哪些变量在堆区哪些在栈区?

数据类型

动态类型

JavaScript,被称为“动态类型”(dynamically typed)的编程语言,意思是虽然编程语言中有不同的数据类型,但是你定义的变量并不会在定义后,被限制为某一数据类型。

JavaScript 是一种弱类型动态语言。

  • 弱类型,意味着不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。
  • 动态,意味着可以使用同一个变量保存不同类型的数据。

八种类型

JavaScript 中共有八种基本的数据类型。

  • 七种原始(基本)数据类型:
    • number 用于任何类型的数字:整数或浮点数,在 数据类型 - 图1#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管理和分配堆区的内存,标记某块被占用,占用多大,通常用双链表来管理堆
  1. 虚拟机的存在
    现代 JavaScript 使用虚拟机,在堆上申请一大块内存,自行管理。
    虚拟机为了执行自己的指令,通常会划分一部分内存,当作虚拟机的栈,函数调用层数过多,就会超出栈的大小,但报错不是 Stack Overflow,而是Maximum call stack size exceeded
    系统报 Stack Overflow 时会直接崩掉,JS 爆栈时本身还有自己能够输出错误的能力
  2. 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’

  1. `typeof` 运算符返回值的类型,但有两个例外:
  2. ```javascript
  3. typeof null == "object" // JavaScript 编程语言的设计错误
  4. typeof function(){} == "function" // 函数被特殊对待
  1. typeof null 的结果为 "object"。这是官方承认的 typeof 的错误,这个问题来自于 JavaScript 语言的早期阶段,并为了兼容性而保留了下来。null 绝对不是一个 objectnull 有自己的类型,它是一个特殊值。typeof 的行为在这里是错误的。
  2. typeof alert 的结果是 "function",在 JavaScript 语言中没有一个特别的 “function” 类型。
    函数隶属于 object 类型。但是 typeof 会对函数区分对待,并返回 "function"。这也是来自于 JavaScript 语言早期的问题。从技术上讲,这种行为是不正确的,但在实际编程中却非常方便。

Object.prototype.toString.call()

可区分 null 类型。

  1. Object.prototype.toString.call(1) // '[object Number]'
  2. Object.prototype.toString.call(1n) // '[object BigInt]'
  3. Object.prototype.toString.call("1") // '[object String]'
  4. Object.prototype.toString.call(true) // '[object Boolean]'
  5. Object.prototype.toString.call(Symbol()) // '[object Symbol]'
  6. Object.prototype.toString.call(undefined) // '[object Undefined]'
  7. Object.prototype.toString.call(null) // '[object Null]'
  8. Object.prototype.toString.call({}) // '[object Object]'
  9. Object.prototype.toString.call(function () {}) // '[object Function]'

typeof 的某些比较

  1. typeof new Number() // 'object'
  2. typeof new String() // 'object'
  3. typeof new Boolean() // 'object'
  4. Object.prototype.toString.call(new Number()) // '[object Number]'
  5. Object.prototype.toString.call(new String()) // '[object String]'
  6. Object.prototype.toString.call(new Boolean()) // '[object Boolean]'

数据类型转换

单独一篇。