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

 更新时间:2023年01月08日 11:09:11   作者:既白biu  
这篇文章主要介绍了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函数执行、作用域链以及内存管理详解的详细内容,更多关于函数执行、作用域链以及内存管理的资料请关注脚本之家其它相关文章,希望大家以后多多支持脚本之家!

相关文章

  • 基于javascript实现图片左右切换效果

    基于javascript实现图片左右切换效果

    这篇文章主要为大家介绍了基于javascript实现图片左右切换效果,感兴趣的小伙伴们可以参考一下
    2016-01-01
  • 微信小程序实现历史搜索功能的全过程(h5同理)

    微信小程序实现历史搜索功能的全过程(h5同理)

    最近在使用微信小程序开发的时候遇到了一个需求,需要实现历史搜索记录的功能,所以下面这篇文章主要给大家介绍了关于微信小程序实现历史搜索功能(h5同理)的相关资料,需要的朋友可以参考下
    2022-12-12
  • JS实现随机数生成算法示例代码

    JS实现随机数生成算法示例代码

    JS实现随机数生成算法的方法有很多,本文为大家介绍一个比较不错的方法,代码如下,感兴趣的朋友可以参考下,希望对大家有所帮助
    2013-08-08
  • JS 自定义带默认值的函数

    JS 自定义带默认值的函数

    今天与同事一起看了一个javscript定义函数问题,如何在定义一个函数里给参数一个默认值.
    2011-07-07
  • JS拖拽组件学习使用

    JS拖拽组件学习使用

    这篇文章主要为大家介绍了JS拖拽组件的开发过程,以及如何正确使用JS拖拽组件,做到举一反三,感兴趣的小伙伴们可以参考一下
    2016-01-01
  • 如何在父窗口中得知window.open()出的子窗口关闭事件

    如何在父窗口中得知window.open()出的子窗口关闭事件

    在父窗口中得知window.open()出的子窗口关闭事件的方法有很多,在本文将为大家详细介绍下,感兴趣的朋友可以参考下
    2013-10-10
  • Bootstrap实现模态框效果

    Bootstrap实现模态框效果

    这篇文章主要为大家详细介绍了Bootstrap实现模态框效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • JS实现页面载入时随机显示图片效果

    JS实现页面载入时随机显示图片效果

    这篇文章主要介绍了JS实现页面载入时随机显示图片效果,涉及javascript基于随机数与数组的页面元素动态修改相关操作技巧,需要的朋友可以参考下
    2016-09-09
  • js防抖函数和节流函数使用场景和实现区别示例分析

    js防抖函数和节流函数使用场景和实现区别示例分析

    这篇文章主要介绍了js防抖函数和节流函数使用场景和实现区别,结合实例形式详细分析了js防抖函数和节流函数基本功能、定义、用法区别及操作注意事项,需要的朋友可以参考下
    2020-04-04
  • js substr、substring和slice使用说明小记

    js substr、substring和slice使用说明小记

    关于substr、substring和slice方法区别的文章,网上搜到了许多,文章内容也基本一致。而后,我将其中一篇文章中的代码挪到本地进行了测试,发现测试结果和原文中的有些出入。
    2011-09-09

最新评论