什么是作用域
作用域(Scope)是计算机程序设计中的一个重要概念,它定义了变量和函数在程序中被识别和可访问的范围。作用域规则可以帮助确保变量名和函数名不会发生冲突。简单来说就是作用域规定了变量可见的范围,控制了变量的访问权限。而作用域在JavaScript中,根据变量和函数的位置和声明的类型也有着不同的类型,比如块级作用域和函数作用域。
作用域访问的基本规则
在JavaScript中,所有的代码在执行之前都需要先进行编译。而编译的过程简单来说就是在给这些变量和函数在某个确定的域中找到一个有效的标识符。而作用域就类似一个又一个的地盘,在自身的地盘访问自身的变量,来防止出错。而作用域与作用域之间又有着类似于父与子之间的继承关系,儿子可以访问父亲的作用域,但是父亲并不能访问儿子的作用域。
```var a=1
function foo1(){
var a=2
console.log(a)//输出2
}
function foo2(){
var b=3
}
function foo3(){
console.log(a)
}
foo1()
console.log(a)//输出1
foo3()
console.log(b)//直接报错
```js
就是代码在执行阶段变量的查找会先从由内到外的作用域中寻找,不能由外到内
讲解完作用域访问的基本规则就来说说三个基本的作用域
全局作用域和函数作用域
全局作用域是指在整个程序中都可访问的变量和函数。它可以在任何地方进行访问,包括函数内部和函数外部。因此在定义全局变量时要小心,因为它很容易被访问和修改。
```var a=1
function foo1(){
console.log(a)
}
function foo2(){
var a=2
console.log(a)
}
function foo3(){
a=3
console.log(a)
}
foo1()//输出1
foo2()//输出2
console.log(a)//输出1
foo3()//输出3
console.log(a)//输出3
```js
可以看出来在函数内部声明为一个局部变量a,在函数内部对其进行修改并不会影响全局作用域中a的值,而当不在函数内部声明局部变量a时,则首先在函数内部作用域寻找a,找不到则往外的全局作用域中寻找,此时在函数内进行修改则会影响全局作用域中a的值
块级作用域
在讲块级作用域之前先提一个JavaScript的奇怪特性————声明提升。
```console.log(a)//输出undifined
var a=1
foo()//输出123
function foo(){
console.log(123)
}
```js
按照正常的理解未先声明变量a就输出未给声明函数就调用应该报错,可是在JavaScript中却分别是输出undifined和成功调用了这个函数,这就是JavaScript的一个奇怪的(bug划掉)特性--声明提升。以上的代码等同于
```var a
console.log(a)//输出undifined
a=1
function foo(){console.log(123)}
foo()//输出123
```js
而后来JavaScript在某次更新中更新了两个关键字const,let用来解决声明提升这一违反往常代码逻辑的问题。首先来写一下这两个关键字相较于var有什么特点。
```let: const:
1.不会声明提升 1.不会声明提升
2.不能重复声明同一变量 2.不能重复声明同一变量
3.声明的变量不能被修改
```js
其实块级作用域简单来说就是{let/const}。在大括号内用let或者const声明一个变量就会形成一个块级作用域,也被叫做暂时性死区。
```if(1){
let a=1
const b=1
}//{}+let,{}+const生成一个块级作用域
console.log(a)
console.log(b)//报错a,b都无法被访问
let a=1
if(true){
console.log(a);//暂时性死区
let a=2
}
console.log(a)//输出1
```js
类似于函数作用域,const和let在{}内部声明的变量只能在{}内部调用和修改。
欺骗词法作用域
这个其实并不能算是一个作用域,更像一个类似与声明提升的(bug划掉)特性。在使用eval函数时有一个奇怪的现象。
```function foo(str){
eval(str)//var b=2
var a=1
console.log(a,b)
}
foo('b=2')//输出(1,2)
console.log(b)//输出2
```js
明明并没有声明b但是却能够输出,甚至在脱离了函数作用域,在全局作用域内都能输出。这个就是欺骗词法作用域。首先解释一个东西
```b=2
```js
直接在JavaScript中这样写就会导致生成一个全局变量b,而在一个函数内调用eval函数的话就类似于这样生成了一个全局变量,明明是在函数中但是却是一个全局变量。欺骗了JavaScript的编译,所以就叫做欺骗词法作用域。