JavaScript函数执行、作用域链以及内存管理详解

目录
  • 前言
  • 函数执行
    • 全局执行上下文
    • 函数执行上下文
  • 作用域链
  • 内存管理
    • 引用计数
    • 标记清除

前言

在我们平常编写JavaScript代码的时候,难免会用到函数,函数里面会有各种变量,这些变量的作用的范围,以及在使用内存存储这些变量时,内存管理的问题,在平时编程亦或者面试时,多多少少都会遇到,所以这篇文章针对这三个问题,进行了深入的探讨。

函数执行

首先说一下JavaScript执行代码的顺序,JavaScript在执行一段可执行代码的时候,会创建一个执行上下文栈(Execution Context Stack 简称ECStack),执行全局代码时创建的全局执行上下文(Global Execution Context 简称GEC),以及执行函数时创建的函数执行上下文(Function Execution Context 简称FEC),在运行时都会按顺序放入栈中,而不管是全局执行上下文还是函数执行上下文在创建时都会有一个变量对象(variable Object)。

全局执行上下文

在JavaScript执行全局代码时,会创建一个全局执行上下文,放入执行上下文栈,还有一个GlobalObject(GO),全局执行上下文中会有一个变量对象(variable Object),指向GO。

在编译阶段,GO会对在全局定义的变量初始化为undefined,当遇到函数时,便会以函数名作为GO的一个属性名,值为存储这个函数空间的内存地址,在这个函数空间中,会有函数的执行体(代码段),还会存储这个函数父级作用域。

编译完成后,代码开始执行,便会对这些变量赋值,当然里面除了这些,还有一些全局的对象和函数,比如setTimeout,Date,String等等,还有一个属性window赋值为this,当遇到函数时,便会创建一个函数执行上下文放入执行上下文栈中。

函数执行上下文

上文说到,代码执行时候,执行到函数时,会创建函数执行上下文,并且函数执行上下文放入执行上下文栈中,同样,在函数执行上下文里面,会有一个VO(Variable Object)变量对象,这里的VO其实指向AO(Activation Object),这里的AO类似于GO,只不过它不是全局的,而是函数特有的,在执行函数内部代码前,即编译阶段,也会将变量赋值为undefined,如果里面嵌套函数,类似GO,会以函数名作为GO的一个属性名,值为存储这个函数空间的内存地址,在这个函数空间中,会有函数的执行体(代码段),还会存储这个函数父级作用域,然后执行时,将变量赋值,如果里面嵌套的函数被执行,也会创建函数执行上下文,并且这个函数执行上下文放入执行上下文栈中。

作用域链

其实在创建VO对象时,也会在函数执行上下文中创建作用域链,这个作用域链包括,自身的变量对象(VO)和父级作用域,当我们查找一个变量时,真实的查找路径是沿着作用域链来查找

这段代码中,显然name会顺着作用域链查找到“why”,然后显然在foo 函数编译未执行阶段,m=undefined,然后执行,m输出的应该是undefined,如下图是代码的执行逻辑图。

这里的message输出的应该是Hello Global,foo函数在全局初始化时父级作用域已经为全局了,然后foo函数执行时,找不到message变量便会去父级作用域去寻找,也就是全局作用域,所以输出的是Hello Global,执行逻辑图如下。

上图的输出是undefined而不是100,就因为foo函数在解析时碰到return var a=100已经认为定义了一个a,赋值为undefined,但在执行时却不会执行到这一步。

这里要提一下,如果变量在定义时,未加任何约束。

比如通常来说定义一个变量是 var name=“anonymous”,let name="anonymous"如果直接写成name=”anonymous“,在其他语言中,这肯定会报错,但是在JavaScript中,允许这种写法,并且这种写法定义的变量会直接加到GO里面。

function foo(){
	var a=b=10
}
foo()
console.log(a,b)

var a=b=10<==>等同于var a=10;b=10;

