当前位置: 首页 > Web前端 > JavaScript

深入理解JavaScript执行上下文、闭包

时间:2023-03-27 13:37:34 JavaScript

理解闭包之前,需要了解几个概念,上下文、作用域链、活动对象、变量对象:上下文:函数的上下文决定了它们可以访问哪些数据,以及它们的行为。全局上下文是最外层的上下文。当代码执行流进入一个函数时,函数的上下文被压入上下文栈。函数执行后,上下文栈会弹出函数上下文。作用域链:当执行上下文中的代码时,会创建作用域链,它决定了每一级上下文中的代码访问变量或函数的顺序。执行代码的上下文变量对象始终位于作用域链的顶部,然后是包含上下文对象,然后是下一个包含上下文对象,直到到达全局上下文对象。激活对象:在函数上下文中,其中包含变量的对象。变量对象:在全局上下文中,其中包含变量的对象。我们总结一下,当一个函数被调用时,它的作用域链中保存了哪些对象?调用函数时,首先创建执行上下文并创建作用域链。参数和其他命名参数初始化函数的活动对象。外部函数的活动对象是函数作用域链的第二个对象。作用域链一直将包含该函数的所有活动对象连接到外部,直到全局上下文终止。下面声明并调用了一个compare()方法,现在来梳理一下这个方法从创建到执行的过程:functioncompare(value1,value2){if(value1value2){返回1;}否则{返回0;}}让result=compare(5,6);1.执行全局代码,创建全局执行上下文,将全局上下文压入执行上下文栈ECStack=[//执行上下文栈globalContext];2.全局上下文变量是变量对象初始化。初始化的同时创建比较函数,创建作用域链,在内部属性[[scope]]中预加载全局变量对象。globalContext={//变量对象初始化VO:[global],Scope:[globalContext.VO],this:globalContext.VO}c??ompare.[[scope]]=[//compare预加载作用域链globalContext中的全局变量对象。画外音];3.执行compare函数,创建compare函数执行上下文,将compare函数执行上下文压入执行上下文栈ECStack=[//执行上下文栈comapreContext,globalContext];4.comapre函数执行上下文初始化及变量赋值:1)复制函数[[scope]]属性创建作用域链,2)创建带参数的活动对象,3)初始化活动对象,即添加形参、函数声明和变量声明,4)将活动对象推入comapre作用域链的顶端。comapreContext={AO:{arguments:{0:51:6length:2},scope:undefined,},Scope:[AO,globalContext.VO],}5.执行代码,函数执行完后返回,从执行上下文堆栈中弹出函数comapre的执行上下文。ECStack=[globalContext];compare()方法的作用域链如下:作用域链实际上是一个包含指针的列表,每个指针指向一个变量对象,但在物理上并不包含对应的对象。闭包《JavaScript高级编程》:闭包是引用另一个函数范围内的变量的函数,通常在嵌套函数中实现。MDN--closure:一个函数和它周围状态(lexicalenvironment,词法环境)的引用捆绑在一起(或者函数被引用包围),这样的组合就是闭包。定义有点抽象,借用阮一峰老师的理解:闭包是一个函数,可以读取其他函数的内部变量。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,所以闭包可以简单理解为“函数内部定义的函数”。所以,本质上,闭包是连接函数内部和函数外部的桥梁。varwinVar='window-小白';functionfun(){varfunVar='fun-小白';console.log(winVar)//window-小白console.log(funVar)//fun-小白}fun();console.log(funVar)//UncaughtReferenceError:funVarisnotdefined函数作用域链如下:当函数在作用域链的顶端找不到winVar对象时,会在全局变量对象中寻找,而在winodow中,fun()函数的变量是无法访问的,所以需要闭包。闭包可以用在很多地方。它有两个最大的用途,一个是读取函数内部的变量,另一个是将这些变量的值时刻保存在内存中。functionfun(){varfunVar='fun-小白';返回函数(){返回funVar;}}让变量=fun();让结果=变量();console.log(result)//fun-小白函??数的作用域链如下图所示。fun方法返回匿名函数后,初始化匿名函数的作用域链,包括fun的活动对象和全局变量对象。虽然fun()函数执行结束,其执行上下文函数的作用域链会被破坏,但是在匿名函数的作用域链中,仍然有对他的引用。这样应该就不难理解为什么闭包可以读取函数内部的变量,并且还把变量的值保存在内存中。返回的匿名函数存储在变量method中。将variable()方法设置为null可以取消引用函数,允许垃圾收集器释放内存。变量=空;同时,由于闭包保留了包含它们的函数的作用域,因此它们比其他函数占用更多的内存。过度使用闭包会导致过多的内存占用,所以闭包不能被滥用,否则会导致网页出现性能问题。参考:MDN--Closure阮一峰的博客JavaScript深入作用域链JavaScript深入闭包《JavaScript高级编程(第四版)》