你来说说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' 避免意外全局变量。
相关推荐
WTSolutions3 分钟前
Image Pixel RGBA Extractor:免费在线图像像素色彩提取工具全解析
javascript
轻语呢喃9 分钟前
Cookie详解:从原理到实战,彻底搞懂用户身份识别机制
javascript·http·html
旺旺大力包11 分钟前
【JS笔记】JS 和 noodjs 的常见操作(十)
开发语言·javascript·node.js·ecmascript
前端进阶者15 分钟前
地图坐标系转换JS库
前端·javascript
夏梦春蝉1 小时前
ES6从入门到精通:其他特性
前端·javascript·es6
csdn_aspnet1 小时前
在 React 中使用 WebSockets 构建实时聊天应用程序
javascript·react.js·node.js
【ql君】qlexcel1 小时前
Notepad++ 复制宏、编辑宏的方法
开发语言·javascript·notepad++··宏编辑·宏复制
就改了2 小时前
Ajax——在OA系统提升性能的局部刷新
前端·javascript·ajax
凌冰_2 小时前
Ajax 入门
前端·javascript·ajax
奋飛3 小时前
TypeScript系列:第六篇 - 编写高质量的TS类型
javascript·typescript·ts·declare·.d.ts