作用域是一套规则,决定着函数和变量的可访问范围。
全局作用域、函数作用域
var a = '全局作用域变量1';
window.b = '全局作用域变量2';
var c = '全局作用域变量3';
function outFun() {
console.log(c);
var c = '函数作用域变量1';
var d = '函数作用域变量2';
function innerFun() {
console.log(a, b, d);
}
innerFun(); // outFun函数作用域可以访问innerFun
}
outFun(); // 全局作用域可以访问outFun
在以上代码中,输出的a,b就是从全局作用域中找到的,输出的d就是从outFun函数作用域中找到的。
作用域链
查找a, b变量时,会先在innerFun函数作用域中找,如果没有,在外层outFun函数作用域中找,还没有,在外层全局作用域中找,这样一层一层的关系,便形成了作用域链。
作用域链包含了执行环境有权访问的所有变量及其访问顺序。作用域可以嵌套,内部作用域能访问外层作用域;反之则不行,外层作用域无法访问内部作用域。
为什么变量c输出undefined
var c = '函数作用域变量1';
实际分为两步:
var c;
- 编译时,在当前作用域中声明一个变量c,未赋值,此时为undefined。
c = '函数作用域变量1'; - 运行时,找到变量c并赋值。 所以在赋值之前输出,是undefined,这就是我们常说的变量提升。
块级作用域
es6中新增了let,用let定义变量时:
- 形成块级作用域。
- 不会出现变量提升,形成暂时性死区。
- 不允许重复声明。 暂时性死区:一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有执行到声明变量时,才可以获取和使用该变量。
if(e){
console.log(f); // Uncaught ReferenceError: Cannot access 'f' before initialization
let f = '块级作用域变量1';
let f = '块级作用域变量2' // Uncaught SyntaxError: Identifier 'f' has already been declared
}
console.log(f); // Uncaught ReferenceError: f is not defined
JavaScript词法作用域
词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的。当词法分析器处理代码时,作用域会保持不变,也就是词法作用域是静态的作用域。