你来说说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' 避免意外全局变量。
相关推荐
da-peng-song6 分钟前
ArcGIS Desktop使用入门(二)常用工具条——数据框工具(旋转视图)
开发语言·javascript·arcgis
低代码布道师1 小时前
第五部分:第一节 - Node.js 简介与环境:让 JavaScript 走进厨房
开发语言·javascript·node.js
满怀10152 小时前
【Vue 3全栈实战】从响应式原理到企业级架构设计
前端·javascript·vue.js·vue
伟笑2 小时前
elementUI 循环出来的表单,怎么做表单校验?
前端·javascript·elementui
确实菜,真的爱3 小时前
electron进程通信
前端·javascript·electron
魔术师ID4 小时前
vue 指令
前端·javascript·vue.js
Clown955 小时前
Go语言爬虫系列教程 实战项目JS逆向实现CSDN文章导出教程
javascript·爬虫·golang
星空寻流年5 小时前
css3基于伸缩盒模型生成一个小案例
javascript·css·css3
waterHBO7 小时前
直接从图片生成 html
前端·javascript·html
EndingCoder7 小时前
JavaScript 时间转换:从 HH:mm:ss 到十进制小时及反向转换
javascript