题目
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
每日一句:说话是一件靠自己用点心,就可以不断进步的事。而把话说好,收获最大,当然也是我们自己。