这样的话b放入GO中,在全局输出值为10;而a仅在函数中被定义,在全局输出显然会报错。

内存管理

不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存的,不同的是某些编程语需要我们自己手动的管理内存,某些编程语言会可以自动帮助我们管理内存:

不管以什么样的方式来管理内存,内存的管理都会有如下的生命周期:

  • 第一步:分配申请你需要的内存(申请)
  • 第二步:使用分配的内存(存放一些东西,比如对象等) ;
  • 第三步:不需要使用时,对其进行释放;

不同的编程语言对于第一步和第三步会有不同的实现:

手动管理内存:比如C、 C++ ,都是需要 手动来管理内存的申请和释放的 (malloc和free)

自动管理内存:比如Java、JavaScript. Python. Swift、 Dart等 ,它们有自动帮助我们管理内存;

引用计数

当一个对象有一个引用指向它时,那么这个对象的引用就+1 ,当一个对象的引用为0时,这个对象就可以被销毁掉;

这个算法有一个很大的弊端就是会产生循环引用:

var obj1={info:obj2};
var obj2={info:obj1}

标记清除

这个算法是设置一个根对象( root object) ,垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对于哪些没有引用到的对象,就认为是不可用的对象;

这个算法可以很好的解决循环弓|用的问题;

JS引擎比较广泛的采用的就是标记清除算法,当然类似于V8弓|擎为了进行更好的优化,它在算法的实现细节上也会结合一些其他的算法。

以上就是JavaScript函数执行、作用域链以及内存管理详解的详细内容,更多关于函数执行、作用域链以及内存管理的资料请关注我们其它相关文章,希望大家以后多多支持我们!

(0)

