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

深入学习JavaScript系列(二)——作用域和作用域链

本篇为第二篇,本系列文章会在后续学习后持续更新。

第一篇:#深入学习JavaScript系列(一)—— ES6中的JS执行上下文

第二篇:# 深入学习JavaScript系列(二)——作用域和作用域链

第三篇:# 深入学习JavaScript系列(三)——this

第四篇:# 深入学习JavaScript系列(四)——JS闭包

第五篇:# 深入学习JavaScript系列(五)——原型/原型链

第六篇: # 深入学习JavaScript系列(六)——对象/继承

第七篇:# 深入学习JavaScript系列(七)——Promise async/await generator

上一篇提到 在js的执行上下文中词法环境中会包含作用域链,同时词法环境解释阶段生成,在执行完毕后会被销毁,这也说明了作用域链的生命周期是随着函数的创建与销毁的。

先通过两个问题来引出一下

1、js执行上下文和作用域链的关系?

作用域链和执行上下文是紧密相连的概念。

执行上下文是JavaScript代码执行时的环境,包括变量、函数、参数等信息。每个函数都有自己的执行上下文,而全局代码也有自己的执行上下文。

从执行上下文的角度来说:
作用域链是由当前执行上下文和所有外层执行上下文的变量对象组成的链式结构。当JavaScript代码在一个执行上下文中查找变量时,会先在当前执行上下文的变量对象中查找,如果没有找到,就会继续在外层执行上下文的变量对象中查找,直到找到该变量或者到达全局执行上下文。

因此,作用域链的形成是由执行上下文的嵌套关系决定的。在函数定义时,就已经确定了该函数的作用域链。当函数被调用时,会创建一个新的执行上下文,并将该执行上下文的变量对象添加到作用域链的顶端,从而形成新的作用域链。
函数销毁时,与之对应的作用域链节点也会销毁。

总之,作用域链和执行上下文是密不可分的,它们共同构成了JavaScript代码的作用域和变量查找机制。

2、什么是js作用域链和作用域?

在 JavaScript 中,每个函数都有一个作用域链,它是一个由当前函数和所有外层函数的变量对象组成的列表。当 JavaScript 引擎查找一个变量时,它会先在当前函数的变量对象中查找,如果找不到,就会在外层函数的变量对象中查找,直到找到该变量或者到达全局对象为止。

每个函数内部声明的变量和函数都只能在该函数内部访问,外部无法访问。但是,如果一个函数内部嵌套了另一个函数,那么内部函数可以访问外部函数的变量和函数,这就是作用域链的作用。

作用域链的创建是在函数定义时确定的,而不是在函数调用时确定的。当一个函数被调用时,它会创建一个新的执行上下文,并将其添加到调用栈中。在执行上下文中,会创建一个变量对象,该变量对象包含了当前函数的所有变量和函数声明。同时,该执行上下文的作用域链会指向当前函数的作用域链。

当 JavaScript 引擎在执行一个函数时,如果需要访问一个变量,它会先在当前函数的变量对象中查找,如果找不到,就会在外层函数的变量对象中查找,直到找到该变量或者到达全局对象为止。这个查找的过程就是作用域链的遍历过程。

通过上面两个问题的展开,其实已经能大致了解了作用域与作用域链,下面就展开讲一讲 在学习作用域链时需要注意哪些内容

3、作用域和作用域链的创建

js采用的是静态作用域,也就是说js函数的作用域是在函数定义的情况下就已经确定了,那么作用域链也是在函数定义的时候就创建,

举个静态作用域的小例子:

var value = 1;function foo() {console.log(value);
}function bar() {var value = 2;foo();
}bar();// 结果是 1 在函数定义的时候确定的作用value等于1 所以调用的时候也等于1 

如果上述的代码是动态作用域,那么value就是在执行时才生成 所以打印为2

