javascript 内存模型实例详解


Posted in Javascript onApril 18, 2020

本文实例讲述了javascript 内存模型。分享给大家供大家参考,具体如下:

我对于 JavaScript 的内存模型一直都比较困惑,很想了解在操作变量的时候,JS 是如何工作的。如果你和我有同样的困惑,希望这篇文章能给你一些启发。

译文,喜欢原文的可以直接拉到底部

当我们声明变量、初始化变量、更改变量值的时候,到底会发生什么?JavaScript 是如何实现这些基本的功能?最重要的是,我们如何才能理解这些基础知识?

本文将覆盖以下 4 个方面:

  1. JavaScript 原始数据类型的变量声明和赋值
  2. JavaScript 内存模型:调用栈和堆
  3. JavaScript 引用类型的变量声明和赋值
  4. Let VS. const

JavaScript 原始数据类型的变量声明和赋值

从一个简单的栗子开始。首先我们声明一个叫myNumber的变量,赋值为 23。

let myNumber = 23

执行这段代码的时候,JavaScript 会...

  1. 为你的变量(myNumber)创建一个唯一标识符。
  2. 为变量分配一个内存地址(运行时)。
  3. 在分配的地址中存储一个值(23)。

javascript 内存模型实例详解

通常我们会说:“myNumber 等于 23”,但从技术上讲,myNumber 等于一个内存地址,那儿保存着一个大小为 23 的值。理解这段话十分关键。

如果我们创建一个 newVar 的新变量,然后把 myNumber 赋值给它:

let newVar = myNumber

因为 myNumber 实际上等于“0012CCGWH80”,那么newVar也等于“0012CCGWH80”,这个内存地址保存的值为 23。最终实现了“newVal 等于 23”的效果。

javascript 内存模型实例详解

如果我们这样做又会发生什么呢?

myNumber = myNumber + 1

显然,myNumber的值为 24,那么对于指向相同内存地址的newVar,它是否也等于 24?

答案当然是否定的!因为 JavaScript 的基本数据类型是不可变的,myNumber + 1的结果是 24,JavaScript 会分配一个新的内存地址来存储这个值,然后将myNumber指向这个新地址。
javascript 内存模型实例详解
图3

再举一个例子:

let myString = 'abc'
myString = myString + 'd'

JS 新手可能认为,字符串abc已经存在于内存里,所以字母d只是追加到它的后面。从技术上讲,这是错误的。由于原始数据类型的不变性,当abcd结合时,JS 会分配一个新的内存地址来保存这个值(abcd),接着myString指向新的地址。
javascript 内存模型实例详解
图4

JavaScript 的内存模型:调用栈和堆

JS 的内存模型可以简单的理解为两个不同的区域:调用栈和堆。
javascript 内存模型实例详解
图5

栈用来保存原始数据以及函数调用,可以粗略的用下图表示。
javascript 内存模型实例详解
图6

上图中,我抽象的在调用栈中显示每个变量的值。但请记住,变量实际指向的是内存地址,那里保存着对应的值。这是理解let vs. cont的关键。

关于堆内存。

堆保存着所有非原始类型的数据。它和栈最大的区别是,堆可以保存无序、能够动态增删的数据——对于对象和数组来说,这是完美的存储空间。

JavaScript 非原始数据类型的变量声明和赋值

还是从一个简单的栗子开始。下面,我们声明一个叫myArray的变量,并初始化一个空数组。

let myArray = []

当 JS 引擎执行上面的代码,内存会发生如下变化:

  1. 为变量(myArray)创建一个唯一标识符。
  2. 在栈中给变量分配一个地址a(运行时)。
  3. 在堆中分配一个地址b,用来存储值 [](运行时)。
  4. 地址a所存储的值为地址b
    javascript 内存模型实例详解
    图7

javascript 内存模型实例详解
图8

现在,我们可以对数组做任何操作了。

myArray.push('first')
myArray.push('second')
myArray.push('third')
myArray.pop()

javascript 内存模型实例详解
图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等于这个内存地址。
javascript 内存模型实例详解
图10

当把 100 赋值给importantId时,因为 100 是原始类型,此时会分配一个用来存储 100 的内存地址。然后 JS 尝试将新的内存地址赋值给importantId,此时就会发生错误。这是我们想要的结果,因为我们不想改变一个非常重要的 ID。
javascript 内存模型实例详解
图11

对于新手来说,由于不清楚“改变”的真是含义,在使用 const 声明变量可能会有些困惑,所以他们默认使用 let 来避免麻烦。

然而,这并不是推荐的做法。Google 在他们的 JavaScript 风格指南中写道:“使用 const 或 let 声明所有变量。除非变量会被重新赋值,否则优先使用 const。一定不要使用 var”。

