作用域,上下文,变量对象一把抓

172 阅读4分钟

前言

JavaScript不同于其他大多数高级语言,比如Java语言有块级作用域,也就是由一个花括号对{……}的位置决定作用域,而在 ES6 之前,Javascript却不是这样的,它使用函数作用域和全局作用域,直到ES6出现之后,才有了块级作用域。

那么今天我们就来了解一下什么是作用域以及作用域链和上下文把,我会用通俗易懂的方式讲出来,不过可能不会太深入毕竟我步入前端也就1年半之久。

变量对象

了解作用域以及上下文前需要先补充一个知识,就是变量对象,你可以将其理解为它储存了上下文中定义的变量以及函数声明。

变量对象三步曲

  • 建立arguments对象。检查当前执行上下文中的参数,建立该对象下的属性与属性值。
  • 检查当前执行上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。 如果该属性之前已经存在,那么该属性将会被新的引用所覆盖。
  • 检查当前执行上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。
VO={
    Arguments:{},//实参
    Param_Variable:具体值,//形参
    Function:<function reference>,//函数的引用
    Variable:undefined//其他变量
}

作用域

作用域有全局作用域,函数作用域,以及ES6的块级作用域,块级和函数可以理解为局部作用域作用域的主要功能是获取上下文的变量对象。

全局作用域

最外层作用域,一开始创建就会有的作用域,保存着全局的变量对象。

函数作用域

函数创建时就会形成作用域,且会将父级乃至全局的可以获取到的上下文变量对象存入到内部的[scope]中。

块级作用域

ES6规定,在某个花括号对{}的内部用let关键字生声明的变量和函数拥有块级作用

  • 这些变量和函数它们只能被花括号对{ }的内部的语句使用,外部不可访问,会形成一个死区
  • 在你写下代码的时候,变量和函数的块级作用域就已经确定下来。块级作用域和函数作用域也可以统称为局部作用域。

词法作用域和动态作用域

词法作用域理解为函数定义时就确定了作用域,调用时作用域不会被改变的。

动态作用域理解为定义时暂时确定作用域,但是调用时作用域才真正确定下来,会被外界影响。

执行上下文

执行上下文分为全局上下文以及局部上下文,上下文的主要作用是为了激活作用域链以及确定变量对象和确定this。

全局上下文

当在浏览器执行时,会将全局上下文推入执行上下文栈的栈底,只有将其他上下文的内容执行完毕后他才会出栈。

局部上下文

也就是函数的上下文,函数执行时会将该函数推入栈中,执行完毕即可出栈。

作用域链

作用域链的作用顾名思义是用来寻找父级以及当前作用域的变量对象,那么他是怎么形成的呢?

  • 已知函数创建时会将父级能够获取到的变量对象存入到自己的[scope]中
  • 函数执行时才会将自己的作用域的变量对象推入[scope]中
  • 作用域就是获取上下文变量对象,而上下文是确定变量对象,所以函数需要执行才能触发作用域链最后一步
  • 最终形成作用域链
{
    Scope: [
        { //当前作用域对应的VO
            实参,
            形参,
            变量,
            函数
        }, 
        { //第二个作用域对应的VO
            实参,
            形参,
            变量,
            函数
        },
        ... 
        { //全局作用域对应的VO
            变量,
            函数
        }
    ]
}

over 明天会更好

本文使用 mdnice 排版