聊一聊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
    • fn1fn2 也被定义在全局作用域。
  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 访问 fn2a

需要改变 fn1 的作用域链,比如:

javascript 复制代码
var a = 10;

function fn2() {
  var a = 20;
  function fn1() {  // fn1 现在定义在 fn2 内部
    console.log(a);  // 此时 a 是 fn2 的局部变量 20
  }
  fn1();
}

fn2();  // 输出 20

此时 fn1 的作用域链是:fn1fn2 → 全局,所以它能访问 fn2a

总结

  • JavaScript 的作用域是静态的(词法作用域),函数的作用域在定义时就已经固定,不会因为调用位置而改变。
  • fn1 定义在全局,所以它只能访问全局变量 a = 10,而不会受到 fn2 的局部变量影响。
  • 如果希望 fn1 访问 fn2 的变量,必须让 fn1fn2 内部定义(形成闭包)。

变通

js 复制代码
var a = 10;

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

fn2();

结果是20

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

相关推荐
奕辰杰1 小时前
关于npm前端项目编译时栈溢出 Maximum call stack size exceeded的处理方案
前端·npm·node.js
JiaLin_Denny3 小时前
如何在NPM上发布自己的React组件(包)
前端·react.js·npm·npm包·npm发布组件·npm发布包
路光.4 小时前
触发事件,按钮loading状态,封装hooks
前端·typescript·vue3hooks
我爱996!4 小时前
SpringMVC——响应
java·服务器·前端
咔咔一顿操作5 小时前
Vue 3 入门教程7 - 状态管理工具 Pinia
前端·javascript·vue.js·vue3
kk爱闹5 小时前
用el-table实现的可编辑的动态表格组件
前端·vue.js
漂流瓶jz6 小时前
JavaScript语法树简介:AST/CST/词法/语法分析/ESTree/生成工具
前端·javascript·编译原理
换日线°6 小时前
css 不错的按钮动画
前端·css·微信小程序
风象南6 小时前
前端渲染三国杀:SSR、SPA、SSG
前端
90后的晨仔6 小时前
表单输入绑定详解:Vue 中的 v-model 实践指南
前端·vue.js