javascript中的作用域链

在 JavaScript 中,作用域链 是一种用于解析变量的机制,它决定了在不同的上下文(或作用域)中如何查找和访问变量。作用域链与 JavaScript 的函数作用域、块级作用域紧密相关,是理解变量可见性和访问权限的关键。

1. 作用域链的基本概念

作用域
  • 作用域(Scope) 指的是代码中变量、函数和对象的可访问范围。
  • JavaScript 主要有三种作用域:
    • 全局作用域:在全局上下文中声明的变量或函数,可以在任何地方访问。
    • 函数作用域:在函数内声明的变量和函数,只能在该函数内访问。
    • 块级作用域:用 letconst 声明的变量具有块级作用域,只在 {} 代码块内有效。
作用域链的定义
  • 作用域链(Scope Chain) 是指当 JavaScript 引擎在函数中查找变量时,如果在当前作用域找不到,它会沿着作用域链向上一级作用域查找,直到找到这个变量或者到达全局作用域为止。
  • 作用域链的查找顺序是从内到外的,即由当前作用域逐层向上查找,直到全局作用域。如果在作用域链中找不到对应的变量,最终会返回 undefined 或抛出错误(如果是严格模式)。

2. 作用域链的形成

  • 当函数被创建时,其作用域链就会确定。作用域链的形成是因为函数在定义时记录了所在上下文的作用域。
  • 每个函数都会有自己的作用域,但函数可以访问父级作用域中的变量,这就是作用域链的关键。

3. 作用域链的查找机制

当 JavaScript 引擎需要查找一个变量时,它会遵循以下步骤:

  1. 当前作用域:首先,JavaScript 会从当前作用域查找变量。
  2. 父级作用域:如果当前作用域没有找到变量,它会沿着作用域链向父级作用域查找。
  3. 全局作用域:如果在所有父级作用域中都找不到变量,它会最终在全局作用域中查找。
  4. 未找到:如果在作用域链中都未找到该变量,则根据是否启用严格模式,返回 undefined 或抛出 ReferenceError

4. 作用域链的案例

4.1 简单函数作用域链
let globalVar = 'I am global';

function outerFunction() {
  let outerVar = 'I am in outerFunction';
  
  function innerFunction() {
    let innerVar = 'I am in innerFunction';
    
    // 依次查找变量,作用域链的顺序是:innerFunction -> outerFunction -> global scope
    console.log(innerVar);  // I am in innerFunction
    console.log(outerVar);  // I am in outerFunction
    console.log(globalVar); // I am global
  }
  
  innerFunction();
}

outerFunction();

解释

  • innerFunction 执行时,它的作用域链是从 innerFunction 本身开始的。
  • 如果 innerVarinnerFunction 内找到,则使用该变量。
  • 如果 outerVar 不在 innerFunction 内定义,JavaScript 会向上查找 outerFunction 的作用域,直到找到。
  • 如果没有找到 outerVar,再继续查找全局作用域中的变量 globalVar
4.2 函数嵌套与作用域链
let a = 10;

function first() {
  let b = 20;
  
  function second() {
    let c = 30;
    
    console.log(a); // 查找顺序:second -> first -> global,输出 10
    console.log(b); // 查找顺序:second -> first,输出 20
    console.log(c); // 查找顺序:second,输出 30
  }
  
  second();
}

first();

解释

  • second 函数内部定义了变量 c,但当它访问 ab 时,需要沿着作用域链向上查找。
  • second 函数的作用域链包含了它自己的作用域和 first 函数的作用域,以及全局作用域。
4.3 块级作用域与作用域链
let globalVar = 'global';

function checkScope() {
  let functionScopeVar = 'function scope';
  
  if (true) {
    let blockScopeVar = 'block scope';
    console.log(blockScopeVar); // block scope
    console.log(functionScopeVar); // function scope
    console.log(globalVar); // global
  }

  // 块级作用域外访问 blockScopeVar 会报错
  // console.log(blockScopeVar); // ReferenceError: blockScopeVar is not defined
}

checkScope();

解释

  • blockScopeVar 是通过 let 定义的块级作用域变量,因此只能在 if 块内访问。
  • blockScopeVar 被查找时,它首先会在块内找到。
  • functionScopeVar 在整个 checkScope 函数内都是可访问的,因为它是在函数作用域中定义的。
  • globalVar 是全局变量,因此可以在任何地方访问。

