【前端】特殊案例分析深入理解 JavaScript 中的词法作用域


在这里插入图片描述

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

文章目录

  • 💯前言
  • 💯案例代码
  • 💯词法作用域(Lexical Scope)与静态作用域
    • 什么是词法作用域?
    • 代码执行的详细分析
  • 💯函数定义与调用的区别
  • 💯动态作用域的对比
  • 💯小结与拓展
  • 💯小结


在这里插入图片描述


💯前言

  • JavaScript 作为一种同时广泛应用于前端和后端的编程语言,其核心机制之一便是 作用域。这种机制对变量和函数的 可见性访问权限 以及 生命周期 起着至关重要的作用,直接影响了代码的行为和执行逻辑。在本文中,我们将深入探讨 JavaScript 的词法作用域 及其相关的 作用域链机制,以此揭示代码执行过程中隐藏的复杂逻辑。通过一个简单而深刻的代码实例,我们将逐步解开 JavaScript 作用域的奥秘,以便更好地理解其背后的设计理念。无论是 初学者 还是有经验的 开发者,对这些概念的掌握都至关重要,是写出 高质量 JavaScript 代码 的基础。
    JavaScript在这里插入图片描述

💯案例代码

首先,让我们从一个简短的代码示例入手,这段代码看似简单,却蕴含了 JavaScript 中关于作用域的一些深刻原理。

var x = 10;

function f1() {
  console.log(x); // 输出为 10
}

function fn2() {
  var x = 20;
  f1(); // 调用 f1
}

fn2(); // 调用 fn2

在这里插入图片描述

fn2() 被调用时,f1() 函数也随之执行。然而,f1() 的输出是 10,而不是 20。这个现象正是 JavaScript 词法作用域的核心体现。接下来,我们将深入分析其背后的原因,并阐述 JavaScript 中关于作用域的独特之处。


💯词法作用域(Lexical Scope)与静态作用域

JavaScript 使用的是 词法作用域,这一概念也被称为 静态作用域。词法作用域的本质在于:函数所能访问的变量,取决于函数在代码中定义的位置,而非函数在运行时调用的位置。这一规则使得 JavaScript 的作用域在代码的编写阶段就已经确定,而不依赖于代码运行时的调用环境。
在这里插入图片描述


什么是词法作用域?

在这里插入图片描述
词法作用域的定义是:函数在定义时就决定了它可以访问哪些变量,这种访问环境在函数创建的那一刻就被固定下来换句话说,函数的 作用域链(Scope Chain) 在函数定义时已经锁定,不会因函数被调用的环境而发生改变。JavaScript 因此是静态作用域语言,其行为完全依赖于代码的书写和定义位置。

在上述代码中,函数 f1 被定义在全局作用域中,这意味着 f1 的作用域链中包含全局变量。因此,当 f1 尝试访问变量 x 时,它会从它的作用域链开始查找,因为 f1 的定义是在全局环境中,因此它首先会查找全局作用域中的变量 x,最终找到 x = 10,所以输出为 10


代码执行的详细分析

在这里插入图片描述
接下来,我们逐步剖析代码的执行过程:

  1. 全局上下文的创建

    • 在全局环境中,变量 x 被声明并赋值为 10。这意味着全局作用域中存在一个变量 x,其值为 10,供整个程序访问。
  2. f1 函数的定义

    • f1 函数在全局作用域中被定义。在函数 f1 被创建的那一刻,JavaScript 引擎已经确定了 f1 的作用域链。也就是说,f1 拥有对全局作用域中所有变量的访问权限,包括变量 x
  3. fn2 函数的定义和调用

    • 函数 fn2 被定义,并且在其内部定义了一个局部变量 x,其值为 20。随后,fn2 调用了 f1
    • fn2 被调用时,局部变量 x 被赋值为 20,此时 fn2 中的 x 和全局的 x 是两个完全独立的变量,处于不同的作用域中。
    • 尽管在 fn2 内部调用了 f1,但是 f1 的作用域链是在其定义时确定的,它只包含了它自己和全局变量。因此,当 f1 试图访问变量 x 时,它只能访问全局的 x,输出结果为 10
      在这里插入图片描述

