javascript 内存模型
我对于 JavaScript 的内存模型一直都比较困惑,很想了解在操作变量的时候,JS 是如何工作的。如果你和我有同样的困惑,希望这篇文章能给你一些启发。
译文,喜欢原文的可以直接拉到底部
当我们声明变量、初始化变量、更改变量值的时候,到底会发生什么?JavaScript 是如何实现这些基本的功能?最重要的是,我们如何才能理解这些基础知识?
本文将覆盖以下 4 个方面:
- JavaScript 原始数据类型的变量声明和赋值
- JavaScript 内存模型:调用栈和堆
- JavaScript 引用类型的变量声明和赋值
- Let VS. const
JavaScript 原始数据类型的变量声明和赋值
从一个简单的栗子开始。首先我们声明一个叫myNumber
的变量,赋值为 23。
let myNumber = 23
执行这段代码的时候,JavaScript 会...
- 为你的变量(myNumber)创建一个唯一标识符。
- 为变量分配一个内存地址(运行时)。
- 在分配的地址中存储一个值(23)。
通常我们会说:“myNumber 等于 23”,但从技术上讲,myNumber
等于一个内存地址,那儿保存着一个大小为 23 的值。理解这段话十分关键。
如果我们创建一个 newVar
的新变量,然后把 myNumber
赋值给它:
let newVar = myNumber
因为 myNumber
实际上等于“0012CCGWH80”,那么newVar
也等于“0012CCGWH80”,这个内存地址保存的值为 23。最终实现了“newVal 等于 23”的效果。
如果我们这样做又会发生什么呢?
myNumber = myNumber + 1
显然,myNumber
的值为 24,那么对于指向相同内存地址的newVar
,它是否也等于 24?
答案当然是否定的!因为 JavaScript 的基本数据类型是不可变的,myNumber + 1
的结果是 24,JavaScript 会分配一个新的内存地址来存储这个值,然后将myNumber
指向这个新地址。
图3
再举一个例子:
let myString = 'abc'
myString = myString + 'd'
JS 新手可能认为,字符串abc
已经存在于内存里,所以字母d
只是追加到它的后面。从技术上讲,这是错误的。由于原始数据类型的不变性,当abc
与d
结合时,JS 会分配一个新的内存地址来保存这个值(abcd
),接着myString
指向新的地址。
图4
JavaScript 的内存模型:调用栈和堆
JS 的内存模型可以简单的理解为两个不同的区域:调用栈和堆。
图5
栈用来保存原始数据以及函数调用,可以粗略的用下图表示。
图6
上图中,我抽象的在调用栈中显示每个变量的值。但请记住,变量实际指向的是内存地址,那里保存着对应的值。这是理解let vs. cont
的关键。
关于堆内存。
堆保存着所有非原始类型的数据。它和栈最大的区别是,堆可以保存无序、能够动态增删的数据——对于对象和数组来说,这是完美的存储空间。
JavaScript 非原始数据类型的变量声明和赋值
还是从一个简单的栗子开始。下面,我们声明一个叫myArray
的变量,并初始化一个空数组。
let myArray = []
当 JS 引擎执行上面的代码,内存会发生如下变化:
- 为变量(myArray)创建一个唯一标识符。
- 在栈中给变量分配一个地址a(运行时)。
- 在堆中分配一个地址b,用来存储值 [](运行时)。
- 地址a所存储的值为地址b
图7
图8
现在,我们可以对数组做任何操作了。
myArray.push('first')
myArray.push('second')
myArray.push('third')
myArray.pop()
图9
Let vs. const
我们应该优先使用const
而不是let
,除非变量会被改变。
我们必须清楚的知道——“改变”到底是什么意思。
值发生了变化,这是对“改变”的一种错误理解。一些 JS 程序员会写下这样的代码:
let sum = 0
sum = 1 + 2
let numbers = []
numbers.push(1)
numbers.push(2)
这段代码正确的使用let
声明变量sum
,因为值被改变了。然而却错误的使用let
来声明变量numbers
,因为他们认为给数组 push 一些数据后,数组的值被改变了。
“改变”的正确解释是——内存地址变了。let
允许你改变内存地址,const
则不允许。
const importantId = 489
importantId = 100 // TypeError: Assignment to constant variable
一起看看这到底发生了什么。
当声明importantId
时,JS 引擎为其分配一个内存地址,并存储一个大小为 489 的值。切记,变量importantId
等于这个内存地址。
图10
当把 100 赋值给importantId
时,因为 100 是原始类型,此时会分配一个用来存储 100 的内存地址。然后 JS 尝试将新的内存地址赋值给importantId
,此时就会发生错误。这是我们想要的结果,因为我们不想改变一个非常重要的 ID。
图11
对于新手来说,由于不清楚“改变”的真是含义,在使用 const 声明变量可能会有些困惑,所以他们默认使用 let 来避免麻烦。
然而,这并不是推荐的做法。Google 在他们的 JavaScript 风格指南中写道:“使用 const 或 let 声明所有变量。除非变量会被重新赋值,否则优先使用 const。一定不要使用 var”。
他们没有明确说明为什么要这样做,但我认为这样做有以下好处:
- 减少未来的bug。
- 使用 const 声明变量时必须初始化,这会强迫程序员更加小心的处理变量作用域,带来更好的内存管理和性能。
- 更好的可读性,哪些变量是不变的,哪些会被重新赋值,一目了然。
bye...
原文链接
javascript 内存模型的更多相关文章
- (转)JavaScript内存模型
JavaScript对象内存模型 转自:http://blog.csdn.net/u010425776/article/details/53617292 推荐-JavaScript作用域链内存模型: ...
- JavaScript 是如何工作的:JavaScript 的内存模型
摘要: 从内存角度理解 let 和 const 的意义. 原文:JavaScript 是如何工作的:JavaScript 的内存模型 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这 ...
- JavaScript学习系列之内存模型篇
一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 如果真的想学好一门语言,那么一定要了解它内存模型,本篇文章就带你走进JavaScript的内存模型,由于本人才疏学浅,若有什么表述有误的地方, ...
- JavaScript的内存模型
引言 在我们的前端日常工作中,无时无刻不在进行着变量的声明和赋值,你是否也曾碰到过变量声明报错或变量被污染的问题,如果你跟笔者一样碰到过,那么我们应该暂时停下来好好思考问题发生的原因以及如何采取相应的 ...
- 浅谈JavaScript原型图与内存模型
js原型详解 1.内存模型: 1.原型是js中非常特殊一个对象,当一个函数(Person)创建之后,会随之就产生一个原型对象 2. 当通过这个函数的构造函数创建了一个具体的对象(p1)之后,在这个具体 ...
- JavaScript闭包模型
JavaScript闭包模型 ----- [原创翻译]2016-09-01 09:32:22 < 一> 闭包并不神秘 本文利用JavaScript代码来阐述闭包,目的是为了使普通 ...
- JavaScript事件模型及事件代理
事件模型 JavaScript事件使得网页具备互动和交互性,我们应该对其深入了解以便开发工作,在各式各样的浏览器中,JavaScript事件模型主要分为3种:原始事件模型.DOM2事件模型.IE事件模 ...
- JavaScript 内存机制
简介 每种编程语言都有它的内存管理机制,比如简单的C有低级的内存管理基元,像malloc(),free().同样我们在学习JavaScript的时候,很有必要了解JavaScript的内存管理机制. ...
- [书籍翻译] 《JavaScript并发编程》 第二章 JavaScript运行模型
本文是我翻译<JavaScript Concurrency>书籍的第二章 JavaScript运行模型,该书主要以Promises.Generator.Web workers等技术来讲解J ...
随机推荐
- 制造业期刊-ZT
小虫一名英国博后,前阵发书,认识了很多机械制造领域的伙伴.得知我录用了多篇顶刊后,很多人私聊我求经验. 哎,哪里那么容易.回想过去5年,制造领域的期刊基本都被拒过一圈.当年自己投稿时就发现,制造顶刊的 ...
- body标签
标签(空格分隔): body标签 body标签: 想要在网页上展示出来的内容一定要放在body标签中. 把我们之前那一段HTML代码贴过来,保存到一个HTML格式的文件中. <!DOCTYPE ...
- ppt复制文本框文字到word的方法
打开ppt按Alt+F11,插入--模块, 选中“工具”--“引用”--MicroSoft Word .. 复制代码: Sub Main() On Error Resume Next Dim tem ...
- 【Python深入】Python中继承object和不继承object的区别
python中定义class的时候,有object和没有object的不同?例如: class Solution(object): class Solution(): 这俩的区别在于—————— 在p ...
- PHP开发——常量
概念 l 常量就是值永远不变的量.如:圆周率.身份证号码等. l 所谓常量值永远不变的量,是指在一次完整的HTTP请求过程中. l 常量在程序运行过程中,不能修改.也不能删除. l 常量比变量 ...
- leveldb 学习记录(一) skiplist
leveldb LevelDb是一个持久化存储的KV系统,并非完全将数据放置于内存中,部分数据也会存储到磁盘上. 想了解这个由谷歌大神编写的经典项目. 可以从数据结构以及数据结构的处理下手,也可以从示 ...
- Android app中存储文件的路径
// 获得缓存文件路径,磁盘空间不足或清除缓存时数据会被删掉,一般存放一些临时文件 // /data/data/<application package>/cache目录 File cac ...
- log4net 写日志
转载地址:https://www.cnblogs.com/vichin/p/6022612.html //基本使用 https://www.cnblogs.com/genesis/p/498562 ...
- Linux 6上使用UDEV绑定共享存储
1.硬盘的查看方式 [root@cl6-11gr2-rac1 ~]# ls -ltr /dev/sd* brw-rw----. 1 root disk 8, 48 8月 16 13:34 /dev/s ...
- Alpha 冲刺 (7/10)
队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭鸭鸭鸭鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作 学习MSI.CUDA 试运行软件并调试 ...