js var是什么类型_JS变量的执行环境和生命周期

14afa78d6b08d5cdefe82ceae639e550.png

温故而知新,这些JS基础知识你都知道吗?

今天和大家分享的是 JavaScript 中有关变量的知识,希望这篇文章能让你对JS中的变量有新的认识.

目录:

  • 变量的执行环境(执行上下文)
  • 执行上下文的生命周期
  • 创建变量对象
  • 变量的数据存储
  • 变量的内存空间
  • 变量的垃圾回收
  • let/const/var的区别

执行环境(执行上下文)

javascript的运行环境主要包括以下三种:

  1. 全局环境:代码运行起来后会首先进入全局环境.
  2. 函数环境:当函数被调用时,会进入当前函数中执行代码.
  3. eval环境:不建议使用,这里不做介绍.

js运行环境也叫做执行上下文,因此在一个JavaScript程序中,必定会出现多个执行上下文.

JS引擎会以栈(遵循后进先出的数据存储方式)的方式来处理执行上下文,也就是我们通常所说的函数调用栈。栈底永远是全局上下文,栈顶则是当前正在执行的上下文. 处于栈顶的执行上下文执行完毕后,会自动出栈.

看个例子可能会更形象些:

function declare() {var a = 1;function update() {a = 2;}update();
}
declare();

下面是上方用例中的执行上下文对应的进出栈流程示意图:

51d1540121fd8917a0042bc2e4a56f7d.png

假如上方的 declare 函数处于全局环境中,那么代码运行时会经历以下几步:

  1. 首先第一步就是全局上下文入栈.
  2. 全局上下文入栈后,遇到的第一个可执行代码就是 declare() 函数的调用,此函数一旦调用,就会创建自己的执行上下文,此时declare EC入栈.
  3. 在新开辟的declare EC执行上下文中,执行内部的可执行代码,直到遇到 update() 函数调用时,又会创建一个新的执行上下文,此时update EC入栈.
  4. 当update EC中的可执行代码执行完毕之后,发现不再有其他执行上下文生成的情况,此上下文会自动从栈中弹出.
  5. update EC执行上下文弹出后,会继续执行 declare EC执行上下文中的可执行代码,直到顺利执行完毕,且没有遇到其他执行上下文,则自动从栈中弹出.
  6. 最后执行栈中只剩下全局上下文,若浏览器不关闭,全局上下文会一直存在,直到浏览器窗口关闭,全局上下文才会最终出栈.

执行上下文的生命周期

说完执行上下文的入栈、出栈情况,下面说说执行上下文的生命周期.

当一个函数调用时,一个新的执行上下文就会被创建,一个执行上下文的生命周期可分为两个阶段:

  1. 创建阶段 --- 此阶段执行上下文会分别创建变量对象、确认作用域链、以及确定this指向.
  2. 执行阶段 --- 执行代码,这个时候会完成变量赋值、函数引用、以及执行其他可执行代码等工作.

画个图看起来会更直观:

6b2a64ede033a925bd681a8f312a22e8.png

这篇文章接下来主要介绍变量的生命周期,所以我们主要针对执行上下文中 创建变量对象、内存空间、变量赋值阶段 展开讲解。

创建变量对象

JS中声明的所有变量都保存在变量对象中, 变量对象的创建依次经历以下步骤:

1. 首先获得函数的参数变量及其值.

2. 依次获取当前上下文中所有的函数声明. 在变量对象中会以函数名建立一个属性,属性值指向该函数所在的内存地址.

3. 依次获取当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性。 如果是var声明,则属性值会初始化为undefined.

变量的数据存储

变量对象创建完成后,接下来就是对数据的存储,基础数据类型往往会保存在栈内存中(特殊情况除外),而引用数据类型的值是保存在堆内存中的对象,在js语言中,不允许直接访问堆内存空间中的数据. 当我们操作对象时实际上是在操作对象的引用,而不是实际的对象. 因此,引用数据类型都是按引用访问的,这里的引用可以理解为保存在变量对象中的一个地址,该地址与堆内存中的对象相关联.