5. 作用域链的特点

  1. 词法作用域(静态作用域)

    • JavaScript 使用词法作用域,即函数的作用域在函数定义时就已经决定,而不是在函数调用时。
    • 因此,作用域链是基于函数的定义位置,而不是调用位置。
  2. 作用域链的层次结构

    • 每个函数调用时,都会有一个自己的作用域。
    • 如果作用域中没有找到某个变量,就会通过作用域链逐级向上查找,直到找到变量或到达全局作用域。
  3. 嵌套作用域

    • 函数可以在其内部定义其他函数,形成嵌套作用域。内层函数可以访问外层函数的变量,但外层函数不能访问内层函数的变量。
  4. 闭包与作用域链

    • 闭包是依赖于作用域链的。闭包是指一个函数能够记住并访问它的词法作用域,即使这个函数在词法作用域之外被调用。

    闭包案例

    function outer() {
      let outerVar = 'I am outer';
      return function inner() {
        console.log(outerVar); // 闭包机制:inner 函数访问 outer 的变量
      };
    }
    
    const innerFunc = outer();
    innerFunc(); // 输出: I am outer
    

    解释inner 函数可以记住并访问 outer 函数的 outerVar,即使 outer 函数已经执行结束。这是因为 inner 函数在定义时创建了一个闭包,保留了对其词法作用域的引用。

6. 作用域链的使用场景

  • 变量的可见性控制:通过不同的作用域,开发者可以限制某些变量的可见性,从而避免全局变量污染,保证代码的模块化。

  • 闭包应用:利用作用域链可以实现闭包,闭包在很多场景中(如回调函数、事件处理、数据封装等)都有广泛应用。

  • 模块化开发:在模块化开发中,通过作用域链可以实现变量的封装和私有化,避免全局变量的冲突。

7. 总结

  • 作用域链 是一种变量解析机制,决定了 JavaScript 如何在多层嵌套的函数和块中查找变量。
  • 作用域链的查找顺序是由内到外的,最终会查找到全局作用域。
  • 通过作用域链,可以在内层函数中访问外层函数的变量,而闭包是作用域链的一个重要应用场景。
  • 使用作用域链,可以更好地控制变量的可见性和生命周期,从而编写出更模块化、更健壮的代码。

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

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

相关文章

【AI学习笔记】初学机器学习西瓜书概要记录(一)机器学习基础知识篇

初学机器学习西瓜书的概要记录(一)机器学习基础知识篇(已完结) 初学机器学习西瓜书的概要记录(二)常用的机器学习方法篇(持续更新) 初学机器学习西瓜书的概要记录(三)进阶知识篇(待更) 文字公式撰写不易&am…

【爱给网-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 1. 暴力破解密码,造成用户信息泄露 2. 短信盗刷的安全问题,影响业务及导致用户投诉 3. 带来经济损失,尤其是后付费客户,风险巨大,造…

virtualbox中的网络模式,网络设置,固定IP

virtualbox关于网络设置的文档:https://www.virtualbox.org/manual/topics/networkingdetails.html#networkingdetails DHCP Dynamic Host Configuration Protocol:动态主机配置协议,是专门用来给网络中的节点分发IP地址,确保每…

用友U8二次开发工具KK-FULL-*****-EFWeb使用方法

1、安装: 下一步,下一步即可。弹出黑框不要关闭,让其自动执行并关闭。 2、服务配置: 输入服务器IP地址,选择U8数据源,输入U8用户名及账号,U8登录日期勾选系统日期。测试参数有效性,提示测试通过…

【Unity-UGUI组件拓展】| Image 组件拓展,支持FIlled和Slice功能并存

🎬【Unity-UGUI组件拓展】| Image 组件拓展,支持FIlled和Slice功能并存一、组件介绍二、组件拓展方法三、完整代码💯总结🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏…

esp32 wifi 联网后,用http 发送hello 用pc 浏览器查看网页

参考chatgpt Esp32可以配置为http服务器,可以socket编程。为了免除编写针对各种操作系统的app。完全可以用浏览器仿问esp32服务器,获取esp32的各种数据,甚至esp的音频,视频。也可以利用浏览器对esp进行各种操作。但esp不能主动仿…

golang学习笔记1-go程序执行流程