💯函数定义与调用的区别

理解 JavaScript 词法作用域的关键在于区分 函数的定义和调用。函数的定义位置决定了它的作用域链,而这种作用域链在函数执行时并不会因调用位置的不同而改变。

在前述代码中,函数 f1 是在全局作用域中定义的,因此它的作用域链包含了全局变量。当 f1 被调用时,无论它是从全局环境还是其他函数内部调用,它始终按照定义时的作用域链来解析变量。因此,即使 fn2 中定义了局部变量 x = 20f1 依然无法访问到 fn2 中的 x,因为 f1 的作用域链在其定义时已经固定,不包括 fn2 的局部变量。


💯动态作用域的对比

在这里插入图片描述
为了更好地理解词法作用域,我们不妨对比一下 动态作用域 的概念。动态作用域与词法作用域的最大区别在于,动态作用域基于函数的调用位置来决定变量的可见性。

若 JavaScript 使用的是动态作用域,那么在 fn2 中调用 f1 时,f1 会首先查找调用环境中的变量 x,也就是 fn2 中的 x,其值为 20。然而,JavaScript 并不是这样工作的。JavaScript 采用词法作用域,作用域链在函数定义时已经确定,而不会因调用位置的不同而改变。

动态作用域 意味着变量解析基于函数的调用栈,而不是其定义位置。换言之,在动态作用域语言中,函数在调用时会根据当前调用的上下文来决定变量的绑定关系。这与 JavaScript 的词法作用域完全不同,因为 JavaScript 在函数定义时就已确定了其作用域链。理解这种差异对于更好地掌握 JavaScript 的行为和作用域管理至关重要。


💯小结与拓展

在这里插入图片描述
通过这段代码,我们能够更深入地理解 JavaScript 中的一个核心概念:词法作用域函数的作用域链在函数被定义时就已经确定,而不是等到函数被调用时才动态地重新决定。这意味着函数始终根据它在代码中定义的位置来解析变量,而不是依据它在运行时的调用位置。这一特性使得 JavaScript 的作用域模型更为直观,但也可能导致在嵌套调用时出现令人困惑的行为。

为了更好地掌握这一概念,我们可以记住以下几点:

  1. 函数的作用域链在定义时确定,而不是在调用时确定
  2. 全局变量在任何函数中都可以被访问,除非函数中存在同名的局部变量。同名的局部变量只会在其函数内部遮蔽全局变量。
  3. 局部变量的作用域仅限于定义它们的函数内部,在函数外部无法访问,这有助于避免变量的意外冲突。
  4. 如果需要在函数调用时访问不同的变量,可以通过 闭包 来实现,但词法作用域的规则仍然适用。
  5. 闭包 是 JavaScript 中的一个重要概念,允许函数携带其定义时的作用域。这使得函数即使在离开定义环境后,仍然可以访问该作用域中的变量,这是理解 JavaScript 词法作用域的重要一环。

闭包 的强大在于它允许创建私有变量、记住函数的执行状态以及实现函数工厂等功能。在 JavaScript 中,闭包的应用极为广泛,尤其是在处理异步操作、回调函数 等情境时,闭包的特性能够确保函数可以访问定义时的作用域。

理解 JavaScript词法作用域 对于编写健壮、可维护的代码至关重要。尤其是在处理嵌套函数、回调函数或模块化代码时,深入掌握作用域规则可以帮助我们避免许多潜在的错误。例如,在编写回调函数时,我们可能在无意识中引用了全局变量或外部函数的变量。如果对 词法作用域 的理解不够深入,这些问题往往难以察觉。

此外,立即执行函数表达式(IIFE) 是一种常用技术,用于在 JavaScript 中创建局部作用域,防止变量污染全局环境。通过 IIFE,我们可以在函数内部创建私有变量,从而避免它们泄漏到全局作用域中。这对代码的维护性和可读性具有非常重要的意义。IIFE 充分利用了 JavaScript 的词法作用域机制,为开发者提供了一个简洁的解决方案来隔离作用域。