下边通过一个列子,来看下变量对象的存储:

function fun(){var a = 1;var b = 'hello world';var c = {x: 100};var d = {y: [1,2]};
}

当fun()函数调用时,会创建一个执行上下文,在当前上下文中创建变量对象,变量对象存放格式如下:

d759e4a49df44cc2e498eb648467abb4.png

如果是基本数据类型,在栈中存储数据本身; 如果是引用数据类型,在栈中存储的是堆中对象的引用;

变量的内存空间

说完变量对象的创建与存储之后,接下来再说说变量的内存空间的使用过程. 内存空间的使用同样也具有自己的生命周期,包含:

  1. 分配内存阶段
  2. 使用分配到的内存
  3. 不需要时释放内存

再来看个例子:

var a = 20; // 分配内存
console.log(a + 1); // 使用内存
a = null; // 释放内存

上边的三行代码分别对应着分配内存、使用分配到的内存、以及释放内存三个过程。其中,分配和使用应该很好理解,最终要的是释放的过程,涉及到垃圾回收机制的实现原理。

变量的垃圾回收

Javascript中具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。所以,在日常开发中,开发人员很少再关心内存使用的问题.

垃圾回收机制的原理就是:找出那些不再继续使用的变量,垃圾收集器会按照固定的时间间隔,周期性的释放其占用的内存.

最常用的垃圾收集方式是标记清除算法. 主要依靠 "引用" 的概念,当一块内存空间中的数据能够被访问时,垃圾回收器就会认为 "该数据能够被获得",不能够被获得的数据,就会被打上标记,并回收内存空间,这种方式叫做 标记 --- 清除算法.

注: 在局部作用域中,当函数执行完毕后,垃圾收集器会很容易做出判断并做局部变量做回收操作。但在全局作用域中,变量什么时候需要自动释放内存空间则很难判断,所以,我们在做开发时,应尽量避免全局变量的使用。如果使用了全局变量,建议通过 a = null 这样的方式释放引用,以确保能够及时的回收内存空间.

介绍完了执行环境、变量对象的创建、内存、垃圾回收后。我们再来看一个最常见的问题:let/const/var 关键字声明变量的方式,来熟悉下变量的生命周期以及它们之间存在的区别.

----------------- 我是分割线 ----------------

let/const/var的区别

关于var、let、const的各自特性,想必大家都已经很清楚了,这里简单罗列:

2618b54c7c8da977fb8fd3e388162c09.png

使用以上三个关键词定义的变量,之所以会有不同的使用特性,这跟他们的生命周期有直接的关系。 JS引擎下,变量的生命周期包含四个阶段:

  • - 声明阶段
  • - 初始化阶段
  • - 赋值阶段
  • - 释放阶段

下边来分别分析下它们生命周期中存在的区别:

  1. var 声明变量的生命周期

0c643ea563bbbdf5824549f2e90557f3.png

当使用var声明一个变量时:

// 第一个特性
function fun() {// 变量提升 -- 不存在暂时性死区console.log(a); // undefinedvar a = 1;
}// 第二个特性 --- 不支持块级作用域
console.log(a); // undefined
{var a = 1;
}
console.log(a); // 1

以函数作用域为例,在函数作用域中遇到 var a = 1 这段代码,编译器会首先查找当前作用域下是否已经声明过该变量,若已经存在,则直接忽略这次声明;若不存在,则在当前作用域中声明一个名字为 a 的变量,并进行初始化操作(变量 a 的值初始化为 undefined),这也是为什么使用 var 可以进行重复声明的原因. 这里的声明和初始化均被提升至作用域的最顶端.

