你来说说JavaScript作用域

大家好,我是有一点想法的thinkmars,目前在准备面试与工作,借着间隙时间学习复习,写一点基础文章,欢迎想找工作的人与我一起学习,一起讨饭吃~


在 JavaScript 中,作用域(Scope)是决定变量和函数可访问性的核心机制。

一、作用域核心概念

  1. 定义

    作用域是变量和函数的可访问范围,决定了代码中哪些部分可以访问某个标识符。

  2. 词法作用域 vs 动态作用域

    • 词法作用域(Lexical Scope):JS 采用词法作用域,作用域在代码书写时确定(如函数定义的位置)。
    • 动态作用域 :作用域在代码运行时确定(如 this 的绑定)。

二、作用域类型

1. 全局作用域(Global Scope)

  • 在函数或代码块外声明的变量,全局可访问。
  • 浏览器中,全局对象是 window;Node.js 中是 global
javascript 复制代码
var globalVar = "I'm global";
console.log(window.globalVar); // 浏览器输出: "I'm global"

2. 函数作用域(Function Scope)

  • var 声明的变量具有函数作用域。
  • 在函数内部声明的变量,外部无法访问。
javascript 复制代码
function foo() {
  var localVar = "I'm local";
}
console.log(localVar); // 报错:localVar未定义

3. 块级作用域(Block Scope)

  • ES6 引入 letconst,支持块级作用域({} 内有效)。
javascript 复制代码
if (true) {
  let blockVar = "I'm block-scoped";
  const PI = 3.14;
}
console.log(blockVar); // 报错:blockVar未定义

三、作用域链(Scope Chain)

  1. 定义

    函数执行时,会从当前作用域逐层向外查找变量,形成作用域链。

  2. 创建过程

    • 函数定义时,会保存其外层作用域的引用(即 [[Scopes]] 属性)。
    • 执行时,将当前变量对象添加到作用域链前端。
    javascript 复制代码
    function outer() {
      let a = 1;
      function inner() {
        console.log(a); // 通过作用域链找到 outer 中的 a
      }
      inner();
    }
    outer(); // 输出 1

四、闭包(Closure)

  1. 定义

    函数与其词法环境的引用捆绑,即使函数在其词法作用域外执行,仍能访问外部变量。

  2. 经典面试题

    javascript 复制代码
    for (var i = 0; i < 5; i++) {
      setTimeout(() => console.log(i), 100); // 输出 5 五次
    }
    // 解决方案:使用 let 或闭包
    for (let i = 0; i < 5; i++) {
      setTimeout(() => console.log(i), 100); // 输出 0,1,2,3,4
    }

五、变量提升(Hoisting)

  1. var 的变量提升
    var 声明会被提升到作用域顶部,赋值保留在原地。

    javascript 复制代码
    console.log(hoistedVar); // undefined
    var hoistedVar = "Hoisted!";
  2. let/const 的暂时性死区(TDZ)
    letconst 存在暂时性死区,声明前访问会报错。

    javascript 复制代码
    console.log(tdzVar); // 报错:Cannot access 'tdzVar' before initialization
    let tdzVar = "TDZ!";

六、面试高频问题

  1. 解释以下代码的输出:

    javascript 复制代码
    let a = 1;
    function outer() {
      console.log(a);
      let a = 2;
    }
    outer(); // 报错:Cannot access 'a' before initialization

    答案 :块级作用域内 let 声明导致暂时性死区。

  2. 如何用闭包实现私有变量?

    javascript 复制代码
    function Counter() {
      let count = 0;
      return {
        increment: () => count++,
        getCount: () => count
      };
    }
    const counter = Counter();
    counter.increment();
    console.log(counter.getCount()); // 1

七、总结与注意事项

  1. 优先使用 let/const:避免变量提升和全局污染。
  2. 闭包内存泄漏:及时解除不再使用的闭包引用。
  3. 严格模式 :使用 'use strict' 避免意外全局变量。
相关推荐
mCell4 小时前
JavaScript 运行机制详解:再谈 Event Loop
前端·javascript·浏览器
amy_jork6 小时前
npm删除包
开发语言·javascript·ecmascript
max5006008 小时前
基于桥梁三维模型的无人机检测路径规划系统设计与实现
前端·javascript·python·算法·无人机·easyui
我命由我123459 小时前
软件开发 - 避免过多的 if-else 语句(使用策略模式、使用映射表、使用枚举、使用函数式编程)
java·开发语言·javascript·设计模式·java-ee·策略模式·js
萌萌哒草头将军9 小时前
Node.js v24.6.0 新功能速览 🚀🚀🚀
前端·javascript·node.js
AALoveTouch10 小时前
大麦APP抢票揭秘
javascript
持久的棒棒君11 小时前
启动electron桌面项目控制台输出中文时乱码解决
前端·javascript·electron
小小愿望13 小时前
移动端浏览器中设置 100vh 却出现滚动条?
前端·javascript·css
烛阴14 小时前
TypeScript 接口入门:定义代码的契约与形态
前端·javascript·typescript
掘金安东尼14 小时前
使用自定义高亮API增强用户‘/’体验
前端·javascript·github