数据结构和内存

81 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

数据类型

ES6中定义了8种数据类型,其中包括7种基础数据类型和1种引用类型(Object)

基础数据类型(primitive value)

基础数据类型常常用来表达一些结构简单的数据。

基础类型
Boolean两个值:truefalse
Null一个值:null
Undefined两个值:truefalse
Number所有数字
String所有字符串
Symbol符号类型,不可变
BigInt大数类型

基础数据类型的值是无法改变的,听起来好像和我的平时使用的不一样,我们使用时不是也可以对字符串进行一系列操作去改变它的值,也可以对数字进行加减乘除的操作。那么让我们写个案例验证一下:

const str = 'hello world'
str[0] = 'b'
console.log(str) // 打印出仍然为‘hello world’

除了不可变性,基础数据类型还有一个很特殊的情况就是它们也可以访问方法,比如字符串类型的slice,concat等等,但是基础数据类型又不是一个对象他为什么可以去访问方法呢? 其实是因为在我们访问字符串时,实际上访问的仍然是一个对象。JS中对每个基础数据类型都提供了对应的包装对象,就比如字符串就有String的包装对象,当我们使用字符串变量访问方法时,实际上经历了如下三步:

const _str = new String('hello world')
_str.slice(' ')
_str = null

包装对象在访问方法时生效,在访问结束时清除。

引用数据类型(reference value)

引用数据类型表达的是更为复杂的数据结构,不是单一的字符串或数字,

const student = {
    sex: 'male',
    age: '18',
    name: 'jack',
    ...
}

引用数据类型是可以被改变的,我们可以直接对它其中的属性进行赋值的操作

const student = {
    sex: 'male',
    age: '18',
    name: 'jack',
}

const str1 = student
str1.name = 'Tom'
console.log(student) // {age: "18",name: "Tom",sex: "male"}
console.log(str1) // {age: "18",name: "Tom",sex: "male"}

可以看到我们对str1的修改影响到了student。我们的引用类型在赋值时,只是对引用类型的地址进行了一次拷贝,这种只拷贝地址的操作,我们称为浅拷贝,所以我们在修改str1的地址内容时,当然也修改了student的地址内的值。当两个引用类型比较时,本质上也是它们的地址在比较。

const a = {}
const b = {}
a === b // false

上面的例子中,由于a和b是分开创建的,虽然他们的值是一样的,但是在内存里它们的地址不一样,所以它们的比较结果也会不同。直接比较地址的操作称为浅比较。

内存

对于一个js应用程序来说,它首先是运行在内存中的。虽然在内存中运行,但是我们也可以做一些数据持久化的工作,比如本地缓存。在浏览器中,浏览器提供了localStorage对象帮助我们实现本地缓存。在js中,内存又分为栈内存和堆内存,其实两者本身没有差别,只是使用时存取方式不同。

应用程序使用栈内存时,是从地址高位开始分配内存空间。使用堆内存时,是从地址低位开始分配空间。

数据结构

栈,堆,队列是我们需要了解掌握的三种基础数据结构

栈(stack)

想要理解栈的概念,可以用羽毛球筒来类比,往筒中依次放入球,处于这个筒中最上面的一定是最后放进去并且最先取出来的,处于这个筒最下面的一定是最先放进去并且最后一个取出来的。总结一句话就是先进后出,后进先出。js中数组Array提供了两个方法对应这种存取方式,pushpop

let a = []
a.push(1) // a: [1]
a.push(2,3,4) // a: [1,2,3,4]
a.pop // a: p[1,2,3]

堆(heap)

堆数据结构是一种特殊的树状结构,它和其他的一些树状结构统一为树。为了易于存储于索引,在实践中,我们常使用二叉堆解决问题,二叉堆就是一颗完全的二叉树,我们使用一个数组就可以存储。

二叉堆又分最大堆和最小堆。

最大堆,又称大顶堆,父节点的键值总是大于等于任何一个子节点。
最小堆,又称小顶堆,父节点的键值总是小于等于任何一个子节点。

队列

队列是一种先进先出的数据结构,就像排队一样。这里的先进先出只是最基本的规则,在实际应用时会有许多的限制,比如浏览器的异步队列,需要等同步执行完成才能开始执行,比如队列的第一个任务需要挂起等待其他的任务执行后再执行等等,因此结合实践时,又会有新的条件出现。