他们没有明确说明为什么要这样做,但我认为这样做有以下好处:

  1. 减少未来的bug。
  2. 使用 const 声明变量时必须初始化,这会强迫程序员更加小心的处理变量作用域,带来更好的内存管理和性能。
  3. 更好的可读性,哪些变量是不变的,哪些会被重新赋值,一目了然。

bye...

原文链接

  • JavaScript's Memory Model
  • JavaScript 内存机制

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
eval与window.eval的差别分析
Mar 17 Javascript
javascript表单验证 - Parsley.js使用和配置
Jan 25 Javascript
javascript简单性能问题及学习笔记
Feb 04 Javascript
JavaScript设计模式之策略模式实例
Oct 10 Javascript
jQuery层级选择器用法分析
Feb 10 Javascript
php常见的页面跳转方法汇总
Apr 15 Javascript
概述如何实现一个简单的浏览器端js模块加载器
Dec 07 Javascript
vue自定义底部导航栏Tabbar的实现代码
Sep 03 Javascript
vue二级菜单导航点击选中事件的方法
Sep 12 Javascript
浅谈Vue.js 关于页面加载完成后执行一个方法的问题
Apr 01 Javascript
vue router 组件的高级应用实例代码
Apr 08 Javascript
解析原来浏览器原生支持JS Base64编码解码
Aug 12 Javascript
javascript-hashchange事件和历史状态管理实例分析
Apr 18 #Javascript
javascript使用Blob对象实现的下载文件操作示例
Apr 18 #Javascript
原生js实现的观察者和订阅者模式简单示例
Apr 18 #Javascript
es6函数name属性功能与用法实例分析
Apr 18 #Javascript
es6数组includes()用法实例分析
Apr 18 #Javascript
es6数组的flat(),flatMap()函数用法实例分析
Apr 18 #Javascript
es6函数中的作用域实例分析
Apr 18 #Javascript
You might like
php去除HTML标签实例
2013/11/06 PHP
phpmailer在服务器上不能正常发送邮件的解决办法
2014/07/08 PHP
PHP提示Cannot modify header information - headers already sent by解决方法
2014/09/22 PHP
PHP memcache在微信公众平台的应用方法示例
2017/09/13 PHP
js 页面传参数时 参数值含特殊字符的问题
2009/12/13 Javascript
利用JS重写Cognos右键菜单的实现代码
2010/04/11 Javascript
JQuery 自定义CircleAnimation,Animate方法学习笔记
2011/07/10 Javascript
JavaScript控制Session操作方法
2013/01/17 Javascript
同域jQuery(跨)iframe操作DOM(示例代码)
2013/12/13 Javascript
jquery ajax 局部无刷新更新数据的实现案例
2014/02/08 Javascript
node.js中的fs.utimesSync方法使用说明
2014/12/15 Javascript
JavaScript将数字转换成大写中文的方法
2015/03/23 Javascript
js文本框走动跑马灯效果代码分享
2015/08/25 Javascript
jquery ajaxfileupload异步上传插件使用详解
2017/02/08 Javascript
angular指令笔记ng-options的使用方法
2017/09/18 Javascript
解决layui前端框架 form表单,table表等内置控件不显示的问题
2018/08/19 Javascript
vue项目部署到Apache服务器中遇到的问题解决
2018/08/24 Javascript
Vue唯一可以更改vuex实例中state数据状态的属性对象Mutation的讲解
2019/01/18 Javascript
nodejs对项目下所有空文件夹创建gitkeep的方法
2019/08/02 NodeJs
uni-app如何实现增量更新功能
2020/01/03 Javascript
JavaScript使用canvas绘制随机验证码
2020/02/17 Javascript
如何使用JavaScript检测空闲的浏览器选项卡
2020/05/28 Javascript
vant 自定义 van-dropdown-item的用法
2020/08/05 Javascript
vue 微信分享回调iOS和安卓回调出现错误的解决
2020/09/07 Javascript
Python的消息队列包SnakeMQ使用初探
2016/06/29 Python
python编程测试电脑开启最大线程数实例代码
2018/02/09 Python
Python实现的简单排列组合算法示例
2018/07/04 Python
django model通过字典更新数据实例
2020/04/01 Python
使用phonegap播放音频的实现方法
2017/03/31 HTML / CSS
美国在线咖啡、茶和餐厅供应商:LollicupStore
2018/05/04 全球购物
中国双语服务优势的在线购票及活动平台:247tickets
2018/10/26 全球购物
求职者应聘的自我评价
2013/10/16 职场文书
毕业自荐信
2013/12/16 职场文书
优秀的自荐信要注意哪些
2014/01/03 职场文书
运动会跳远广播稿
2014/02/04 职场文书
详解RedisTemplate下Redis分布式锁引发的系列问题
2021/04/27 Redis