【JavaScript】数据类型与存储

149 阅读3分钟

数据类型

1. 基本/简单类型

  • Number
  • String
  • Boolean
  • Null
  • Undefined
  • NaN
  • Symbol 唯一值
  • bigint 大整型 数据结尾时加n表示 --用于处理超出最大安全值的数的运算

代码示例

let num = 123  // Number
console.log(num)
console.log(num.toString()) // String

let flag = false // boolean
console.log(flag)

let un = undefined

let nu = null

// 以上都是字面量创建方式 -- 推荐   

// 不推荐使用构造函数创建简单数据类型
let str1 = new String('hello')
let str2 = new Number('hello')

2. 引用/复杂类型

  • { } Object
  • [ ] Array
  • function
  • Date

代码示例

// 1. 声明对象  --属性区分一定要加逗号
let FWB = {
  uname: '傅文斌',
  age: 18,
  gender: '男',
  'school': 'ECUT',
}

// new 方法格式
const o = new Object()
o.uname = 'new方法'
console.log(o)

// new简写格式
const ob = new Object({ uname: 'new方法简写格式' })
console.log(ob)

// 字面量格式
const obj3 = {
  uname: '字面量格式',
}
console.log(obj3)

// 数组同理

存储规则

前置知识:

  1. 语言类型

  • JS这门语言是动态语言,即在使用前不需要定义数据类型,只在运行时决定数据类型

  • 同时,JS支持隐式类型转换,称为弱类型语言

  1. 内存空间:V8引擎在执行代码时,开辟出来的对应空间

  • 代码空间:代码占据的空间大小
  • 栈空间:调用栈
  • 堆空间:存放复杂数据类型数据

JS存储机制

我们先来看个代码示例,仔细看看输出结果会是什么?为什么会如此?

function foo () {
  var a = 1
  var b = a
  a = 2
  console.log(a) // 2
  console.log(b) // 1
}

foo()

function foo1 () {
  var a1 = { name: 'FWB' } // a 是一个对象
  var b1 = a1 
  a1.name = 'BWF' 
  console.log(a1.value) // 输出 'BWF'
  console.log(b1.value) // 输出 'BWF'
}

foo1()

其实造成这个现象的原因其实是js的存储机制在作祟:

  1. 复杂数据类型存储在堆中,字面量和简单类型储存在栈中

  2. 形如a1 = {...}字面量a1只是指向堆中对应的数据类型的形如#001的地址值

  3. 所以b1 = a1只会把引用值赋值,所以它们共享一个地址对应的复杂数据类型

  4. js栈中的字面量和简单类型不会共享,只有副本,因为没有引用,会直接赋值

话不多说,上图

对于简单数据类型,执行上下文中直接存放对应值。变量直接赋值时,生成副本并直接赋值

简单数据类型存储

对于复杂数据类型JS将其存放在堆中,执行上下文中对应字面量仅存储指向该类型的地址值

所以变量直接赋值时,会复制地址值,共享一个地址对应的复杂数据类型。所有的操作也会共享

复杂数据类型存储

小问题:为什么复杂数据类型存放在堆里呢?

其实这个问题很好想,前面提到调用栈设计不能过大,而简单类型的数据就算是再大也不可能会出现内存空间占用过多的问题。

但是复杂类型不同,拿对象这一类型举例,对象中可以嵌套对象、方法、数组、简单数据。只要程序员想,可以储存任意多的数据量,这个时候如果将复杂数据类型再储存到栈中必然导致爆栈。

所以规定将复杂数据类型放在堆中,而堆空间的大小在理想状态下可以无限大,那么复杂数据类型存入其中就是自然而然的事了。

小测验

function foo (person) {
  person.age = 20
  person = {
    name: '张三',
  }
  return person
}

let p1 = {
  name: '李四',
  age: 18,
}
let p2 = foo(p1)

console.log(p1)  // { name: '李四', age: 20 }

console.log(p2)  // { name: '张三' }

其实这个例子很简单,按照上文图解方式画图就好了:

小测验图解

其中难点在于p1传入foo函数后的操作,其实第三行给person重新赋值是相当于给person指向了一个新的堆内存中存放的数据

这下你搞明白了吗?