作用域
什么是作用域
作用域其实就是看代码在执行过程中某个区域内是否能访问到某个变量、函数或者对象。
举个栗子
function foo() {
var a = 10;
console.log(a); // 10
}
foo() // 函数调用
console.log(a); // Uncaught ReferenceError: a is not defined
从上面这个例子我们可以看到,函数调用后,函数里面输出10,然而变量a
在函数外并没有声明,函数执行后,外面并不能访问到a
,那么结果就会报错。因此,我们可以理解为,作用域是为了将变量进行隔离,防止其对全局环境造成污染
在 ES6 之前,我们说js只有两种作用域,全局作用域和函数作用域,而对着 ES6 的到来,为我们提供了另一种作用域,即块级作用域(需通过 let
或 const
来声明)。
全局作用域
在代码的任何地方都能访问到的变量拥有全局作用域
window 对象的属性具有全局作用域
console.dir(alert);
console.dir(clearInterval)
fn()
function fn() {
console.dir(alert);
console.dir(clearInterval)
function foo() {
(function () {
console.dir(alert);
console.dir(clearInterval)
})()
}
foo()
}
定义在全局的变量拥有全局作用域
var a = 0;
function fn() {
console.log(a); // 0
function foo() {
(() => {
console.log(a); // 0
})(); // IIFE
}
foo() // 函数调用
}
fn() // 函数调用
上面代码我们可以看出,变量a通过var
关键字在代码最外层声明(var关键字在全局下声明的变量会被挂在window上),并且在任何地方都能被访问到,因此变量a
具有全局作用域。
未声明直接赋值的变量具有全局作用域
function fn() {
function foo() {
(function () {
b = 100;
})() // IIFE
console.log(b); // 100
}
foo() // 函数调用
console.log(b); // 100
}
fn() // 函数调用
console.log(b); // 100
变量b
并没有通过任何关键字声明,在函数里直接被赋值100,函数调用后,变量b
被挂在全局对象window上,在全局任何地方都能被访问,因此变量b
拥有全局作用域。
函数作用域
只能在函数内部访问的变量拥有函数作用域
function foo() {
var a = 1000000;
(function () {
var b = 999;
console.log(a); // 1000000
})()
// console.log(b); // Uncaught ReferenceError: a is not defined
}
foo()
console.log(a); // Uncaught ReferenceError: a is not defined
比如最开始的例子和上面这个例子,变量a
和变量b
始终只能在其声明时的函数fn内不进行访问,在函数外就会报错,因此变量a
和b
拥有函数作用域。
块级作用域
块级作用域是 ES6 增加的一种作用域,通过
let
和const
声明的变量会产生块级作用域;
{
let a = 5;
const b = 6;
var c = 7;
console.log(a); // 5
console.log(b); // 6
console.log(c); // 7
}
// console.log(a); // Uncaught ReferenceError: a is not defined
// console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // 7
在 { } 之中声明的变量,用 let 和 const 声明的变量拥有局部作用域 在 { } 外部不能被访问,而通过var声明的则被挂在了全局对象window上,成为了全局变量可以在任何地方被访问。
举个栗子
for (var a = 0; a < 5; a++) {
// console.log(a);
}
console.log(a); // 5
if (true) {
var b = 111111;
}
console.log(b); // 111111
我们可以看到,最终输出 a 是 5,b 是 111111,并且a和b都被挂在了全局对象window上,这显然并不是我们想看到的结果。
因此我们可以改用let
关键字来定义这些变量
for (let a = 0; a < 5; a++) {
// console.log(a);
}
// console.log(a); // Uncaught ReferenceError: a is not defined
if (true) {
let b = 111111;
const c = 2222;
}
// console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // Uncaught ReferenceError: b is not defined
因为使用了let
或const
关键字,所以这些变量产生了块级作用域,使其在声明时的 {} 外面无法被访问,从而解决一些不必要的麻烦。