几句话讲作用域

75 阅读2分钟

作用域是一套规则决定着函数和变量的可访问范围

全局作用域、函数作用域

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词法作用域

词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的。当词法分析器处理代码时,作用域会保持不变,也就是词法作用域是静态的作用域。