动态作用域的语言:

  1. Bash:Bash 是一种常用的 Unix shell,它支持动态作用域。在 Bash 中,变量的作用域是在函数被调用时
  2. Perl:Perl 是一种通用的高级编程语言,它支持动态作用域。在 Perl 中,变量的作用域是在程序运行时确定
  3. Lisp:Lisp 是一种函数式编程语言,它也支持动态作用域。在 Lisp 中,变量的作用域是在函数被调用时确定
  4. Emacs Lisp:Emacs Lisp 是一种基于 Lisp 的编程语言,它是 Emacs 编辑器的扩展语言,也支持动态作用域。

4 作用域和作用域链的执行销毁

js有两种函数作用域:全局作用域和函数作用域,创建上面已经讲到了,那么执行呢,其实是在函数中的变量被使用时会执行,先在当前作用域中查找变量,然后再逐步往上查找父级作用域,直到找到为止,
如果最终都没找到,那么就会抛出一个 ReferenceError 异常。

作用域链及作用域链的销毁

当一个函数执行完毕之后,那么它的执行上下文也随之销毁,于此同时。对应的函数作用域和作用域链也会销毁(当前函数作用域节点),函数中声明的变量也会被销毁,这就是js中大垃圾回收机制(gc)
最常见的有标记清除法和计数算法

注:如果函数中使用了闭包,那可能包含外部变量,此时只有外部变量被销毁时才能销毁对应的闭包。这就是所谓的内存泄漏。

5 几个作用域练习题目

题目一:

var scope = "global scope";
function checkscope(){var scope = "local scope";function scope(){return scope;}return scope();
}
checkscope();
//  local scope
var scope = "global scope";
function checkscope(){var scope = "local scope";function scope(){return scope;}return scope;
}
checkscope()();
//  local scope

解释:函数的作用域基于函数创建的位置,

注:checkscope()()和checkscope()的区别:

题目一中,checkscope()是一个函数,它返回另一个函数scope()。这个内部函数scope()可以访问checkscope()函数的局部变量。它是一个闭包函数,因为它可以访问其外部函数checkscope()的词法环境。

checkscope()()是在调用checkscope()返回的函数scope()。它不是直接调用checkscope()函数本身。由于checkscope()函数返回了一个函数,因此需要在checkscope()后添加另一个括号来调用内部函数scope()。相当于》checkscope(). f()

题目二:

var a = 1;function foo() {console.log(a);var a = 2;
}foo();
// undefined

解释:因为在函数 foo() 中,先执行了 var a = 2 语句,此时变量 a 被重新定义并赋值为 2。所以,在函数 foo() 中,变量 a 的值是 2,而不是全局变量 a 的值。因此,执行 console.log(a) 语句时,输出的是 undefined,因为变量 a 被重新定义,但没有被初始化。 变量能提升 但是赋值不能提升

题目三:

var x = 1;function outer() {var y = 2;function inner() {var z = 3;console.log(x + y + z);}inner();
}outer();
// 6

解释:在调用inner函数时,可以访问外层作用域中的变量 xy。变量 x 的值是 1,变量 y 的值是 2,变量 z 的值是 3。所以,执行 console.log(x + y + z) 语句时,输出的是 6。

题目四:

function foo() {var a = 1;function bar() {console.log(a);}return bar;
}var baz = foo();
baz();

答案:代码输出的是1。因为bar函数在定义时可以访问到其外层函数foo的变量a,并将其保存在函数作用域内部。当执行baz()时,实际上是执行了内部函数bar(),此时访问到的变量a是定义时保存在bar函数作用域内的变量,所以输出的是1

题目五:

var a = 1;
function foo() {console.log(a);
}function bar() {var a = 2;foo();
}bar();

答案:代码输出的是1。因为bar函数定义了一个名为a的局部变量,并将其赋值为2,但是在调用foo函数时,它会在全局作用域中查找变量a,因为在foo函数内部没有定义变量a。因此,foo函数输出的是全局变量a的值,即1。