另外,ES6 引入的 letconst 关键字 进一步增强了对作用域的控制。这些关键字使得变量具有块级作用域,避免了传统 var 所带来的变量提升和全局污染问题。块级作用域使得代码逻辑更加明确,特别是在循环和条件判断中使用时,可以显著减少潜在的作用域问题。


💯小结

  • 在这里插入图片描述
    希望本文能够帮助你深入理解 JavaScript 中的作用域机制。如果在编写代码时遇到关于作用域的困惑,不妨回顾这段代码,思考定义位置与调用位置之间的区别如何影响变量的可见性。这将有助于你大大提高代码的质量和调试的效率。同时,通过多练习各种作用域相关的问题,并理解 JavaScript 如何解析变量,你将在面对复杂代码时更加得心应手,对 JavaScript 的特性有更深刻的理解。

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/927842.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Docker】Docker配置远程访问

配置Docker的远程访问,你需要按照以下步骤进行操作: 1. 在Docker宿主机上配置Docker守护进程监听TCP端口 Docker守护进程默认只监听UNIX套接字,要实现远程访问,需要修改配置以监听TCP端口。 ‌方法一:修改Docker服务…

贪心算法题

0简介 0.1什么是贪心算法 贪心算法是用贪婪(鼠目寸光)的角度,找到解决问题的最优解 贪心策略:(从局部最优 --> 整体最优) 1把解决问题的过程分为若干步; 2解决每一个问题时,都选择当前“看上去”最优的解法; 3“…

【HTTP】HTTP协议

一个Web Server就是个服务器软件(程序),或者是运行这个服务器软件的硬件(计算机),其主要功能是通过HTTP协议与客户端进行通信,来接收,存储,处理来自客户端的HTTP请求&…

分布式存储方式的地理信息数据仓库建立设计方案

