浏览器工作原理和实践——栈空间和堆空间

function foo(){

var a = {name:“极客时间”}

var b = a

a.name = “极客邦”

console.log(a)

console.log(b)

}

foo()

若执行上述这两段代码,你知道它们输出的结果是什么吗?

下面我们就来一个一个分析下。执行第一段代码,打印出来 a 的值是 2,b 的值是 1,这没什么难以理解的。

接着,再执行第二段代码,你会发现,仅仅改变了 a 中 name 的属性值,但是最终 a 和 b 打印出来的值都是{name:“极客邦”}。这就和我们预期的不一致了,因为我们想改变的仅仅是 a 的内容,但 b 的内容也同时被改变了。

要彻底弄清楚这个问题,我们就得先从“JavaScript 是什么类型的语言”讲起。

JavaScript 是什么类型的语言

==================================================================================

每种编程语言都具有内建的数据类型,但它们的数据类型常有不同之处,使用方式也很不一样,比如 C 语言在定义变量之前,就需要确定变量的类型,你可以看下面这段 C 代码:

int main()

{

int a = 1;

char* b = “极客时间”;

bool c = true;

return 0;

}

上述代码声明变量的特点是:在声明变量之前需要先定义变量类型。我们把这种在使用之前就需要确认其变量数据类型的称为静态语言。

**相反地,我们把在运行过程中需要检查数据类型的语言称为动态语言。**比如我们所讲的 JavaScript 就是动态语言,因为在声明变量之前并不需要确认其数据类型。

虽然 C 语言是静态,但是在 C 语言中,我们可以把其他类型数据赋予给一个声明好的变量,如:

c = a

前面代码中,我们把 int 型的变量 a 赋值给了 bool 型的变量 c,这段代码也是可以编译执行的,因为在赋值过程中,C 编译器会把 int 型的变量悄悄转换为 bool 型的变量,我们通常把这种偷偷转换的操作称为隐式类型转换。而支持隐式类型转换的语言称为弱类型语言,不支持隐式类型转换的语言称为强类型语言。在这点上,C 和 JavaScript 都是弱类型语言。

对于各种语言的类型,你可以参考下图:

在这里插入图片描述

JavaScript 的数据类型

===============================================================================

现在我们知道了,JavaScript 是一种弱类型的、动态的语言。那这些特点意味着什么呢?

  • 弱类型,意味着你不需要告诉 JavaScript 引擎这个或那个变量是什么数据类型,JavaScript 引擎在运行代码的时候自己会计算出来。

  • 动态,意味着你可以使用同一个变量保存不同类型的数据。

那么接下来,我们再来看看 JavaScript 的数据类型,你可以看下面这段代码:

var bar

bar = 12

bar = “极客时间”

bar = true

bar = null

bar = {name:“极客时间”}

从上述代码中你可以看出,我们声明了一个 bar 变量,然后可以使用各种类型的数据值赋予给该变量。

在 JavaScript 中,如果你想要查看一个变量到底是什么类型,可以使用“typeof”运算符。具体使用方式如下所示:

var bar

console.log(typeof bar) //undefined

bar = 12

console.log(typeof bar) //number

bar = “极客时间”

console.log(typeof bar)//string

bar = true

console.log(typeof bar) //boolean

bar = null

console.log(typeof bar) //object

bar = {name:“极客时间”}

console.log(typeof bar) //object

执行这段代码,你可以看到打印出来了不同的数据类型,有 undefined、number、boolean、object 等。那么接下来我们就来谈谈 JavaScript 到底有多少种数据类型。

其实 JavaScript 中的数据类型一种有 8 种,它们分别是:

在这里插入图片描述

了解这些类型之后,还有三点需要你注意一下。

第一点,使用 typeof 检测 Null 类型时,返回的是 Object。这是当初 JavaScript 语言的一个 Bug,一直保留至今,之所以一直没修改过来,主要是为了兼容老的代码。

第二点,Object 类型比较特殊,它是由上述 7 种类型组成的一个包含了 key-value 对的数据类型。如下所示:

let myObj = {

name:‘极客时间’,

update:function(){…}

}

从中你可以看出来,Object 是由 key-value 组成的,其中的 vaule 可以是任何类型,包括函数,这也就意味着你可以通过 Object 来存储函数,Object 中的函数又称为方法,比如上述代码中的 update 方法。

第三点,我们把前面的 7 种数据类型称为原始类型,把最后一个对象类型称为引用类型,之所以把它们区分为两种不同的类型,是因为它们在内存中存放的位置不一样。到底怎么个不一样法呢?接下来,我们就来讲解一下 JavaScript 的原始类型和引用类型到底是怎么储存的。

内存空间

===================================================================

要理解 JavaScript 在运行过程中数据是如何存储的,你就得先搞清楚其存储空间的种类。下面是我画的 JavaScript 的内存模型,你可以参考下:

在这里插入图片描述

从图中可以看出, 在 JavaScript 的执行过程中, 主要有三种类型内存空间,分别是代码空间、栈空间和堆空间

其中的代码空间主要是存储可执行代码的,这个我们后面再做介绍,今天主要来说说栈空间和堆空间。

栈空间和堆空间

======================================================================

这里的栈空间就是我们之前反复提及的调用栈,是用来存储执行上下文的。为了搞清楚栈空间是如何存储数据的,我们还是先看下面这段代码:

function foo(){

var a = “极客时间”

var b = a

var c = {name:“极客时间”}

var d = c

}

foo()

前面文章我们已经讲解过了,当执行一段代码时,需要先编译,并创建执行上下文,然后再按照顺序执行代码。那么下面我们来看看,当执行到第 3 行代码时,其调用栈的状态,你可以参考下面这张调用栈状态图:

在这里插入图片描述

从图中可以看出来,当执行到第 3 行时,变量 a 和变量 b 的值都被保存在执行上下文中,而执行上下文又被压入到栈中,所以你也可以认为变量 a 和变量 b 的值都是存放在栈中的。

接下来继续执行第 4 行代码,由于 JavaScript 引擎判断右边的值是一个引用类型,这时候处理的情况就不一样了,JavaScript 引擎并不是直接将该对象存放到变量环境中,而是将它分配到堆空间里面,分配后该对象会有一个在“堆”中的地址,然后再将该数据的地址写进 c 的变量值,最终分配好内存的示意图如下所示:

在这里插入图片描述

从上图你可以清晰地观察到,对象类型是存放在堆空间的,在栈空间中只是保留了对象的引用地址,当 JavaScript 需要访问该数据的时候,是通过栈中的引用地址来访问的,相当于多了一道转手流程。

其实前端开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

这里再分享一个复习的路线:(以下体系的复习资料是我从各路大佬收集整理好的)

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

《前端开发四大模块核心知识笔记》

最后,说个题外话,我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。

  • 20
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值