# 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的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。
作用域和执行上下文之间最大的区别是: 执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。
一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。