聊一聊js的全局作用域和局部作用域

题目

js 复制代码
var a = 10;

function fn1(){
  console.log(a)
}

function fn2() {
  var a = 20;
  fn1();
}

fn2();

结果是10


代码解释

这段代码的输出结果是 10,而不是 20,这是由 JavaScript 的作用域链(Scope Chain)和词法作用域(Lexical Scoping) 决定的。

代码执行流程

  1. 全局作用域:

    • var a = 10 声明了一个全局变量 a,值为 10。
    • fn1 和 fn2 也被定义在全局作用域。
  2. 调用 fn2():

    • 进入 fn2 的执行上下文,var a = 20 声明了一个 局部变量 a (只属于 fn2 的作用域)。
    • 调用 fn1()。
  3. 调用 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

每日一句:说话是一件靠自己用点心,就可以不断进步的事。而把话说好,收获最大,当然也是我们自己。

相关推荐
深蓝AI8 小时前
Meta 开源用了八年的设计系统 astryx:连坐两天 GitHub 总榜第一,组件库开始写给 agent 的说明书
前端·人工智能
IT_陈寒8 小时前
Vue的watch把我坑惨了,原来要这样用才靠谱
前端·人工智能·后端
1-1=08 小时前
Vue 3 模板语
前端·javascript·vue.js
用户7610304473719 小时前
【转战智能眼镜开发】ROKID AIUI 小游戏 亚马逊标枪项目实战讲解
前端
风卿9 小时前
看着AI写代码太孤独了,来欣赏星云吧:一个DSH 装饰插件
前端·javascript
牧艺9 小时前
弹层不用再手算坐标了:CSS Anchor Positioning 从原理到可上线
前端·javascript·css
用户老李9 小时前
YOLO 目标检测自学教程 07:YOLOv3——多尺度预测
前端
moshuying10 小时前
ESP32 墨水屏金价牌(上):从点亮到把图片推过去
前端
李剑一10 小时前
别再抬杠,前端岗已经没了!无论前后端,我们最终都要成“全干”工程师
前端·面试
Highcharts.js10 小时前
Highcharts 仪表盘与速度仪图表库介绍
前端·信息可视化·仪表板·highcharts·图表开发·图表库·仪表可视化