背景介绍 随着地理信息技术的发展,GIS系统中的数据规模越来越庞大,传统集中式存储方式在处理高并发查询和大规模空间分析时面临瓶颈。分布式存储通过数据分片、并行计算等技术,为地理信息数据管理提供了新的解决方案。适用场景: 遥感影像存储与分析 城市交通数据管理(如G…

深度学习案例:ResNet50模型+SE-Net

本文为为🔗365天深度学习训练营内部文章 原作者:K同学啊 一 回顾ResNet模型 ResNet,即残差网络,是由微软研究院的Kaiming He及其合作者于2015年提出的一种深度卷积神经网络架构。该网络架构的核心创新在于引入了“残差连接”&…

droppath

DropPath 是一种用于正则化深度学习模型的技术,它在训练过程中随机丢弃路径(或者说随机让某些部分的输出变为零),从而增强模型的鲁棒性和泛化能力。 代码解释: import torch import torch.nn as nn # 定义 DropPath…

KAN-Transfomer——基于新型神经网络KAN的时间序列预测

1.数据集介绍 ETT(电变压器温度):由两个小时级数据集(ETTh)和两个 15 分钟级数据集(ETTm)组成。它们中的每一个都包含 2016 年 7 月至 2018 年 7 月的七种石油和电力变压器的负载特征。 traffic(交通) :描…

03-12、SpringCloud Alibaba第十二章,升级篇,服务注册与配置中心Nacos

SpringCloud Alibaba第十二章,升级篇,服务注册与配置中心Nacos 一、为什么SpringCloud Alibaba 1、为什么 有了spring cloud这个微服务的框架,为什么又要使用spring cloud alibaba这个框架了?最重要的原因在于spring cloud中的…

算法之旅:LeetCode 拓扑排序由简入繁完全攻略

前言 欢迎来到我的算法探索博客,在这里,我将通过解析精选的LeetCode题目,与您分享深刻的解题思路、多元化的解决方案以及宝贵的实战经验,旨在帮助每一位读者提升编程技能,领略算法之美。 👉更多高频有趣Lee…

MATLAB 离散点构建凸包,计算面积周长(88)

MATLAB 离散点构建凸包,计算面积周长(88) 一、算法介绍二、算法实现1.代码2.总结这是缘,亦是命中最美的相见!!! 一、算法介绍 给定一堆离散点云,构建二维凸包,并计算凸包的面积和周长。 凸包是由顺序顶点构成的,因此凸包也可以当作多边形,则例的面积和周长计算方法…

Matlab Simulink HDL Coder开发流程(一)— 创建HDL兼容的Simulink模型

创建HDL兼容的Simulink模型 一、使用Balnk DUT模板二、从HDL Coder库中选择模块三、为DUT开发算法/功能四、为设计创建Testbench五、仿真验证设计功能六、Simulink模型生成HDL代码 这个例子说明了如何创建一个用于生成HDL代码的Simulink模型。要创建兼容HDL代码生成的MATLAB算法…

【智商检测——DP】

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e510, M 110; int f[N][M]; int main() {int n, k;cin >> n >> k;for(int i 1; i < n; i){int x;cin >> x;f[i][0] __gcd(f[i-1][0], x);for(int j 1; j < min(i, k)…

神经网络入门实战:(九)分类问题 → 神经网络模型搭建模版和训练四步曲

(一) 神经网络模型搭建官方文档 每一层基本都有权重和偏置&#xff0c;可以仔细看官方文档。 pytorch 官网的库&#xff1a;torch.nn — PyTorch 2.5 documentation Containers库&#xff1a;用来搭建神经网络框架&#xff08;包含所有的神经网络的框架&#xff09;&#xff1b…

不同云计算网络安全等级

导读云计算的本质是服务&#xff0c;如果不能将计算资源规模化/大范围的进行共享&#xff0c;如果不能真正以服务的形式提供&#xff0c;就根本算不上云计算。 等级保护定级流程 定级是开展网络安全等级保护工作的 “基本出发点”&#xff0c;虚拟化技术使得传统的网络边界变…

langchain实现基于sql的问答

1. 数据准备 import requestsurl "https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db"response requests.get(url)if response.status_code 200:# Open a local file in binary write modewith open("Chinook.db", "wb&qu…

flink学习(14)—— 双流join

概述 Join:内连接 CoGroup&#xff1a;内连接&#xff0c;左连接&#xff0c;右连接 Interval Join&#xff1a;点对面 Join 1、Join 将有相同 Key 并且位于同一窗口中的两条流的元素进行关联。 2、Join 可以支持处理时间&#xff08;processing time&#xff09;和事件时…

深入学习指针(5)!!!!!!!!!!!!!!!

文章目录 1.回调函数是什么&#xff1f;2.qsort使用举例2.1使用qsort函数排序整形数据2.2使用sqort排序结构数据 3.qsort函数的模拟实现 1.回调函数是什么&#xff1f; 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递…

CEF127 编译指南 Linux篇 - 构建CEF Client(七)

1. 引言 在完成 CEF127 的编译工作后&#xff0c;我们需要了解如何正确运行编译后的程序。本文将详细介绍如何使用 CMake 构建示例程序&#xff0c;并成功运行 CEF 客户端。通过本文的指导&#xff0c;您将能够在 Linux 环境下顺利运行 CEF 应用程序。 2. 准备工作 2.1 确认…

位图的学习

一&#xff0c;位图介绍 位图&#xff08;Bitmap&#xff09;是一种用于存储图像的方式&#xff0c;它通过二维矩阵&#xff08;由像素组成&#xff09;来表示图像的每一个细节。每个像素通常对应一个特定的颜色值&#xff0c;位图的每个“位”就代表了图像的一个像素。 位图…

电脑与优傲协作机器人(实体)的TCP通讯(操作记录)

目录 一、UR通信端口 二、电脑&#xff08;客户端&#xff09;连接协作机器人&#xff08;服务端&#xff09; 1.设置网络方法 2.检查设置 3.示教器切换远程控制&#xff08;注&#xff09; 4.客户端与协作机器人建立连接 5.连接测试 三、电脑&#xff08;服务端&#…