什么是作用域和作用域链?
作用域(scope):简单来说作用域就是变量能够生效的一个范围,一旦超过了这个范围变量就无法访问
作用域链(Scope Chain):访问变量的一个查找过程,首先会在当前的作用域进行查找,查找不到就会返回到父级作用域来查找,若是依旧没有找到,就会依次逐级向上查找,直到全局作用域,变量访问的原则遵循就近原则
作用域的类型:
- 全局作用域
- 函数作用域
- 块级作用域
函数作用域
在foo函数内声明了一个名字叫Jack ,在函数内访问打印,没有问题,但是在函数外无法访问到,name变量的生效范围只有函数的{ } 内这么大一块的范围
function foo() {
let name ='jack'
console.log(name); //jack
}
foo()
console.log(name);//报错
块级作用域
块级作用域可通过ES6新增命令let和const声明,所声明的变量在指定块的作用域外无法被访问。块级作用域在如下情况被创建:
- 在一个函数内部
- 在一个代码块(由一对花括号包裹)内部
for(let i = 0;i<5;i++) {
console.log(i);//0,1,2,3,4
}
console.log(i);//Uncaught ReferenceError: i is not defined
全局作用域
声明在文件最外层的就是全局作用域,使用var声明的变量也是属于全局作用域,无论在哪里都可以被访问到
这里在全局声明了一个变量 a 赋值为了10,在全局和函数内都可以被访问到
let a = 10
console.log(a);//10
function foo() {
console.log(a);//10
}
foo()
所有window对象的属性拥有全局作用域
一般情况下,window对象的内置属性都拥有全局作用域,例如window.name、window.location、window.top等等。
全局作用域有个弊端:如果我们写了很多行 JS 代码,变量定义都没有用函数包括,那么它们就全部都在全局作用域中。这样就会 污染全局命名空间, 容易引起命名冲突。
值得注意的是若是变量没有声明直接进行赋值也属于全局变量,不推荐
function foo() {
age = 20
console.log(age);//20
}
foo()
console.log(age);//20
为window对象动态添加的属性默认也是属于全局的,同样不推荐
作用域链
let foo = 'foo';
function fo() {
let one = 'bac';
// 打印 'one'
console.log(one);
// 打印 'foo'
console.log(foo);
number = 42;
console.log(number); // 打印 42
}
fo();
当函数fo()被调用,Javascript引擎首先在当前作用域下寻找变量bac,然后寻找foo变量但发现在当前作用域下找不到,然后继续在外部作用域寻找找到了它(这里是在全局作用域找到的)。
然后将42赋值给变量number。Javascript引擎会在当前作用域以及外部作用域下一步步寻找number变量(没找到)。
如果是在非严格模式下,引擎会创建一个number的全局变量并把42赋值给它。但如果是严格模式下就会报错了。
结论:当使用一个变量的时候,Javascript引擎会循着作用域链一层一层往上找该变量,直到找到该变量为止。
小结
作用域:
- 作用域是指变量、函数和对象的可访问范围。
- JavaScript中的作用域分为全局作用域和局部作用域。
- 全局作用域中定义的变量可以在整个程序中访问,称为全局变量。
- 局部作用域由函数创建,在函数内部定义的变量只能在函数内部访问,称为局部变量或函数作用域变量。
作用域链:
- 作用域链是一种变量查找机制,用于确定在特定作用域中访问变量时的查找顺序。
- 当访问一个变量时,JavaScript引擎首先在当前作用域中查找该变量,如果找到了则直接使用。
- 如果在当前作用域中没有找到该变量,JavaScript引擎将会沿着作用域链向上一级作用域继续查找,直到找到该变量或者抵达全局作用域。
- 作用域链的尽头是全局作用域,如果在所有的嵌套作用域中都找不到该变量,JavaScript引擎将抛出
ReferenceError。