作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。
作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
一、全局作用域和函数作用域
1. 全局作用域
全局作用域在页面打开时创建,在页面关闭时销毁,全局作用域中声明的变量和函数将会作为 window 对象的属性和方法保存。
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
- 最外层函数和在最外层函数外面定义的变量拥有全局作用域;
- 所有末定义直接赋值的变量自动声明为拥有全局作用域;
- 所有 window 对象的属性拥有全局作用域。
var name = 'akl';
age = 23;
function get() {
console.log(name); // akl
console.log(age); // 23
}
get();
console.log(window); // Window
注:严格意义的全局变量都属于 window 对象的属性,但 let 和 const 声明的变量并不属于 window 对象,所以它们并不是严格意义上的全局变量,在此仅仅从它们的作用域这个角度来说它们是全局变量的。
2. 函数作用域
在 function 中声明的变量具有函数作用域,函数作用域中的变量只能在当前函数的一对花括号({}) 中访问。
函数作用域在调用函数时创建,函数执行完毕时销毁,访问变量由内而外。
作用域是分层的,内层作用域可以访问外层作用域的变量,反之则不行。
3. 函数作用域的预编译
(1) 创建对象 AO;
(2) 找形参和变量声明,将形参和变量声明作为 AO 对象的属性名,值为 undefined;
(3) 实参和形参相统一;
(4) 在函数体里找函数声明,将值赋给函数体。
function fn(a, c) {
console.log(a) // function a(){}
var a = 123;
console.log(a); // 123
console.log(c); // function c() {}
function a() {}
if (false) {
var d = 678;
}
console.log(d) // undefined
console.log(b) // undefined
var b = function() {}
console.log(b) // function() {}
function c() {}
console.log(c) // function c() {}
}
fn(1, 2);
AO对象:
AO {
a: undefined 1 function a() {}
b: undefined
c: undefined 2 function c() {}
d: undefined
}
4. 全局作用域的预编译
(1) 创建对象 GO GO{};
(2) 找变量声明,将变量声明作为 GO 对象的属性名,值为 undefined;
(3) 找函数声明,将值赋给函数体。
二、块级作用域
使用 let 和 const 声明的变量具有块级作用域,在指定块的作用域外无法被访问。块级作用域在如下情况被创建:
- 在一个函数内部;
- 在一个代码块(由一对花括号({})包裹)内部。
值得注意的是:块语句(花括号({})中间的语句),如 if 和 switch 条件语句或 for 和 while 循环语句中,使用 var 声明的变量没有块级作用域,在块语句中使用 var 定义的变量将保留在它们已经存在的作用域中。
if (true) {
var a = 100;
let b = 200;
}
console.log(a); // 100
console.log(b); // 报错:ReferenceError: b is not defined
if (false) {
var c = 300;
const d = 400;
}
console.log(c); // 300
console.log(d); // 报错:ReferenceError: d is not defined