当函数调用执行时,JS引擎会在当前的函数作用域为该变量进行赋值操作,在 var a = 1 这条语句之前的任何位置访问变量 a,它的值将会是 undefined. 因为初始化操作也被进行了提升.

当代码执行至 var a = 1 时,引擎也会从当前作用域下查找是否存在变量 a, 若存在,直接进行赋值;若不存在会强制在当前作用域声明一个变量 a, 并进行赋值操作.

注: 不要随意在代码块中使用var声明变量,因为它会直接挂到全局变量window对象上,污染全局环境,且不能及时的进行垃圾回收释放内存.

----------------- 我是分割线 ----------------

2. let 声明变量的生命周期

cbbc2c26f10835ee78ce27a3e3a59eb0.png

使用let进行变量声明:

// 第一个特性 --- 块级作用域
{let a = 1;
}
console.log(a); // a is not defined// 第二个特性 --- 暂时性死区
console.log(a); // a is not defined
let a = 1;// 第三个特性 --- 不可重复声明
let a;
let a = 1; // Uncaught SyntaxError: Identifier 'a' has already been declared

let 的生命周期与 var 的主要区别在于,声明和初始化阶段没有同时进行提升,只有声明阶段被提升至当前作用域的最上方,所以在声明和初始化阶段之间就出现了暂时性死区现象,此阶段不可访问该变量,否则会抛出异常.

let 不可重复声明,遇到let声明的变量时,编译器同样会先查找当前作用域下是否已经声明过该变量,若已经存在,引擎就会抛出异常(Uncaught SyntaxError: Identifier 'a' has already been declared);若不存在,则在当前作用域中声明一个名字为 a 的变量,并把声明操作提升至作用域最上方.

最重要的是 let 具备了块级作用域,它很好的规避了 var 身上的缺点,在代码块内声明变量,避免污染全局变量.

----------------- 我是分割线 ----------------

3. const 声明变量的生命周期

0cf44b11107e5c38422bec762749cbad.png

使用const声明变量:

// 第一个特性 --- 一旦声明不可修改
const a = 1;
a = 2; // Uncaught TypeError: Assignment to constant variable.// 第二个特性 --- 存在暂时性死区
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization (初始化前无法访问a)
const a = 1;

let 最大的区别是 const 是用来定义常量的,它创建一个值得只读引用,一旦定义不可修改. const 也存在暂时性死区, 如果我们在声明之前访问该变量的值,则会抛出异常(初始化前无法访问该变量),这也很好的证明了,const 的生命周期同样存在声明提升,并且它的初始化和赋值操作必须一起完成.

注: 使用 const 声明变量时,实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。

一个常量不能和它所在作用域内的其他变量或函数拥有相同的名称. 更多const示例描述,可参考:

const​developer.mozilla.org
847dd7fd4f79eff83706c4a7a82b2850.png

不管是var、let、还是const声明的变量,在变量使用完毕后,最终都会随着执行上下文的出栈、浏览器的关闭、或者手动释放内存的环节进行垃圾回收. 由于JS中自动垃圾回收机制的存在,使我们往往在开发时忽略了内存使用的问题,但这个是所有变量都要经历的过程.

以上就是今天的所有分享啦~ 感谢您认真读完,共勉!

参考文献:

《Javascript高级程序设计》

《Javascript核心技术开发解密》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/271528.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络中不能显示全部计算机,win10家庭版局域网中计算机设备无法完全显示的解决方法...

许多用户都喜欢通过局域网来共享一些文件等,打开局域网就可以看到所有共享的计算机,然而有不少win10家庭版用户却发现局域网中只能看到几台计算机设备,无法完全显示,该怎么办呢,现在为大家讲解一下win10家庭版局域网中…

java.lang.NoClassDefFoundError:org/apache/commons/io/Charsets (jsoup配合htmlunit 爬取异步加载的网页遇到的)