关键点 foo的父级作用域不是bar 而是全局 只是foo()在bar()中执行。

题目六

var a = 1;
function foo() {var a = 2;function bar() {console.log(a);}return bar;
}var baz = foo();
baz();

答案:代码输出的是2。在foo函数内部定义了一个局部变量a,并将其赋值为2,然后返回了内部定义的函数barbar函数可以访问到foo函数的作用域链,因此可以访问到变量a。当调用baz函数时,实际上是执行了内部函数bar,此时访问到的变量a是定义时保存在foo函数作用域内的变量,所以输出的是2。

和题目五对比,因为bar函数是定义在foo函数内的 ,所以bar的父级作用域是foo

看到这里的同学都很厉害,那就顺便给我点个赞吧!

参考一:# JavaScript深入之作域链

参考二:# JavaScript深入之词法作用域和动态作用域

相关文章:

深入学习JavaScript系列(二)——作用域和作用域链

本篇为第二篇,本系列文章会在后续学习后持续更新。 第一篇:#深入学习JavaScript系列(一)—— ES6中的JS执行上下文 第二篇:# 深入学习JavaScript系列(二)——作用域和作用域链 第三篇&#x…...

【计算机视觉 | 目标检测】DETR风格的目标检测框架解读

文章目录一、前言二、理解2.1 DETR的理解2.2 DETR的细致理解2.2.1 Backbone2.2.2 Transformer encoder2.2.3 Transformer decoder2.2.4 Prediction feed-forward networks (FFNs)2.2.5 Auxiliary decoding losses2.3 更具体的结构2.4 编码器的原理和作用2.5 解码器的原理和作用…...

【LeetCode】剑指 Offer 41. 数据流中的中位数 p214 -- Java Version

题目链接:https://leetcode.cn/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof 1. 题目介绍(41. 数据流中的中位数) 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位…...

CSS3 知识总结

1,什么是CSS 用于定义网页的样式,包括不同设备和屏幕尺寸的设计、布局和显示变化。 2,CSS的作用优点 CSS 描述 HTML 元素如何在屏幕、纸张或其他媒体上显示 CSS 节省了大量工作。它可以一次控制多个网页的布局 3,css构成 CSS 规…...

回溯算法37:解数独

主要是我自己刷题的一些记录过程。如果有错可以指出哦,大家一起进步。 转载代码随想录 原文链接: 代码随想录 leetcode链接:37. 解数独 题目: 编写一个程序,通过填充空格来解决数独问题。 数独的解法需 遵循如下规则…...

【蓝桥杯-筑基篇】动态规划

🍓系列专栏:蓝桥杯 🍉个人主页:个人主页 目录 1.最大连续子段和 2.LCS 最大公共子序列 3.LIS 最长上升子序列 4.数塔 5.最大子矩阵和 6.背包问题 ①01背包问题 ②完全背包 1.最大连续子段和 这段代码是一个求最大子数组和的算法,使用…...

Unity利用Photon PUN2框架快速实现多人在线游戏实例分享

简介 Photon 是一个泛用性的 ScoketServer 套装软件,可用于多人在线游戏、聊天室、大厅游戏,并同时支持 Windows、Unity3D、iOS、Android、Flash 等平台。Photon 包含两个部分,一部分是 Socket 服务器,另一部分是其针对各个平台编写的 SDK,Unity3D 平台对应的 SDK 为 Pho…...

ChatGPT直出1.5w字论文查重率才30% - 基于物联网技术的智能家居控制系统设计与实现

文章目录ChatGPT直出1.5w字论文查重率才30% - 基于物联网技术的智能家居控制系统设计与实现一、绪论1.1 研究背景与意义1.2 国内外研究现状分析1.3 研究内容与目标1.4 研究方向和思路二、物联网技术与智能家居概述2.1 物联网技术原理与应用2.2 智能家居的概念与发展历程2.3 智能…...

