likes
comments
collection
share

Javascript词法作用域&变量对象

作者站长头像
站长
· 阅读数 56

作用域

作用域是指程序源代码中定义变量的区域。作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。

JavaScript 采用词法作用域(lexical scoping),也就是静态作用域,也就是说它由 函数声明时 所在的位置决定的,他在函数声明时就已经确认了,而上一篇我们提到的执行上下文在函数被执行时才会生成,也就是说作用域决定了执行上下文中访问变量的规则,限制访问的范围,执行上下文在作用域决定的访问权下查找变量、函数等,执行上下文中就保存了作用域的访问路径——作用域链、变量对象和this。

静态作用域

我们来看下面这个简单的例子

var value = 1;
​
function func1() {
    console.log(value);
}
​
function func2() {
    var value = 2;
    func1();
}
​
func2();

我们来简单分析下执行过程:

函数声明时,就生成了一个全局作用域和两个函数的局部作用域,但是作用域并没有决定它里面有什么,只是划了自己的地盘,但是当代码执行到func2函数时,创建func1执行上下文,并执行fuc1函数,创建func2的执行上下文此时各个执行上下文中便保存了各自的作用域链

此时func1函数执行上下文内部查找是否有局部变量 value,可以看到并没有,如果没有,就根据书写的位置,查找上面一层的代码,可以看到func1Scop和func2Scop属于同一级别的作用域,尽管func2Scop中也有value但是func1无法从func2的作用域中获取到value,他会找到globalScop中的value,也就是 value 等于 1,所以结果会打印 1。

变量对象

变量对象时执行上下文中管理的数据作用域,存储了在上下文中定义的变量和函数声明。变量对象可以理解为仅仅是在规范上的一中定义,无法被真正访问到,而函数运行,创建了一个执行上下文,此时变量对象才能被真正访问到,此时我们称之为活动对象(activation object, AO),它通过函数的 arguments 属性初始化。arguments 的属性值是 Arguments 对象。

在此之前我们先了解一个概念,叫全局对象(Global Object)。在 W3School 有这样一段介绍:

全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。

在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。因为全局对象是作用域链的头,这意味着所有非限定性的变量和函数名都会作为该对象的属性来查询。

例如,当JavaScript 代码引用 parseInt() 函数时,它引用的是全局对象的 parseInt 属性。全局对象是作用域链的头,还意味着在顶层 JavaScript 代码中声明的所有变量都将成为全局对象的属性。

在浏览器中,全局对象就是Windows对象,我们可以在全局代码中用this访问到它,并且他预定义了相当多的对象、函数和属性。我们还可以发现,在浏览器中的全局对象this的windows属性是指向自己的,其实就是说全局上下文的变量对象其实就是全局对象,保存了所有在顶层 JavaScript 代码中定义的对象、函数和属性。

而刚才提到的活动对象就是函数在执行上下文中被激活的变量对象。上一篇有提到,当代码运行到执行函数的语句时会先创建执行上下文,执行上下文中的会创建活动对象,活动对象包含函数传入的形参、函数内部的函数声明和变量声明,用下面的例子举例

var value = 1;
​
function func1(value) {
    console.log(value);
}
​
function func2() {
    var value = 2;
    func1(value);
}
​
func2();
  1. 首先创建全局对象,GO除预定义的属性外还包括

    GO = {
        value : undefined,
        func1: reference to function func1(){},
        func2 reference to function func2(){}
    }
    
  2. 全局对象创建完毕后代码开始运行,创建全局上下文栈,并且激活变量对象,顺序执行代码也就会修改变量对象中属性的值,当代码运行到最后,全局对象中的活动变量是这个样子

    GO = {
        value : 1,
        func1: reference to function func1(){},
        func2 reference to function func2(){}
    }
    
  3. 当代码运行到执行 func2 函数的语句时,同样会创建他的变量对象:

    AO = {
        arguments: {},
        value : undefined
    }
    

    创建 func2 函数的执行上下文后,执行代码码、修改比那辆对象内属性的值

  4. 运行 func1 之前同样创建 func2 函数的变量对象,因为此时有形参value,因此他的变量对象的arguments属性中是有内容的:

    AO = {
        arguments: {
            value: 1
        }
    }
    

总结

今天我们了解了执行上下文中的一个成员成员——变量对象,准确的说在执行上下文中的应该是活动变量。了解了变量声明和赋值的过程,上面我们提到的作用域,规定了变量查找的规则,那么它是如何起作用的呢,就关系到执行上下文中的作用域链了,会在下一篇文章中一起学习。最后再打个广告,关注公众号程序猿青空,免费领取191本计算机领域黑皮书电子书,更有集赞活动免费挑选精品课程(各个领域的都有),不定期分享各种优秀文章、学习资源、学习课程,能在未来(因为现在还没啥东西)享受更多福利。

转载自:https://juejin.cn/post/7197034140486844476
评论
请登录