当前位置: 首页 > article >正文

【前端】JavaScript 变量声明与函数提升例题分析:深入理解变量提升、作用域链与函数调用


在这里插入图片描述

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]
本文专栏: 前端

文章目录

  • 💯前言
  • 💯第一题:变量和函数声明的提升与函数内局部作用域
    • 题目代码
    • 代码解析与预解析结果
      • 1. 全局作用域的预解析
      • 2. 函数作用域 `fn` 的预解析
      • 代码执行流程
      • 最终输出
  • 💯第二题:函数声明与变量赋值的相互覆盖
    • 题目代码
    • 代码解析与预解析结果
      • 预解析步骤
      • 代码执行过程
      • 最终输出
  • 💯第三题:函数作用域链与调用时作用域的区别
    • 题目代码
    • 作用域链与调用过程解析
      • 预解析步骤
      • 代码执行过程
      • 最终输出
    • 深入理解:为什么不会访问 `fn2` 中的变量?
      • 如何模拟访问局部作用域?
  • 💯总结与扩展:JavaScript 中的作用域与变量管理
    • 1. 变量提升与函数提升的对比
    • 2. 作用域链是静态的
    • 3. 块作用域与变量声明
    • 4. 函数表达式与函数声明
  • 💯小结


在这里插入图片描述


💯前言

  • JavaScript 中,变量声明的提升函数声明的提升以及作用域链是非常重要的概念。理解它们可以帮助我们更好地理解代码的执行顺序以及变量的可见性,避免在编码过程中出现一些难以理解的错误
    在本文中,我们将通过三个示例代码逐步讲解这些核心概念,确保你能够全面深入地理解它们
    JavaScript在这里插入图片描述


💯第一题:变量和函数声明的提升与函数内局部作用域

在这里插入图片描述


题目代码

在这里插入图片描述

var x = 10;
function fn() {
  x = 20;
  console.log(x); // 这里打印值是多少?
  var x = 30;
}
x = 30;
fn();
console.log(x); // 这里打印值是多少?

代码解析与预解析结果

在这里插入图片描述
在 JavaScript 执行代码之前,会先进行 预解析 ,把变量和函数声明提升到当前作用域的顶部。在这段代码中,我们有两个作用域需要考虑: 全局作用域 和 函数作用域 fn


1. 全局作用域的预解析

  • 提升函数声明 fn
  • 声明 var x,初始值为 undefined

在预解析后,全局代码的结构为:

var x; // 声明提升,初始值 undefined
function fn() { ... } // 函数声明提升
x = 10; // 赋值为 10
x = 30; // 赋值为 30
fn();
console.log(x);

在这里插入图片描述


2. 函数作用域 fn 的预解析

fn 函数中:

  • 提升 var x,此时 x 的初始值为 undefined
  • 赋值 x = 20console.log(x) 的执行在变量声明之后。
    在这里插入图片描述
    所以,函数内部的代码结构如下:
function fn() {
  var x; // 提升,初始值为 undefined
  x = 20; // 赋值操作
  console.log(x); // 打印 20
  x = 30; // 赋值操作
}

代码执行流程

  1. 全局变量 x 初始值为 10,后被赋值为 30
  2. 调用 fn 函数时,由于 fn 内部的 x 变量声明提升,导致 x 的初始值为 undefined,在执行 x = 20 后,console.log(x) 打印的是 20
  3. 全局作用域中的 console.log(x) 打印 30
    在这里插入图片描述

最终输出

  • fn 内的 console.log(x): 输出 20
  • 全局的 console.log(x): 输出 30
    在这里插入图片描述


💯第二题:函数声明与变量赋值的相互覆盖

在这里插入图片描述


题目代码

在这里插入图片描述

console.log(x); // 这里打印值是多少?
var x = function () {
  x = 90;
};
x();
var x = 30;

function x() {
  x = 40;
}

console.log(x); // 这里打印值是多少?

代码解析与预解析结果

在这里插入图片描述


预解析步骤

在这里插入图片描述

  1. 全局作用域提升:
  • 函数 x 的声明提升到顶部。
  • 变量 var x 提升,初始值为 undefined

预解析后的代码结构:

var x; // 声明提升,初始值为 undefined
function x() {
  x = 40;
}
console.log(x);//函数体
x = function () {
    x = 90;
}
x();
x = 30;
console.log(x);//30

代码执行过程

在这里插入图片描述

  1. 第一次 console.log(x): 打印函数体,因为函数 x 的声明优先级高于变量声明,且 x 是函数。
  2. x = function () {...}: 重写了变量 xx 不再是函数,而是一个匿名函数。
  3. x(): 调用匿名函数,将 x 赋值为 90
  4. x = 30: 重新将 x 赋值为 30
  5. 第二次 console.log(x): 输出 30

最终输出

  • 第一次 console.log(x): 打印函数体。
  • 第二次 console.log(x): 输出 30
    在这里插入图片描述


💯第三题:函数作用域链与调用时作用域的区别

在这里插入图片描述


题目代码

在这里插入图片描述

var x = 20;
function fn1() {
  console.log(x); // 这里打印值是多少?
}
x = 30;
function fn2() {
  var x = 1;
  fn1();
}
x = 40;
fn2();

作用域链与调用过程解析

