HXWfromDJTU/blog

Javascript - 作用域链与[[scope]]

Opened this issue · 0 comments

前言

执行环境 ➡️ 作用域链 ➡️ 变量对象

每个执行环境都有一个[ [scope] ]变量,指向对象的的作用域链,作用域链上的每个栏位都存放着对应的函数空间的变量对象。

执行环境

  1. 函数在创建的时候,会创建一个执行环境(excution context)对内部对象,他定义了一个函数执行时的环境

  2. 函数每次执行时,执行环境都是独一无二的,多次调用就有多个执行环境。每个人执行环境都有自己的作用域链,用于解析标志符

  3. 每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量函数都保存在这个对象中

  4. 全局环境是最外围的一个执行环境,随着ECMAScript的真实环境变化而变化,常见的是globalwindow

  5. 某个执行中的代码执行完毕,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。

[ [scope] ]

  1. [ [scope] ]和执行期上下文虽然保存的都是作用域链,但是不相同。
  2. [ [scope] ]属性在函数创建的时候产生,会一直存在
  3. 执行上下文在函数执行的时候才产生,函数执行完后便销毁

作用域栈

每个函数都有自己的执行环境,每当执行流进入一个函数时,当前函数的环境就会被推入一个环境栈中。当函数执行完后,栈将其环境弹出,把控制权返回给外层执行环境。

作用域链

  • 开始执行的时候,作用域链上只有全局对象,并保存在内部属性[[SCOPE]]上。
  • 调用某个函数的时候,会取出之前存有全局对象的[[scope]],并把当前函数的活动对象,追加到取出的全局对象中,形成新的作用域链,再次存入[[SCOPE]]
  • 代码在一个环境中执行时,会创建变量对象的一个作用域链。

始终记住 Javascript 没有块级作用域

在ES6的let之前,javascript是没有块级作用域的

if(false){
    var abc = 123;
}
console.log(abc)

使用var的abc声明提升到了作用域顶端,这里的所谓if作用域是不存在的,其实就是�外层的顶级作用域。

实例理解

var name = "Andy";

function changeGlobalName(){
    let innerName = "innerName"
    name = "Jacky"
    function changeInnerName(){
         let tempName = "innerInnerName";
         innerName = "changeByInnerInner"
    }
    changeInnerName()
}
changeGlobalName()

执行环境作用域链

window (name changeGloalName)
     ⬆️                             ⬆️
changeGlobalName (arguments inner changeInnerName)
     ⬆️                             ⬆️
changeInnerName (arguments tempName)
     ⬆️

  • 每个环境都可以向上搜索作用域链,以查询变量和函数名。但任何环境都不能通过向下:arrow_down:搜作用域链进入另一个执行环境

小结

  1. 一般来说Javascript的作用域只有:全局作用域函数作用域
    还有两种不常使用的withcatch 语句。在严格模式下,with是被禁止使用的。
  2. 每次进入一个新的执行环境,都会创建一个用于搜索变量的函数和函数的作用域链。
  3. 函数的局部环境不仅有权访问函数作用域的变量,而且有权访问其父环境,乃至全局环境。
  4. 全局环境只能够访问全局环境中定义的 变量函数,而不能直接访问局部环境中的任何数据;
  5. 变量的执行环境有助于确定该变量何时应该被内存释放。

参考资料

[1] JavaScript内部属性 Scope 与作用域链及其性能问题 - CSDN

[2] 稳扎稳打Javascript - by 柴毛毛

[3] Javascript高级程序设计 - 第三版