特斯拉的操作系统是用什么语言编写的?

总目录链接>> AutoSAR入门和实战系列总目录 文章目录特斯拉车辆操作系统特斯拉GitHub中使用的语言Ruby和GoPythonSwift 和 Objective CQt我们知道操作系统至少需要一些非常低级的代码,这些代码在系统首次启动时运行,必须使用接近硬件的语言编写。…...

C++学习8-C++提高编程

文章目录前言一、模板1.1 模板的概念1.2 函数模板1.2.1 函数模板语法1.2.2 函数模板注意事项1.2.3 函数模板案例复习:计算数组长度1.2.4 普通函数与函数模板的区别1.2.5 普通函数与函数模板的调用规则1.2.6 模板的局限性1.3 类模板1.3.1 类模板语法1.3.2 类模板与函…...

ubuntu安装git server

一安装 要在Ubuntu上安装Git服务器,需要按照以下步骤进行操作: 安装Git: sudo apt-get update sudo apt-get install git 创建一个Git用户和一个Git仓库目录: sudo adduser git sudo mkdir /home/git/repo.git sudo chown git:git /home/git/repo.git 初始化Git仓库: c…...

物流云数据分析平台

物流云数据分析服务平台 http://project.webcats.cn/bx/36569/2455/index.html 本次系统模拟的是湖南省数据,解释权归杭氏集团所有! 1、系统简介: 物流大数据集成展示系统旨在通过大屏幕全面显示指定地区的物流运营车辆、物流公司和货主的相关信息和…...

配置OBS存储功能、新搭建obs

通过应用开发环境与OBS(Object-based Storage Service)对接,实现对象或者Widget资产存储功能。 背景信息 对象存储服务(Object-based Storage Service,OBS)是一个基于对象的海量存储服务,为客…...

基于DPDK收包的suricata的安装和运行

操作系统版本:Ubuntu 20.04.5 suricata版本: suricata-7.0.0-rc1 suricata是一个基于规则的入侵检测和防御引擎,功能强大,但性能可能 差强人意,不过目前最新的7版本已经支持DPDK收包了,DPDK是Intel提供的高…...

浅谈23种设计模式

创建型模式 有5种设计模式 抽象工厂(Abstract Factory):多套方案 抽象工厂模式是对创建不同的产品类型的抽象。对应到工作中,我们的确应该具备提供多套方案的能力,这也是我们常说的,要提供选择题。当你有这…...

JetBrains Rider 2022.3.3 Crack

具有 ReSharper 强大功能的令人难以置信的 .NET IDE!Rider 在我们使用 Windows 和 macOS 的整个开发团队中使用。 什么是骑士? JetBrains Rider 是一个基于 IntelliJ 平台和 ReSharper 的跨平台 .NET IDE。 支持许多 .NET 项目类型 JetBrains Rider 支持…...

浅理解扁平数据结构转Tree(树形结构)

文章目录📋前言🎯扁平数据结构🎯树形数据结构🎯使用递归将扁平数据转换为树形数据📝最后📋前言 在前端开发中,我们经常需要将扁平数据结构转换为树形结构(Tree)。比如在…...

前端开发——JavaScript的条件语句

世界不仅有黑,又或者白 世界而是一道精致的灰 ——Lungcen 目录 条件判断语句 if 语句 if else 语句 if else if else 语句 switch语句 break 关键字 case 子句 default语句 while循环语句 do while循环语句 for循环语句 for 循环中的三个表达式 for 循环嵌套 for …...

2.11 循环赛日程表

博主简介:一个爱打游戏的计算机专业学生博主主页: 夏驰和徐策所属专栏:算法设计与分析 目录 书本内容: 我的理解: 更优化的算法: 总结 1.注意实现问题 2.当用C语言和C实现循环赛日程表算法时&#xff…...

SpringBoot——SB整合mybatis案例(残缺版本)第三集