声明:本人已有C,C,Python基础,只写本人认为的重点,方便自己回顾。 命令行执行go程序有两种方式,其流程如下图 注意第一种方式会得到可执行文件,第二种不会。 例1 在当前目录下编译hello.go go build hel…

Matplotlib绘图基础

1、散点图 绘制散点图是数据可视化中非常常见的操作,它用于显示两组数据之间的关系。Matplotlib 提供了 plt.scatter() 函数,可以轻松绘制散点图。以下是一个基础的散点图示例代码,并包含了一些优化可视化呈现的技巧。 import matplotlib.p…

istio中如何使用serviceentry引入外部服务

假设需要引入一个外部服务,外部服务ip为10.10.102.90,端口为32033. 引入到istio中后,我想通过域名gindemo.test.ch:9090来访问这个服务。 serviceentry yaml内容如下: apiVersion: networking.istio.io/v1beta1 kind: ServiceEn…

53 语言模型(和之后用来训练语言模型的数据集)_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录理论部分使用计数来建模N元语法总结 代码读取长序列数据随机采样顺序分区 小结练习 理论部分 在上一部分中,我们了解了如何将文本数据映射为词元,以及将这些词元可以视为一系列离散的观测,例如单词或字符…

构建与优化自定义进程池

1. 什么是进程池? 简单来说,进程池就是预先创建固定数量的工作进程,通过设计任务队列或调度算法来分配任务给空闲的进程 —— 实现“负载均衡”。 2. 进程池框架设计 枚举错误返回值: enum {UsageError 1,ArgError,PipeError };…

基于51单片机的汽车倒车防撞报警器系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 本课题基于微控制器控制器, 设计一款汽车倒车防撞报警器系统。 要求: 要求:1.配有距离, 用于把车和障碍物之间的距离信号送入控制器。 2.配有报警系…

如何安装和注册 GitLab Runner

如何安装和注册 GitLab Runner GitLab Runner 是一个用于运行 GitLab CI/CD (Continuous Integration/Continuous Deployment) 作业。它是一个与 GitLab 配合使用的应用程序,可以在本地或云中运行。Runner 可以执行不同类型的作业,例如编译代码、运行测…

传统软件应用技术的价值转换率越来越低

为什么感觉到卷?可能的一个原因是大家都在进步,用户和竞争对手也在进步,而自己却没有进步,也谈不上思维模式的改变。 我们不谈理论、不谈理想、不谈市场环境不好,就谈与用户接触过程的案例,这是最有说服力的…

传输层协议(TCP和UDP)

目录 一、UDP 1、UDPAPI 2、UDPAPI的使用 二、TCP 1、TCPAPI 2、TCP的相关特性 2.1 确认应答 2.2 超时重传 2.3 连接管理(三次握手,四次挥手) 2.4 滑动窗口 2.5 流量控制 2.6 拥塞控制 2.7 延时应答 2.8 捎带应答 2.9 面向字节…

1.3 计算机网络的分类

欢迎大家订阅【计算机网络】学习专栏,开启你的计算机网络学习之旅! 文章目录 前言一、按分布范围分类二、按传输技术分类三、按拓扑结构分类四、按使用者分类五、按传输介质分类 前言 计算机网络根据不同的标准可以被分为多种类型,本章从分布…

SqlSugar的where条件中使用可空类型报语法错误

SQLServer数据表中有两列可空列,均为数值类型,同时在数据库中录入测试数据,Age和Height列均部分有值。   使用SqlSugar的DbFirst功能生成数据库表类,其中Age、Height属性均为可空类型。   当Where函数中的检索条件较多时&a…

【Elasticsearch系列四】ELK Stack

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

idea中java及java web项目的常见问题

1、乱码问题,主要有几处地方,需要检查。 ①确保文件编码,其实主要就是在idea启动文件中,增加了 -Dfile.encodingUTF-8的设置 ②编辑器默认编码,都改为UTF-8 ③Tomcat的运行配置,编码也改为UTF-8,同样使用…

Wpf使用NLog将日志输出到LogViewer

1 LogViewer LogViewer是通过UDP传输的高性能实时log查看器。 具有一下特性: 通过UDP读取日志通过文件导入日志导出日志到一个文件中排序、过滤(日志树,日志等级)和查找突出显示搜索文本从UPD接收日志时忽略IP地址列表多接收器支…