相关推荐

  • 中高级前端必须了解的JS中的内存管理(推荐)

    前言 像C语言这样的底层语言一般都有底层的内存管理接口,比如 malloc()和free()用于分配内存和释放内存. 而对于JavaScript来说,会在创建变量(对象,字符串等)时分配内存,并且在不再使用它们时"自动"释放内存,这个自动释放内存的过程称为垃圾回收. 因为自动垃圾回收机制的存在,让大多Javascript开发者感觉他们可以不关心内存管理,所以会在一些情况下导致内存泄漏. 内存生命周期 JS 环境中分配的内存有如下声明周期: 1.内存分配:当我们申明变量.函数.对象的时候

  • JavaScript中的执行环境和作用域链

    前言 JS 中的执行环境和作用域链是非常重要的概念,它们是 JS 引擎在处理 JS 代码的时候对变量和函数的处理方式,这两个概念的正确理解能够帮助我们更好地理解和预测代码的行为. 执行环境 执行环境定义了变量或者函数有权访问的数据集合,每一个执行环境都有一个与之关联的变量对象,该执行环境中定义的所有变量和函数都保存在这个对象中.我们无法直接访问这个对象,这个对象只是在解析器处理数据的时候使用. 我们平时说的全局变量就是在最外围的一个执行环境中定义的变量,全局执行环境根据 ECMAScript 的

  • javascript内存管理详细解析

    介绍 低层次的语言,如C,具有低级别的内存管理命令,如:malloc()和free(),需要开发者手工释放内存.然而像javascript这样的高级语言情况则不同,对象(objects, strings 等)创建的时候分配内存,当他们不在使用的时候内存会被自动回收,这个自动回收的过程被称为垃圾回收.因为垃圾回收的存在,让javascript等高级语言开发者产生了一个错误的认识,以为可以不用关心内存管理. 内存生命周期 不管什么样的编程语言,内存的生命周期基本上是一致的. 1.分配你需要的内存 2

  • javascript作用域和作用域链详解

    目录 一.javascript的作用域 1.全局作用域 2.局部作用域 二.javascript的作用域链 三.作用域链和优化 四.改变作用域链 1.with语法改变作用域链 2.catch语法 总结 一.javascript的作用域 1.全局作用域 1.最外层函数和最外层函数定义的变量 var age = 20 function func1() { var sex = "男" function func2() { console.log("hello func2"

  • JavaScript内存管理与闭包实例详解

    目录 1. 内存管理的理解 1.1 认识内存管理 1.2 JavaScript的内存管理 2. 垃圾回收(GC) 2.1 认识垃圾回收 2.2 GC算法 – 引用计数 2.3 GC算法 – 标记清除 2.4 其他算法优化补充 3. 闭包的概念理解 3.1 JavaScript的函数式编程 3.2 定义 4. 闭包的内存流程 5. 闭包的内存泄漏 5.1 认识内存泄露 5.2 内存泄露的测试 5.3 浏览器的优化 总结 1. 内存管理的理解 1.1 认识内存管理 不管什么样的编程语言,在代码的执行

  • JavaScript作用域与作用域链优化方式

    目录 前言 内容 作用域 作用域的嵌套 作用域的一些实现细节 作用域链 相关优化 总结 前言 作用域和作用域链是所有JavaScript开发人员每天都要接触和应用的内容.不管是面试中的作用域链的面试考察,还是日常代码研发中变量与作用域链的构建,它的身影几乎无处不在.它就像一顶优秀厨师的厨师帽,只要我们走进厨房,我们就要将它整理好,套在头上.没有它整洁干净的戴在头上,你就不是一名好的JavaScript工程师. 其实,作为一名前端工程师,我也曾经疑惑过:基本上所有的计算机语言都具有作用域的概念,但

  • JavaScript内存管理介绍

    简介 低级语言,比如C,有低级的内存管理基元,想malloc(),free().另一方面,JavaScript的内存基元在变量(对象,字符串等等)创建时分配,然后在他们不再被使用时"自动"释放.后者被称为垃圾回收.这个"自动"是混淆并给JavaScript(和其他高级语言)开发者一个错觉:他们可以不用考虑内存管理. 内存生命周期 不管什么程序语言,内存生命周期基本一致: 1.分配你所需要的内存 2.使用它(读.写) 3.当它不被使用时释放   ps:和"把

  • JVM内存管理之JAVA语言的内存管理详解

    引言 内存管理一直是JAVA语言自豪与骄傲的资本,它让JAVA程序员基本上可以彻底忽略与内存管理相关的细节,只专注于业务逻辑.不过世界上不存在十全十美的好事,在带来了便利的同时,也因此引入了很多令人抓狂的内存溢出和泄露的问题. 可怕的事情还不只如此,有些使用其它语言开发的程序员,给JAVA程序员扣上了一个"不懂内存"的帽子,这着实有点让人难以接受.毕竟JAVA当中没有malloc和delete.没有析构函数.没有指针,刚开始接触JAVA的程序员们又怎么可能接触内存这一部分呢,更何况有不

  • C语言与C++中内存管理详解

    目录 内存分布 动态内存管理方式-堆区 C语言动态内存管理 C++动态内存管理 new和delete的用法 operator new与operator delete函数 new和delete的实现原理 定位new表达式 高频面试题 重点new/delete和malloc/free的区别 内存泄漏 内存分布 主要段及其分布 ​ 每个程序运行起来以后,它将拥有自己独立的虚拟地址空间.这个虚拟地址空间的大小与操作系统的位数有关系.32位硬件平台的虚拟地址空间的地址可以从0~2^32-1,即0x0000

  • C++内存管理详解使用方式

    目录 c++中内存管理的方式 new和delete操作符的使用方式 operator new和operator delete函数 new和delete的原理内部实现 内置类型 自定义类型 c++中内存管理的方式 在c语言中,我们拥有malloc和free等函数可以对内存进行动态管理 但是总体来说不是很方便,所以c++拥有了一种新的方式来对内存进行管理:通过new和delete操作符来对内存进行动态分配 new和delete操作符的使用方式 new操作符的使用方式: #include<iostre

  • C++ 类中有虚函数(虚函数表)时 内存分布详解

    虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table.在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承.覆盖的问题,保证其容真实反应实际的函数.这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数. 这里我们着重看一下这张虚函数表.C++的编译器应该是

  • C++动态内存管理详解

    目录 1.C/C++程序地址空间 2.C语言动态内存管理 (1)malloc (2)calloc (3)realloc (4)free 3.C++动态内存管理 (1)C++为什么要设计一套自己专属的动态内存管理方式? (2)new/delete定义 1)new/delete操作内置类型 2)new/delete操作自定义类型 (3)new/delete的实现原理 4.malloc/free和new/delete的区别 共同点: 不同点: 5.内存泄漏 总结 1.C/C++程序地址空间 计算机物理

  • javascript的内存管理详解

    介绍 低层次的语言,如C,具有低级别的内存管理命令,如:malloc()和free(),需要开发者手工释放内存.然而像javascript这样的高级语言情况则不同,对象(objects, strings 等)创建的时候分配内存,当他们不在使用的时候内存会被自动回收,这个自动回收的过程被称为垃圾回收.因为垃圾回收的存在,让javascript等高级语言开发者产生了一个错误的认识,以为可以不用关心内存管理. 内存生命周期 不管什么样的编程语言,内存的生命周期基本上是一致的. 1.分配你需要的内存 2

  • JavaScript函数的特性与应用实践深入详解

    本文实例讲述了JavaScript函数的特性与应用实践.分享给大家供大家参考,具体如下: 函数用于指定对象的行为.所谓的编程,就是将一组需求分解为一组函数和数据结构的技能. 1 函数对象 JavaScript 函数就是对象.对象是名值对的集合,它还拥有一个连接到原型对象的链接.对象字面量产生的对象连接到 Object.prototype,而函数对象连接到 Function.prototype(这个对象本身连接到 Object.prototype).每个函数在创建时会附加两个隐藏属性:函数的上下文

  • c++动态内存管理详解(new/delete)

    目录 前言 用法上 对内置类型 对自定义类型 new/delete底层原理 重载类的专属operatornew和operatordelete 定位new new/delete与malloc/free区别总结 内存泄漏 总结 前言 想必大家对c语言的动态内存分配并不陌生,忘了的小伙伴也可以看看我的这篇文章C语言动态内存分配 c语言的动态内存分配由于有些地方用起来比较麻烦同时检查错误的机制不适合c++,因此c++引入new/delete操作符进行内存管理,下面我们来深入探讨c++为什么要引入new/

  • JavaScript块级作用域绑定以及状态提升详解

    目录 前言 作用域/执行上下文 var声明 块级声明 不声明的变量 1.不使用关键字声明变量 2.使用var声明的变量 var声明和块级声明的区别 全局作用域绑定 状态提升 块级绑定的最佳实践 函数声明提升 总结 前言 在ES6之前,JavaScript只有经典的var声明,这给开发者带来了很多的困扰.在ES6出现后,又增加了let和const关键字的声明方式.这里会讲有关变量声明,作用域,状态提升相关知识. 作用域/执行上下文 在JavaScript的世界里,作用域可以分为三种,分别是全局作用

  • Python深入06——python的内存管理详解

    语言的内存管理是语言设计的一个重要方面.它是决定语言性能的重要因素.无论是C语言的手工管理,还是Java的垃圾回收,都成为语言最重要的特征.这里以Python语言为例子,说明一门动态类型的.面向对象的语言的内存管理方式. 对象的内存使用 赋值语句是语言最常见的功能了.但即使是最简单的赋值语句,也可以很有内涵.Python的赋值语句就很值得研究. a = 1 整数1为一个对象.而a是一个引用.利用赋值语句,引用a指向对象1.Python是动态类型的语言(参考动态类型),对象与引用分离.Python

随机推荐