在这里插入图片描述
在 JavaScript 中,函数的作用域链在函数定义时就已经确定,而不是在函数调用时动态改变的。这意味着 fn1 的作用域链不会包含 fn2 的局部作用域 ,即使 fn1 是在 fn2 中调用的。


预解析步骤

在这里插入图片描述

  1. 全局作用域提升:
  • 函数 fn1fn2 提升到顶部。
  • 变量 var x 提升,初始值为 undefined

预解析后的代码结构:

var x; // 提升
function fn1() {
  console.log(x);
}
function fn2() {
  var x; // 提升
  x = 1;
  fn1();
}
x = 20;
x = 30;
x = 40;
fn2();

代码执行过程

在这里插入图片描述

  1. x = 20 ,然后更新为 30
  2. x = 40 ,全局变量 x 最终值为 40
  3. 调用 fn2()
  • fn2 内,声明并赋值局部变量 x = 1
  • 调用 fn1() 时,fn1 的作用域链中没有 fn2 的局部变量,只有全局变量。
  • 因此,fn1 中的 console.log(x) 打印全局变量的值,即 40

最终输出

  • fn1 内的 console.log(x): 输出 40,因为作用域链是静态的,函数 fn1 只能访问定义时所在的全局变量。
    在这里插入图片描述

深入理解:为什么不会访问 fn2 中的变量?

  • 作用域链是静态的 :当函数 fn1 定义时,它的作用域链已经被锁定,只包含全局作用域,而 fn2 的局部作用域不会成为 fn1 的一部分。
  • 调用栈与作用域链的区别 :调用栈记录了函数的调用顺序,但不会影响作用域链的解析。
    在这里插入图片描述

如何模拟访问局部作用域?

  1. 传递参数 :将 fn2 的局部变量作为参数传递给 fn1
function fn1(x) {
  console.log(x);
}
function fn2() {
  var x = 1;
  fn1(x); // 传递参数
}
fn2(); // 输出 1
  1. fn2 中重新定义 fn1 :使 fn1 成为 fn2 的内部函数,这样它就能访问 fn2 的局部变量。
function fn2() {
  var x = 1;
  function fn1() {
    console.log(x); // 可以访问 `fn2` 的局部变量
  }
  fn1();
}
fn2(); // 输出 1

在这里插入图片描述



💯总结与扩展:JavaScript 中的作用域与变量管理

在这里插入图片描述


1. 变量提升与函数提升的对比

  • 变量提升 :var 声明的变量会被提升,但初始化仍在原位置。
  • 函数提升 :函数声明会被提升到作用域顶部,且可以在声明之前调用。
    在这里插入图片描述

2. 作用域链是静态的

  • 函数的作用域链在定义时就确定下来,调用位置不会影响它的作用域链。
    在这里插入图片描述

3. 块作用域与变量声明

  • letconst :与 var 不同,它们会在块级作用域内提升,但在被赋值之前无法访问(即存在“暂时性死区”)。
  • 使用 letconst 可以有效避免变量提升带来的潜在问题,建议在开发中优先使用。
    在这里插入图片描述

4. 函数表达式与函数声明

  • 函数声明 会提升,而 函数表达式 不会。
  • 如果需要确保函数只能在特定位置后调用,应该使用函数表达式。
    在这里插入图片描述

💯小结

  • 在这里插入图片描述
    通过这篇文章的三个题目,我们深入理解了 JavaScript 中的变量提升函数提升作用域链的复杂机制。这些概念是 JavaScript基础,也是编码中最容易引发错误的部分。只有对它们有清晰的理解,才能避免代码中的陷阱,更高效地编写JavaScript 程序

在这里插入图片描述



http://www.kler.cn/a/409520.html

相关文章:

  • python继承和反射
  • MySQL中的ROW_NUMBER窗口函数简单了解下
  • JS精进之Hoisting(提升)
  • IEC61850读服务器目录命令——GetServerDirectory介绍
  • 【时时三省】NIT计算机考试基础知识
  • RangeInt,开源一个有限范围计数器模块。c语言的。 可以用于单片机
  • meterpreter常用命令 下
  • 取石子游戏
  • L1G1000 书生大模型全链路开源开放体系笔记
  • Python中使用matplotlib绘制图像并填充满整个figure区域
  • iphone小程序设置burpsuite代理抓包
  • 深入FastAPI:表单和文件上传详解
  • 03-微服务搭建
  • HC-SR501 PIR传感器是如何工作的以及如何与ESP32接口的
  • 【GPT】长时间面对屏幕会导致想吃垃圾食品吗
  • Python 类和对象:详细讲解中篇
  • 详解Qt QStorageInfo 存储信息类
  • 健康之路走上IPO之路 百度演双重角色
  • JavaFX:简介、使用场景、常见问题及对比其他框架分析
  • 241124学习日志——[CSDIY] [ByteDance] 后端训练营 [14]
  • oracle会话追踪
  • 七天掌握SQL--->第五天:数据库安全与权限管理
  • java实现小程序接口返回Base64图片
  • MySQL面试-1
  • 李继刚:提示词(Prompt)的本质是表达的艺术
  • 实战 | C#中使用YoloV8和OpenCvSharp实现目标检测 (步骤 + 源码)