JS的作用域
有如下代码:
1 <body> 2 <script type="text/javascript"> 3 alert(a); 4 alert(b); 5 alert(c); 6 alert(d); 7 8 var a = 5; 9 if (false) { 10 var b = 6; 11 }else{ 12 c = 7; 13 } 14 function f() { 15 var d = 8; 16 } 17 </script> 18 </body>
这段代码4个alert(),根据前面的预处理知识,首先a是undefined,d是f的局部变量,c不会被预处理,所以报错,那b呢?结果是b也弹出undefined,说明在预处理的时候,if else判断语句被忽略了。
首先知道,作用域有4类,分别是:
- 块作用域;
- 函数作用域;
- 动态作用域;
- 词法作用域(也称为静态作用域或闭包);
然后一一分析
1.作用域的定义
一个变量、函数、成员在整个程序里面可以被访问的范围就叫作用域。
2.块作用域
正如大部分的编程语言一样,以一对大括号括起来的一段代码就叫一个块,所谓块作用域,就是只在这个块中生效。
有如下代码:
1 for (var i = 0; i < 3; i++) { 2 3 } 4 console.log(i);
该循环结束后,控制台打印为3,说明var i 是不具备块作用域效果的,但是在ES6标准中,var 被 let 取代,凡是以 let声明的变量是块作用域的,如下:
1 for (let i = 0; i < 3; i++) { 2 3 } 4 console.log(i);
var 改为 let后该段代码打印会报错 i is not defined;所以官方推荐使用let而不是var来声明变量。
3.函数作用域
即函数里面声明的变量,相当于Java的局部变量,只在一个函数里面生效。
4.动态作用域
有如下代码:
1 function f() { 2 console.log(x); 3 } 4 5 function f1() { 6 var x = 5; 7 f(); 8 } 9 10 function f2() { 11 var x = 6; 12 f(); 13 } 14 15 f1();
所谓动态作用域,就是如上所示,同一变量在不同的作用域中值应该不同,但是上面代码执行会报错,说明JS并不是动态作用域,而是静态作用域。
5.词法作用域
静态作用域,又叫词法作用域(LexicalEnviroment)或闭包,所以闭包和作用域是紧密联系的,严格意义上来说,JS的作用域规则便是词法作用域。
在JS代码被解释执行的时候,
- 某个函数 f 被解释创建,然后会给 f 函数本身添加一个成员(不可见的成员) [[scope]], scope等于创建函数 f 时的全局词法环境,[[scope]] == LexicalEnviroment == window, 也就是说,函数内部有一个隐形对象指向window;
- 当函数f真正执行的时候,会创建函数自己的词法环境。f 的词法环境会跟 f 的作用域关联起来,Lx -> f.[[scope]] ;
1 function f() { //scope == window 2 //f.le -> f.[[scope]] 3 var x = 100; 4 function g() { //g.[[scope]] == f.le 5 //g.le -> g.[[scope]] 6 } 7 g(); 8 } 9 //g.le -> g.[[scope]] -> f.le -> f.[[scope]] == windos 这便是整个作用域的链条关系
一般的作用域都遵循此规则,但是以new Function(){}方式创建的函数其作用域却永远指向全局,而不是它的父函数。
回顾函数的创建方式:
- 直接声明,function 函数名(){}
- 函数表达式,var 函数名 = 匿名函数或有名函数/自调用匿名函数
- new Function("函数名","函数体")形式
1 <body> 2 <script type="text/javascript"> 3 var x = 12; 4 5 function f() { 6 var x = 100; 7 //g.[[scope]] == window 8 var g = new Function("", "alert(x)"); 9 g(); 10 } 11 f(); 12 </script> 13 </body>
这段代码弹出的是12,而不是100,证明了这个规则,还有,这种写法在单独运行js文件时会报 x is not defined,因为没有全局window对象。
6.作用域的本质
所以:作用域的而本质就对某一个需要的变量,从本身词法环境开始,逐级向上查找,直至全局对象的过程。
作用域的意义在于信息隐藏,比如避免太多的全局变量引起的冲突,如下代码:
1 <body> 2 <script type="text/javascript"> 3 (function () { 4 var a = 5; 5 var b = 6; 6 7 function f() { 8 alert(a); 9 } 10 window.f = f; 11 })(); 12 13 f(); 14 </script> 15 </body>
在这段代码中,自调函数定义了变量,将其访问方式挂载到window对象上,局部变量a还可以被外面访问到,并没有被销毁,这便涉及到JS的特性---闭包。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探