题目
js
var a = 10;
function fn1(){
console.log(a)
}
function fn2() {
var a = 20;
fn1();
}
fn2();
结果是10
代码解释
这段代码的输出结果是 10
,而不是 20
,这是由 JavaScript 的作用域链(Scope Chain)和词法作用域(Lexical Scoping) 决定的。
代码执行流程
-
全局作用域:
var a = 10
声明了一个全局变量a
,值为10
。fn1
和fn2
也被定义在全局作用域。
-
调用
fn2()
:- 进入
fn2
的执行上下文,var a = 20
声明了一个 局部变量a
(只属于fn2
的作用域)。 - 调用
fn1()
。
- 进入
-
调用
fn1()
:fn1
内部访问a
,但fn1
自身没有定义a
,于是 沿着作用域链向上查找。fn1
是在 全局作用域 中定义的,所以它的作用域链是:fn1
的局部作用域 → 全局作用域。
- 它不会访问
fn2
的局部变量a
(因为fn2
的作用域不在fn1
的作用域链上)。 - 最终找到的是 全局变量
a
(值为10
) ,所以输出10
。
关键点:词法作用域(Lexical Scoping)
JavaScript 的作用域是 静态(词法)作用域 ,即 函数的作用域在定义时就已经确定,而不是在执行时。
fn1
是在全局作用域定义的,所以它只能访问:- 自己的局部变量(这里没有)。
- 全局变量(
a = 10
)。
- 它 不会 访问
fn2
的局部变量a = 20
,因为fn2
的作用域不是fn1
的父级作用域。
如果想让 fn1
访问 fn2
的 a
?
需要改变 fn1
的作用域链,比如:
javascript
var a = 10;
function fn2() {
var a = 20;
function fn1() { // fn1 现在定义在 fn2 内部
console.log(a); // 此时 a 是 fn2 的局部变量 20
}
fn1();
}
fn2(); // 输出 20
此时 fn1
的作用域链是:fn1
→ fn2
→ 全局,所以它能访问 fn2
的 a
。
总结
- JavaScript 的作用域是静态的(词法作用域),函数的作用域在定义时就已经固定,不会因为调用位置而改变。
fn1
定义在全局,所以它只能访问全局变量a = 10
,而不会受到fn2
的局部变量影响。- 如果希望
fn1
访问fn2
的变量,必须让fn1
在fn2
内部定义(形成闭包)。
变通
js
var a = 10;
function fn2() {
var a = 20;
function fn1(){
console.log(a)
}
fn1();
}
fn2();
结果是20
每日一句:说话是一件靠自己用点心,就可以不断进步的事。而把话说好,收获最大,当然也是我们自己。