最近用jsoup配合htmlunit 爬取异步加载的网页运行代码的时候,报错java.lang.NoClassDefFoundError:org/apache/commons/io/Charsets 报错截图如下 解决措施: 1:common-fileupload 1.3.1的版本依賴的commons-io 2.2,而htmlunit的jar依賴的是common-io 2.4 htmlunit…

echarts 3d地球 背面光线太暗_新技术:多波长光源,同时3D打印多种光敏树脂材料...

如今,光固化3D打印技术已经被广泛的采用,在齿科、首饰、手办等领域,然而如上图一样的常规光固化3D打印机,一次仍然只能打印一种材料。 △FDM技术类型的3D打印机可以通过增加喷头数量来实现多色或者多种材料同时打印,图…

计算机系统基础:计算机性能评价知识笔记

1、计算机性能常用的性能评测方法 1.1 时钟频率 计算机的时钟频率可以反映出机器的运行速度。一般主频越高,速度越快。 1.2 指令执行速度 加法指令执行速度是衡量计算机性能指标的重要指标之一。 1.3 等效指令速度法 随着计算机指令系统发展,种类越来越多…

astc贴图格式是什么意思_c4d配合AEe3d导入c4d模型贴图及插件安装所有流程

所有使用的软件及插件,分享给大家,需要软件或者插件的可以留言我,免费分享给大家,其实也可以网上下载到,就是有的版本和相应需要的插件不兼容和支持,。不少小伙伴用c4d做模型,做好后到底怎么玩&…

前大灯是近光灯还是远光灯_汽修案例:长安福特翼博前大灯间歇自动点亮

点击↑汽修案例关注置顶,获得正时大全一辆行驶里程约2300km的2017年长安福特翼博。客户反映:该车在行驶过程中前大灯间歇出现自动点亮的故障现象。故障现象出现时大灯点亮与否不受大灯开关控制,如图1所示。故障诊断:车辆来店报修时…

commons-httpclient 和 httpclient 区别

commons-httpclient 和 httpclient 区别 项目里的pom中&#xff0c;里面有这么两个包依赖。 <dependency><groupId>commons-httpclient</groupId><artifactId>commons-httpclient</artifactId><version>3.1</version> </depend…

计算机基础:多媒体基础知识笔记

1、媒体的定义 媒体包括两面&#xff1a; 1、物理载体&#xff1a;比如磁盘、光盘、磁带以及相关的播放设备。 2、信息载体&#xff1a;也就是信息的表现形式&#xff0c;比如文字、声音、图像、动画、视频等。CCITT定义的存储媒体和表示媒体。 2、媒体的分类 1、感觉媒体&…

gnuradio上怎么使用python文件_使用Python从PDF文件中提取数据

前言数据是数据科学中任何分析的关键&#xff0c;大多数分析中最常用的数据集类型是存储在逗号分隔值(csv)表中的干净数据。然而&#xff0c;由于可移植文档格式(pdf)文件是最常用的文件格式之一&#xff0c;因此每个数据科学家都应该了解如何从pdf文件中提取数据&#xff0c;并…

Attach Volume 操作(Part I) - 每天5分钟玩转 OpenStack(53)

上一节我们创建了 volume&#xff0c;本节讨论如何将 volume attach 到 Instance&#xff0c;今天是第一部分。 Volume 的最主要用途是作为虚拟硬盘提供给 instance 使用。Volume 是通过 Attach 操作挂载到 instance 上的。本节我们就来详细讨论 Cinder 是如何实现 Attach 的。…

python如何确定拐点_多年股市老鸟买卖操作经验——如何在波段操作确定买入点!经典...

多年股市老鸟买卖操作经验——如何在波段操作确定买入点&#xff01;经典&#xff01;股市谚语&#xff1a;“长线是金,短线是银,波段操作是钻石。”这从一个侧面反映了波段操作的重要性。波段操作就是在股价趋势变化的早期阶段辨识买卖机会&#xff0c;在波动的阶段性底部(谷底…

