当前位置: 首页 > 科技观察

你能说出Javascript的九个作用域中的几个?

时间:2023-03-19 02:16:05 科技观察

作用域想必大家都知道,就是变量生效的作用域。比如一个函数会产生一个作用域,声明的变量只会在函数内部生效。这样的作用域一共有9个,大部分前端都叫不出其中的几个。下面我们一起过一遍这9个作用域,看看你知道多少:(为了保证准确性,所有作用域类型都是通过调试得到的)1.全局作用域通过var声明一个变量,类型为Breakpoint,可以看到有一个作用域是Global输入Scope,即全局作用域,存放变量a:在浏览器环境下,可以通过a访问全局变量,也可以通过window.a访问。2、Local作用域声明了一个函数,在函数中声明了一个变量。在调用这个函数的时候,可以看到Scope中有一个Local类型的作用域,也就是本地作用域,里面存放的是变量b:这两个函数域很常见,没啥好说的。3、块作用域es6增加了块语句,同样会生成一个作用域:如图,里面声明的变量a会放在Block作用域中,即块级作用域。if、while、for等语句会生成Block作用域:前几个作用域很常见,但大多数前端不知道以下作用域:4.脚本作用域你认为这段代码会生成什么Domain:很多同学会说,这不就是全局作用域吗?能否解释一下这个现象:如果a、b、c都是全局变量,在浏览器中可以通过window.xx访问,但是结果是window.a和window.b都是undefined,访问的是a直接,b就可以得到值。只看当前作用域:会发现let和const声明的全局变量放在脚本作用域,而var声明的变量放在全局作用域。这是在浏览器环境使用letconst声明全局变量时的特殊作用域,脚本作用域。这个全局变量可以直接访问,但不能通过window.xx。所以再看到这样的代码也就不足为奇了:window.xxx=xxx;这个xxx必须是通过let和const声明的全局变量,需要手动链接到window。节点环境中是否存在上述脚本作用域?让我们用节点调试它。5.模块作用域同样的代码在node环境下没有Script作用域,但是多了一个Local作用域:这个Local作用域还有module、exports、require等变量,称为模块作用域。这个作用域有点特殊,其实也是一个函数作用域。为什么?稍后会有解释。说到特殊作用域,其实有一些:6.CatchBlock作用域Catch语句还会生成一个特殊作用域,CatchBlock作用域,其特点是可以访问错误对象:在node中也是一样的,但是还是有的是一层模块作用域:有同学会问,那finally语句呢?这个没有什么特别的,就是Blockscope:类似于WithBlock:7.WithBlockscope,大家猜猜这个with语句中的作用域是什么:你一定猜到了,with语句中的函数域就是这个对象:换成普通对象更明显:8.Closure作用域闭包是JS中常见的概念。它是一种函数返回另一个函数的形式。返回的函数引用外部函数的变量。将以闭包的形式保存。例如:functionfun(){consta=1;常量b=2;返回函数(){constc=2;控制台日志(a,c);调试器;};}constf=fun();f();闭包的变量是如何保存的?通过node可以看出:变量a的值是通过Closure作用域保存的,这个Closure作用域是闭包的核心。那为什么只保存了a,没有保存b和c呢?c在返回函数的范围内,不在外层范围内,b没有用到,所以Closure范围内只保存了a。那么在执行过程中会恢复Closure作用域:这样函数需要的外部变量都在Closure作用域中,没有任何损失,可以正常执行。是不是很巧妙!这是闭包的核心。当然,闭包的作用域也可以是多层的,例如:functionfun(){consta=1;常量b=2;返回函数(){constc=2;常量d=4;返回函数(){conste=5;console.log(a,c,e);};};}constf=fun()();f();使用的外部变量在两个作用域,然后是两个AClosure作用域:只剩下使用作用域的变量a和c。执行的时候会还原两层闭包作用域:这个函数需要的外部环境一点都不多。当您了解Closure的范围时,您就真正了解了闭包。闭包中还有一个特例,就是eval:如果我把上面的代码改一下,把打印语句改成eval,会发生什么?函数fun(){consta=1;常量b=2;返回函数(){constc=2;常量d=4;返回函数(){conste=5;eval("console.log(a,c,e);");};};}constf=fun()();f();有的同学会说,这不是一样的吗,都会形成一个闭包。没错,闭包会形成,只是保存的变量不同:你会发现它把外部作用域的变量都保存到Closure作用域中,包括模块作用域的变量。为什么?因为它根本不分析字符串,也分析不出来。如果你的JS是从服务器动态获取然后eval呢?无法分析!无法分析如何保证代码执行不出错?你不能把他们都救下来吗?所以当返回的函数有eval时,JS引擎会形成一个特别大的Closure,把所有的变量都放在里面。这样执行eval就不会报错了:变量都给你了,怎么会报错呢?但是这样显然性能不好,会占用更多的内存,所以尽量不要在闭包中使用eval。前面说了模块作用域是一个特殊的函数作用域,为什么这么说呢?这跟node模块的执行机制有关系。比如这样一段代码:functionfunc(){require;调试器;}函数();执行后发现形成了一个闭包:而如果不访问模块作用域内的变量,就没有这一层:我明明没有闭包的代码!这个跟node模块的执行机制有关:node会把模块变成一个函数,这个函数有五个参数:exports、require、module、__dirname、__filename,然后传入这五个参数执行:so函数模块的作用域只是一个函数作用域!模块中的函数引用模块作用域中的变量,然后执行,自然就形成了一个闭包。9.evalscope最后一种特殊的作用域是evalscope。比如这样一段代码:eval(`consta=1;constb=2;constc=3;console.log(a,b,c);debugger;`);执行后是这样的:可以看到有一个单独的eval函数作用域,eval代码中声明的变量都在这个作用域内:综上所述,JS中一共有9个作用域,我们通过调试来分析:globalscope:全局作用域,在浏览器环境下是window,在node环境下是globalLocalscope:局部作用域,或者函数作用域Blockscope:块级作用域Scriptscope:let和const声明的全局变量会保存在脚本作用域,可以直接访问这些变量,但是不能通过window.xx访问模块作用域:其实严格来说这也是一个函数作用域,因为node执行的时候会包裹一层函数,这是一个特殊的函数作用域,包括module、exports、require等。VariableCatchBlockScope:catch语句可以访问错误对象的作用域WithBlockScope:with语句的作用域是传递的对象的值在封闭范围内:W当函数返回到函数时,使用到的外部变量会被保存在Closure作用域中,里面有它执行时应该有的所有变量。这就是关闭。eval的闭包比较特殊,会保存Closurescope中的所有变量Eval作用域:eval代码声明的变量都会保存在Eval作用域中。这些都是调试得到的,是JS引擎在执行代码区时的真实功能。JavaScript中有9个作用域,你能说出几个?