小白如何快速学懂作用域-js篇
你真的懂什么是声明提升吗? (作者也是初学者,分享自己的学习心得,求赞,轻喷)
什么是作用域
作用域指一个变量的作用范围。 在js中,作用域有 全局作用域 、函数作用域 和 块级作用域。 还有一个特殊的作用域------欺骗词法作用域。
作用域
那么作用域是怎么产生的呢?首先我们在运行代码时,编译器引擎是需要先编译再执行我们的代码的。例如vscode,当我们需要运行一段代码时,V8引擎就会先对代码进行编译,找到有哪些有效标识符,然后再去执行代码语句。这时候作用域的区别就体现出来了。如下图:

V8开始编译,找到一个变量 a 和一个函数 foo(注意这时函数里的东西是暂时不用管的,编译只负责找到有效标识符)。这个时候开始执行,先生成了一个全局作用域 ,在全局作用域下开始执行第一行,把1赋给a,再来到第七行调用该函数 foo。那在执行函数时是不是又要先编译呢,于是'阿编'来到函数内,发现并没有有效标识符,那'阿执'要进来了,又生成了函数作用域 ,一看,只有一个打印的执行语句,要我们打印a的值,可是函数里并没有a。这里又有新的知识点,变量的查找会先从内到外的作用域中查找,不能从外到内。
在函数作用域内没有找到变量a,再往外一层来到全局作用域,发现a的值是1。
我们再举个例子加深理解一下变量的查找

还和上面一样,编译找到变量 a 和函数 foo,生成全局作用域。执行第一行赋值语句,调用函数foo。来到函数内找到变量 a 和函数 bar。生成foo函数的作用域,执行第四行的赋值语句,第九行调用函数 bar。'阿编'又来到bar函数内,就这?啥也没有。'阿执':领域展开(bar函数的作用域-.-),需要打印a的值。先在自己作用域看看吧,没有,往外一层来到foo的作用域,发现a的值是2,完事。
全局作用域
写在script标签内的都是全局作用域
函数作用域
函数被调用的那一刻产生
块级作用域
- var 声明的变量存在声明提升,提升到当前作用域的顶端
- 函数声明会整体提升,提升到当前作用域的顶端
- let + {} 会形成块级作用域 const + {}也会
我们再来细细聊一下这个声明提升,用上面你学会的方法试着解析下面这段代码:
css
console.log(a);
var a = 1
运行后你会发现我们这里输出的是undefined。这表示我们找到了变量a,但是它没有值。因为执行代码是从上往下执行的,你的打印代码在我赋值之前,可不就是没有值嘛。可是你又仔细一想,嗯~?(恭喜你,盲生,你发现了华点)我都没定义a,你凭什么就先访问到了a呢,你不是应该报错吗?------声明提升闪亮登场
因为声明提升,上面代码在执行时变成了这样:
css
var a
console.log(a);
a = 1
同理,第2点也就很好解释了,不多比比。所以在编程时我们推荐使用 let 去声明变量。
let
- 不会声明提升
- 不能重复声明同一变量
const
- 不会声明提升
- 不能重复声明同一变量
- 声明的变量值不能被修改 (我们通常称之为常量)
欺骗词法作用域
- eval() 让原本不属于这里的代码加进来,变成就是这里写的代码
- with 当修改对象中不存在的属性时,该属性会泄露到全局成为全局变量
you should know:当你声明变量没有加关键字,不管你写在哪它都是全局变量。
以上没有过多解释的我们知道就行,毕竟咱是小白(主要我也解释不清-.-),在后续学习中我们会了解的。