获取后端接口请求中的参数(@PathVariable,@RequestParam,@RequestBody区别,使用postman请求

获取参数 SpringBoot提供的获取参数注解包括&#xff1a;PathVariable&#xff0c;RequestParam&#xff0c;RequestBody,三者的区别如下表&#xff1a; 一:后端接口什么都不加 postman请求后端接口 二:后端接口加RequestParam POST请求RequestParam&#xff1a; ① 用来处理…

spark shell 删除失效_Spark任务提交源码解析

1. 前言反反复复捣鼓了很久&#xff0c;终于开始学习Spark的源码了&#xff0c;果不其然&#xff0c;那真的很有趣。这里我打算一本正经的胡说八道来讲一下Spark作业的提交过程。基础mac系统基础环境如下&#xff1a;JDK 1.8IDEA 2019.3源码Spark 2.3.3Scala 2.11.8提交脚本# 事…

硬件基础:理解串口通信以及232,485,422常见问题

这里并不对串口的编程作讲解&#xff0c;主要是从应用的角度去讲一讲。因为更多的时候&#xff0c;都是产品做好了&#xff0c;比如触摸屏需要和控制器&#xff0c;PLC通信。理想的情况下&#xff0c;一般只要一上电&#xff0c;不需要太多的操作和配置&#xff0c;就可以通信上…

decimal转为string sql_SQL注入详解|OWASP Top 10安全风险实践(二)

本文为一些列连载文章之一&#xff0c;不定期更新&#xff0c;计划目录如下&#xff1a;OWASP介绍SQL注入命令注入XML外部实体注入XPATH注入反射式、DOM及存储XSS失效的身份认证和会话管理不安全的直接对象引用安全配置错误敏感信息泄露功能级访问控制缺失跨站请求伪造服务端请…

各类排序算法实现(亲测)

排序算法通常分为外部排序和内部排序&#xff0c;通常所说的八类排序属于内部排序&#xff1b; 外部排序在此不说明&#xff0c;主要给出八类排序的简单思想和实现&#xff1a; 1.插入排序 1.1 直接插入排序&#xff1a; 每次将一个新数&#xff0c;插入到已经排列好的有序…

计算机基础:声音的相关知识笔记

1、声音的相关概念 模拟声音信号&#xff1a;声波在时间和幅度上都是连续的模拟信号。 1.1 声音的组成 幅度&#xff1a;声波的振幅。计量单位是分贝&#xff08;dB&#xff09; 频率&#xff1a;声波每秒变化的次数&#xff0c;用Hz表示。人耳能听到的声音信号的频率范围20Hz~…

ansi编码_Java 字符编码

点击上方蓝字关注我们&#xff01;作者介绍王云静&#xff0c;Java 开发工程师&#xff0c;2018 年 7 月加入去哪儿网&#xff0c;目前在目的地 - 呼叫中心。曾获得过 ACM 亚洲区域赛铜牌。-----基本概念字符集字符(Character)是各种文字和符号的总称&#xff0c;包括各国家文字…

外卖和快递行业数据_下周一起,整治全面启动!锁定全市外卖、快递行业!

为加强我市外卖、快递行业电动自行车交通安全管理&#xff0c;降压预防事故&#xff0c;营造良好的通行秩序&#xff0c;下周一起(12月21日)深圳交警将开展电动自行车交通安全月暨外卖、快递行业集中整治行动。⭕圈重点⭕下周一起(12月21日)正式开展外卖、快递行业集中整治行动…

计算机基础:图形、图像相关知识笔记

1、图形、图像的基础知识 图形&#xff1a;由称为矢量的数学对象所定义的直线和曲线等组成。 图像&#xff1a;也称为栅格图像&#xff0c;由点阵图或位图图像、用像素来代表图像。每一个像素都被分配一个特点的位置和颜色值。 图形和图像之间在一定条件下可以互相转换&#xf…