简述 JavaScript 作用域与词法分析

作用域 & 作用域链

在JS中,作用域是通过函数划分的,函数的作用域在定义阶段就已经确定:

  1. 最外层函数,和最外层定义的变量,拥有全局作用域
  2. 未声明,直接赋值的变量,拥有全局作用域
  3. 函数内部定义的变量,拥有局部作用域
  4. 另外,所有的window对象的属性和方法拥有全局作用域,因此我们调用alert()window.alert()效果是一样的。(window对象更多方法,参考JavaScript基础三)

当出现函数嵌套时,就会出现作用域链。解释器在查找变量时,会根据作用域链,从内到外寻找,找到就停止,找不到抛出异常。

AO对象 & 词法分析

JS代码运行分为两个阶段:先进行词法分析,再执行。

一个函数被调用的一瞬间,产生一个活动对象AO(Active Object),词法分析会为该对象填充属性。

词法分析根据作用域链,从外到里进行分析,分为三步:

  1. 分析形参/实参
  2. 分析变量声明(注意,变量的值是在执行时候决定的)
  3. 分析函数声明


下面通过一个例子来说明:

function bar(age) {
        console.log(age);
        var age = 99;
        var sex= 'male';
        console.log(age);

        function age() {
            alert(123)
        }

        console.log(age);
        return 100;
}

bar(5);

/* 运行结果
ƒ age() { alert(123) }
99
99
100 - 返回值
 */

说明:

  1. 调用bar函数的瞬间,生成AO
  2. 分析形参/实参:
    1. 函数声明时的形参,作为AO的属性,默认值undefined,即AO.age = undefined
    2. 接收实参5,给AO.age属性赋值,即AO.age = 5
  3. 分析变量声明:
    1. 如果AO还没有该属性,添加;如果有(相当于重新声明),不执行任何操作,原来的值不会消失
    2. 第3行,找到声明var age,判断已经存在AO.age,不作任何处理
    3. 第4行,找到声明var sex,添加AO.sex = undefined
  4. 分析函数声明:
    1. 如果AO还没有该属性,添加;如果有,则直接覆盖(太霸道了。。。)
    2. 第7行,找到function age() {...}声明,则覆盖原来的AO.age = 5,变为AO.age = function age() {}
  5. 执行过程:
    1. 执行第2行 console.log(age)时,当前AO.age = function age() {…} —— 输出函数
    2. 执行第3行 age = 99时,对AO.age赋值 99
    3. 执行第4行 sex= 'male'时,对AO.sex赋值 male
    4. 执行第5行 console.log(age)时,AO.age = 99 —— 输出99
    5. 注意,第7行function age() {...}函数声明,不进行任何操作,因为词法分析阶段已经完成了。
    6. 执行第11行,console.log(age)时,AO.age还是99 —— 输出99
    7. foo函数返回100

你可能感兴趣的:(javascript)