了解完使用阿里云存储的操作后,现在需要在案例里面集成阿里云进行开发。云服务——阿里云OSS的入门使用_北岭山脚鼠鼠的博客-CSDN博客 阿里云OSS——集成 对于前端传过来的图片要先上传到OSS,然后获取图片在云端的访问地址,存储到数据库里面…...

Baumer工业相机堡盟相机不满帧如何使用CameraExplorer设置相机参数让它的帧率达到满帧

项目场景 Baumer工业相机堡盟相机是一种高性能、高质量的工业相机,可用于各种应用场景,如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能,可以实时传输高分辨率图像。此外,该相机还具…...

巴黎爱情回忆 NFT 作品集

由 Metaverse Studio 制作。 欢迎来到浪漫之都巴黎!尽情游览美丽壮观的地标,探索法国文化。在离开之前,别忘了从《巴黎爱情回忆》NFT 作品集中带走一件纪念品。从世界著名的法国人物到标志性资产,这些 NFT肯定会为您的钱包带来巴黎…...

openai开放gpt3.5-turbo模型api,使用python即可写一个基于gpt的智能问答机器人

1安装python库 使用pip安装openai库,注意gpt3.5-turbo模型需要python>3.9的版本支持,本文演示的python版本是python3.10.10 pip install openai2创建api key 需要提前在openai官网上注册好账号,然后打开https://platform.openai.com/ac…...

GUI开发--LCD屏幕的使用(非第三方库)--笔记

导:界面交互需要GUI,GUI需要文字和图片,所有此处总结在M4芯片上实现GUI的基本操作!该芯片具有160K大小的内存,有512K的flash;故而没有使用第三方库! LCD屏幕的使用--笔记 1.汉字显示-两种方式…...

CesiumForUnreal实现地形等高线效果

文章目录 1.实现目标2.实现过程2.1 实现原理2.2 具体过程3.参考资料1.实现目标 在UE5中使用CesiumForUnreal插件添加Cesium World Terrain在线的世界地形,然后以25米为等高距,绘制一定范围内的等高线,如下图所示: 2.实现过程 由于这里直接使用CesiumForUnreal插件加载的在…...

Python爬虫——Python Selenium基本用法

Selenium 作为一款 Web 自动化测试框架,提供了诸多操作浏览器的方法,这里对其中的常用方法做详细介绍。 定位节点 Selenium 提供了 8 种定位单个节点的方法,如下所示: 定位节点方法方法说明find_element_by_id()通过 id 属性值定…...

仿真与测试:单元测试与Test Harness

本文描述单元测试的概念,以及Test Harness建立的方法和简单的单元测试过程。 文章目录1 单元测试1.1 场景举例1.2 简单的测试方法2 Test Harness建立2.1 模型配置2.2 创建Test Harness3 总结1 单元测试 单元测试,简单来说就是在Simulink模型中只测试一小…...

面试常问集锦——MySQL部分

Mysql速成大法 请签收MySQL灵魂十连 https://mp.weixin.qq.com/s?__bizMzI4NjI1OTI4Nw&mid2247488721&idx1&sneead82d2b7a0fdf993beacc4dfd60313&chksmebdef5e9dca97cff9d638877e5855850727ae26ebcfd60c7700ae53e311fa6ddb64b63bb9552&scene178&cur_a…...

算法训练第四十四天|完全背包理论 、518. 零钱兑换 II、377. 组合总和 Ⅳ

第九章 动态规划part06完全背包理论基础完全背包C测试代码总结518. 零钱兑换 II题目描述思路总结377. 组合总和 Ⅳ题目描述思路总结完全背包理论基础 参考:https://programmercarl.com/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%…...

0x06多层感知机

感知机 感知机形象的来看就是我们接触过的一个只有两个部分组成(输出和输入)组成的最简单的神经网络之一。 给定输入x,权重w和偏移b以及一个感知函数,感知机就能输出: 这个函数可以形象的用作二分类问题,…...