作用域链

# 1.自由变量

当前作用域中没有的变量,成为自由变量。自由变量通过向父作用域查找而得到(并不严谨)。

var a = 100
function fn() {
    var b = 200
    console.log(a) // 当前作用域并不存在这个a ,这里的a在这里就是一个自由变量
    console.log(b)
}
fn()

# 2.作用域链

如果父级也没呢?再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是 作用域链 。

TIP

个人理解: 在函数内部,当我们访问一个变量的时候,这个变量既不是用var声明的也不是函数的参数(即为自由变量),那么我们就到函数的父作用域上去找这个变量, 如果父作用域也不存在,就继续向上查找,直到找到window。这种一层一层向上寻找的机制被成为作用域链。

# 3.自由变量的取值

var x = 10
function fn() {
  console.log(x)
}
function show(f) {
  var x = 20;
  (function() {
    f() //10,而不是20
  })()
}
show(fn)

在fn函数中,取自由变量x的值时,要到创建fn函数的那个作用域中取,无论fn函数将在哪里调用。

要到创建这个函数的那个域”,作用域中取值,这里强调的是“创建”,而不是“调用”,切记切记——其实这就是所谓的"静态作用域"。

var a = 10
function fn() {
  var b = 20
  function bar() {
    console.log(a + b) //30
  }
  return bar
}
var x = fn(),
  b = 200
x() //bar()

fn()返回的是bar函数,赋值给x。执行x(),即执行bar函数代码。取b的值时,直接在fn作用域取出。取a的值时,试图在fn作用域取,但是取不到,只能转向创建fn的那个作用域中去查找,结果找到了,所以最后的结果是30。

注意:函数中自由变量的取值,要到函数创建的那个作用域中找,而不是调用的那个作用域。

# 4.执行上下文

JavaScript属于解释型语言,JavaScript的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样。

解释阶段:

  • 词法分析
  • 语法分析
  • 作用域规则确定

执行阶段:

  • 创建执行上下文
  • 执行函数代码
  • 垃圾回收

TIP

JavaScript解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是this的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。

作用域和执行上下文之间最大的区别是: 执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。

一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。

Last Updated: 12/10/2020, 6:16:00 PM