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 相关文章推荐
popdiv
Jul 14 Javascript
javascript背景时钟实现方法
Jun 18 Javascript
基于MVC4+EasyUI的Web开发框架形成之旅之界面控件的使用
Dec 16 Javascript
javascript验证手机号和实现星号(*)代替实例
Aug 16 Javascript
Javascript中引用类型传递的知识点小结
Mar 06 Javascript
vue 请求后台数据的实例代码
Jun 22 Javascript
详解html-webpack-plugin用法全解
Jan 22 Javascript
微信小程序实现分享到朋友圈功能
Jul 19 Javascript
Vue文件配置全局变量的实例
Sep 06 Javascript
在Bootstrap开发框架中使用dataTable直接录入表格行数据的方法
Oct 25 Javascript
js实现通过开始结束控制的计时器
Feb 25 Javascript
ES6 class类链式继承,实例化及react super(props)原理详解
Feb 15 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调用方法mssql_fetch_row、mssql_fetch_array、mssql_fetch_assoc和mssql_fetch_objcect读取数据的区别
2012/08/08 PHP
php字符串分割函数用法实例
2015/03/17 PHP
php生成zip文件类实例
2015/04/07 PHP
PHP最常用的正则表达式
2017/02/13 PHP
JS对HTML标签select的获取、添加、删除操作
2013/10/17 Javascript
三种检测iPhone/iPad设备方向的方法
2014/04/23 Javascript
使用jQuery实现Web页面换肤功能的要点解析
2016/05/12 Javascript
JavaScript操作选择对象的简单实例
2016/05/16 Javascript
JS图片放大效果简单实现代码
2016/09/08 Javascript
用jQuery实现可输入多选下拉组合框实例代码
2017/01/18 Javascript
JavaScript 实现 Tab 点击切换实例代码
2017/03/25 Javascript
jQuery.Form实现Ajax上传文件同时设置headers的方法
2017/06/26 jQuery
angular4自定义组件详解
2017/09/28 Javascript
vue路由跳转时判断用户是否登录功能的实现
2017/10/26 Javascript
node.js多个异步过程中判断执行是否完成的解决方案
2017/12/10 Javascript
解决npm管理员身份install时出现权限的问题
2018/03/16 Javascript
vue框架下部署上线后刷新报404问题的解决方案(推荐)
2019/04/03 Javascript
javascript实现小型区块链功能
2019/04/03 Javascript
ajax跨域访问遇到的问题及解决方案
2019/05/23 Javascript
更强大的vue ssr实现预取数据的方式
2019/07/19 Javascript
生成无限制的微信小程序码的示例代码
2019/09/20 Javascript
layui实现根据table数据判断按钮显示情况的方法
2019/09/26 Javascript
vue 解决文本框被键盘遮住的问题
2019/11/06 Javascript
[02:54]DOTA2英雄基础教程 撼地者
2014/01/14 DOTA
[02:04]2014DOTA2国际邀请赛 DK一个时代的落幕
2014/07/21 DOTA
Python 详解基本语法_函数_返回值
2017/01/22 Python
python实现二叉查找树实例代码
2018/02/08 Python
解决python ogr shp字段写入中文乱码的问题
2018/12/31 Python
python3.6编写的单元测试示例
2019/08/17 Python
python七种方法判断字符串是否包含子串
2020/08/18 Python
如何利用Python 进行边缘检测
2020/10/14 Python
HTML5学习笔记之html5与传统html区别
2016/01/06 HTML / CSS
学校门卫工作职责
2013/12/07 职场文书
大众服装店创业计划书范文
2014/01/01 职场文书
《金孔雀轻轻跳》教学反思
2014/04/20 职场文书
Javascript 解构赋值详情
2021/11/17 Javascript