一、什么是作用域?
引用《你不知道的JavaScript》中的定义:作用域是一套良好的规则来存储变量,并且方便地找到变量。
二、LHS与RHS
在分析作用域时就不可避免的需要先了解访问作用域的方法,其中有两个概念LHS和RHS。程序中的源代码在执行之前都会经历三个步骤,分别是分词/词法分析、解析/语法分析、代码生成。这三个步骤统称编译。
举例 var b = a;这句代码,它可以拆解成var b(对b这个变量进行定义),b = a;对b这个变量进行赋值。而这句代码中就包含了对b的LHS查询以及对a的RHS查询。
LHS(Left Hand Side)、RHS(Right Hand Side),按照语义可以将LHS和RHS简单分为在赋值符号(不一定非得是”=“ 也可能是”<“、">" 等)的左侧或者是右侧。拿b = a;作为例子,b是位于=左侧被赋值的变量,a则是在=右侧赋值的具体值,这是针对位置对RHS和LHS的简单位置划分。从查询意义上说,可以笼统的将LHS查询作为找出变量b,并对b进行赋值操作;RHS则是找出变量a,查询出它的具体值。更为清晰直观的说法,LHS是找出一个杯子,并将杯中的水倒空,装上新的饮料;RHS则是找出一个杯子,查看里面装的是什么饮料。
书本上的例子,这里也摆上来,可以自己测试一下是否理解。
function foo(a){ var b = a; return a + b; } var c = foo(2);
上述代码中有几个LFS查询又有几个RFS查询呢?
答案:3次LHS查询和4次RHS查询。3次LHS分别是b=a;c=foo(2);a=2;其中a=2是隐藏在函数调用中的。4次RHS分别是b=a中查找a;return a+b;中查找a,b以及c = foo(2)中查找foo();
在了解RHS以及LHS查询后,我们可以开始理解查找作用域的逻辑思路,下面给出几段简单的代码,依照每个代码给出的输出结果,来看JS中的作用域。
代码段一
function init(a){ b = a + 3; } init(2); alert(b); //输出5
在函数内部定义变量时,省略var操作符的情况下,可以创建一个全局变量,此段代码的b就是一个全局变量,因此输出5而非undefine
代码段二
1 2 3 4 5 | function init (a){ var b = a + 3; } init(2); alert(b); //输出ReferenceError |
在函数init()内声明一个var b变量,变量的作用域在函数运行退出后就销毁了,因此RHS中是无法在作用域中查找到b变量的,因此会显示ReferenceError。
代码段三
1 2 3 4 5 6 | var b; function init(a){ b = a + 3; } init(2); alert(b); //输出undefine |
首先b变量是在全局作用域声明,但未赋值,因此,在执行alert(b),查找变量b作用域时,是可以在全局作用域中找到的,但因并未对b赋值,因此会输出undefine。
代码段四
1 2 3 4 5 | var b = 2; function init(a){ alert(a+b); } init(2); //输出4 |
首先变量b是在全局作用域里面,在函数中alert(a+b)中调用的时候,首先RHS搜索,a=2,b则是在全局作用域中赋值为2,输出4,作用域嵌套,内层函数为无定义则到外层查询。
三、词法作用域
词法作用域概念:
词法作用域就是定义在词法阶段的作用域,词法作用域由你在写代码时将变量和块作用域写在何处而决定。
四、需要谨记的规则
1、作用域查找会在找到第一个匹配的标识符时停止;从执行作用域开始查找变量,逐步到最外层搜索,如果在全局作用域中也没有找到,就会停止。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程