还没搞懂作用域、执行上下文、变量提升?看这篇就够啦

前言

 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!

 🍅 个人主页:南木元元


目录

作用域(Scope)

全局作用域

函数作用域

块级作用域

作用域链(Scope Chain)

执行上下文

执行上下文的类型

执行上下文栈

创建执行上下文

(1)创建阶段

(2)执行阶段

作用域与执行上下文区别

解释阶段

执行阶段

变量提升

本质原因

小练习

结语


作用域(Scope)

在JavaScript中,作用域(Scope)是指代码中变量、函数和对象的可访问性范围,决定了变量或函数在何处可以被访问或引用。

JavaScript中有3种类型的作用域,包括:

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)

全局作用域

在全局作用域中定义的变量在代码中任何地方都能访问到,常见的就是在最外层函数外面定义一个变量,该变量拥有全局作用域。

var globalVar = "I am global";

function test() {
  console.log(globalVar); // "I am global"
}

test();
console.log(globalVar); // "I am global"

除了上述情形外,还有几种情况拥有全局作用域:

  • 所有未定义直接赋值的变量自动声明为全局作用域
  • 所有window对象的属性拥有全局作用域,如窗口名字window.name、页面URL地址window.location等

全局作用域有很大的弊端,过多的全局作用域变量会污染全局命名空间,容易引起命名冲突。

函数作用域

函数作用域是指变量和函数在函数内部定义时,只在函数内部可见和可访问,函数外部无法访问函数内部的变量和函数(每当调用一个函数时,都会创建一个新的函数作用域)。

function localFunction() {
  var localVar = "I am local";
  console.log(localVar); // "I am local"
}

localFunction();
console.log(localVar); // ReferenceError: localVar is not defined

块级作用域

使用ES6中新增的let和const指令可以声明块级作用域,块级作用域可以在函数中创建也可以在一个代码块中创建(由一对花括号{}包裹的代码片段),使用var声明的变量不会有块级作用域。

if (true) {
  let blockVar = "I am in block";
  console.log(blockVar); // "I am in block"
}
console.log(blockVar); // ReferenceError: blockVar is not defined

作用域链(Scope Chain)

某个变量在当前作用域中找不到时,就会往它的上⼀级寻找,如果找到全局作用域还没找
到,就放弃寻找(返回 undefined),这种层级关系就是作用域链

作用域链的作用就是保证对变量和函数的有序访问

var globalVar = "I am global";

function outerFunction() {
  var outerVar = "I am outer";
  
  function innerFunction() {
    var innerVar = "I am inner";
    console.log(globalVar); // "I am global"
    console.log(outerVar); // "I am outer"
    console.log(innerVar); // "I am inner"
  }

  innerFunction();
}

outerFunction();

在上述代码中,innerFunction可以访问globalVar和outerVar,因为它们在外部作用域中。

需要注意的是,js采用的是静态作用域(词法作用域),函数的作用域在函数定义时就确定了(这里不再详细展开,推荐阅读JavaScript深入之词法作用域和动态作用域)

执行上下文

当JavaScript代码执行一段可执行代码时,会创建对应的执行上下文,执行上下文就是代码在执行过程中的环境信息。

执行上下文的类型

1.全局执行上下文:这是默认的执行上下文,当JavaScript代码开始运行时首先创建。全局执行上下文中的代码在全局作用域中执行,并且只有一个全局执行上下文。

2.函数执行上下文:每当一个函数被调用时,会创建一个新的执行上下文。每个函数都有自己的执行上下文,函数执行上下文在函数调用时被创建,并在函数执行完毕后被销毁。

3.eval函数执行上下文eval函数内部代码会创建一个独立的执行上下文,但不推荐使用eval

执行上下文栈

JavaScript引擎使用执行上下文栈来管理执行上下文。

当JavaScript执行代码时,首先遇到全局代码,会创建一个全局执行上下文并且压入执行栈中,每当遇到一个函数调用,就会为该函数创建一个新的执行上下文并压入栈顶,引擎会执行位于执行上下文栈顶的函数,当函数执行完成之后,执行上下文从栈中弹出,继续执行下一个上下文。当所有的代码都执行完毕之后,从栈中弹出全局执行上下文。

示例:

function f(a) {
  console.log(a);
}

function func(a) {
  f(a);
}

func(1);

假设用ESP指针来保存当前的执行状态,在系统栈中会产生如下的过程:

  1. 调用func, 将 func 函数的上下文压栈,ESP指向栈顶。

  2. 执行func,又调用f函数,将 f 函数的上下文压栈,ESP 指针上移。

  3. 执行完 f 函数,将ESP 下移,f函数对应的栈顶空间被回收。

  4. 执行完 func,ESP 下移,func对应的空间被回收。

 图示如下:

创建执行上下文

创建执行上下文有两个阶段:创建阶段执行阶段。

(1)创建阶段

在 JavaScript 代码执行前,执行上下文将经历创建阶段。主要包括:

  • 创建变量对象(Variable Object, VO)

在ES5中称为变量对象,在ES6及以后称为词法环境组件,这个对象包含了执行环境中所有变量和函数。

对全局执行上下文,变量对象是全局对象;对函数执行上下文,变量对象包含函数的参数、局部变量和函数声明。

提升函数和变量声明:将函数声明提升到当前作用域的顶部,并存储在变量对象中;将变量声明提升到当前作用域的顶部,但不会初始化(初始化在执行阶段进行)。

  • 建立作用域链

创建作用域链,包含当前执行上下文的变量对象和其父级执行上下文的变量对象,直到全局对象。

作用域链本质上是一个指向变量对象的指针列表,作用域链的前端始终都是当前执行上下文的变量对象,全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。

  • 确定this绑定

在全局执行上下文中,this指向全局对象;在函数执行上下文中,this取决于函数的调用方式。

(2)执行阶段

完成对变量的分配,在变量声明阶段被提升的变量,现在会被赋值,最后执行代码。

作用域与执行上下文

JavaScript属于解释型语言,其执行分为解释和执行两个阶段。

解释阶段

  • 词法分析:即分词,它的工作就是将一行行的代码分解成一个个token
  • 语法分析:将上一步生成的token数据,根据一定的语法规则转化为AST
  • 作用域规则确定

执行阶段

  • 创建执行上下文
  • 执行函数代码
  • 垃圾回收

JavaScript解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。

作用域和执行上下文之间最大的区别是: 执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变

变量提升

变量提升是指使用var声明的变量以及函数声明会被提升到作用域顶部, 变量提升的结果,就是可以在变量初始化之前访问该变量(返回undefined),在函数声明前可以调用该函数。

console.log(a); // undefined
var a = 5;
console.log(a); // 5

在上述代码中,尽管console.log(a)在var a = 5之前调用,但代码不会报错,因为在执行代码之前,变量已经被提升。

同样,函数声明也会被提升:

foo(); // "Hello, world!"
function foo() {
  console.log("Hello, world!");
}

上述代码中,即使foo在函数声明之前调用,代码依然可以正常执行。

本质原因

变量提升的本质原因:js 引擎在代码执行前有一个解析的过程,创建了执行上下文,初始化了一些代码执行时需要用到的对象。当访问一个变量时,会到当前执行上下文中的作用域链中去查找,而作用域链的首端指向的是当前执行上下文的变量对象,这个变量对象是执行上下文的一个属性,它包含了函数的形参、所有的函数和变量声明,这个对象的是在代码解析的时候创建的。

简单来说就是在执行JS代码之前,需要先解析代码。解析的时候会先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来,变量先赋值为undefined,函数先声明好可使用。这一步执行完了,才开始正式的执行程序。

注意点

1.let和const声明的变量不会被提升。如果在声明前使用它们,会报错。

2.函数声明会被提升,函数表达式不会被提升。

小练习

说出下面代码的执行结果。

var foo = function () {
    console.log("foo1")
}
foo()

var foo = function () {
    console.log("foo2")
}
foo()


function foo() {
    console.log("foo1")
}
foo()

function foo() {
    console.log("foo2")
}
foo()

这是一道关于变量提升的题,具体就不展开了,可以看这道面试题真的很变态吗?

结语

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~ 

 

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

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

相关文章

编译选项导致的结构体字节参数异常

文章目录 前言问题描述原因分析问题解决总结 前言 在构建编译工程时,会有一些对应的编译配置选项,不同的编译器,会有对应的配置项。本文介绍GHS工程中编译选项配置不对应导致的异常。 问题描述 在S32K3集成工程中,核1的INP_SWC…

【并发程序设计】15.信号灯(信号量)

15.信号灯(信号量) Linux中的信号灯即信号量是一种用于进程间同步或互斥的机制,它主要用于控制对共享资源的访问。 在Linux系统中,信号灯作为一种进程间通信(IPC)的方式,与其他如管道、FIFO或共享内存等IPC方式不同&…

c++ 哈希 unordered_map unordered_set 的学习

1. unordered 系列 在 c98 中, STL 提供了底层是红黑树结构的一系列关联式容器,set 和 map 的查询效率可以达到 log2N,红黑树最差的情况也只是需要比较红黑树的高度次,当节点数量非常多时,查找一个节点还需要比较几十…

护肤品美妆商城小程序的作用是什么

经营美妆的方式多种多样,商场街边、电商平台、微商等,无论厂商品牌还是经销商批发零售都有大量目标群体,客户在哪里商家就应该在哪里,私域生意模式,商家需要线上多渠道获客转化和提高营收。 运用【雨科】平台搭建护肤…

PatchEmbed

PatchEmbed 是用于计算机视觉任务的神经网络层,特别是在Vision Transformer (ViT) 模型中使用。它负责将输入的图像分割成固定大小的图像块(patches),并将这些图像块线性嵌入到高维空间中。这是Vision Transformer处理图像的方式&…

JVM虚拟机性能监控工具

命令行工具 jps 虚拟机进程状况查询工具 jps(JVM Process Status Tool),可以列出正在运行的虚拟机进程,并显示虚拟机执行主类名称或者jar文件名,还有这些进程的本地虚拟机唯一ID(LVMID,Local Virtual Machine Identifier)。 # …

Vue.js 与 TypeScript(1) :项目配置、props标注类型、emits标注类型

像 TypeScript 这样的类型系统可以在编译时通过静态分析检测出很多常见错误。这减少了生产环境中的运行时错误,也让我们在重构大型项目的时候更有信心。通过 IDE 中基于类型的自动补全,TypeScript 还改善了开发体验和效率。 一、项目配置 在使用 npm cr…

AI 网页解锁器,用于网页抓取一切 | 最快的验证码解决服务

想象一下,解锁互联网的全部潜力,数据自由流动,没有任何障碍阻挡你获取所需信息。在网络爬虫的世界里,这个梦想常常会遇到障碍:CAPTCHA和反机器人措施,这些措施旨在保护网站免受自动化访问的侵害。但如果有一…

蓝桥杯软件测试-十五届模拟赛2期题目解析

十五届蓝桥杯《软件测试》模拟赛2期题目解析 PS 需要第十五界蓝桥杯模拟赛2期功能测试模板、单元测试被测代码、自动化测试被测代码请加🐧:1940787338 备注:15界蓝桥杯省赛软件测试模拟赛2期 题目1:功能测试题目 1(测试用例&…

60 关于 SegmentFault 的一些场景 (1)

前言 呵呵 此问题主要是来自于 帖子 月经结贴 -- 《Segmentation Fault in Linux》 这里主要也是 结合了作者的相关 case, 来做的一些 调试分享 当然 很多的情况还是 蛮有意思 本文主要问题如下 1. 访问可执行文件中的 只读数据 2. 访问不存在的虚拟地址 3. 访问内核地址…

【机器学习】基于OpenCV和TensorFlow的MobileNetV2模型的物种识别与个体相似度分析

在计算机视觉领域,物种识别和图像相似度比较是两个重要的研究方向。本文通过结合深度学习和图像处理技术,基于OpenCV和TensorFlow的MobileNetV2的预训练模型模,实现物种识别和个体相似度分析。本文详细介绍该实验过程并提供相关代码。 一、名…

Python代码:二十六、反转列表

1、题目 描述 小明有一个列表记录了各个朋友的喜欢的数字,num [3, 5, 9, 0, 1, 9, 0, 3],请你帮他创建列表,然后使用reverse函数将列表反转输出。 输入描述: 无 输出描述: 第一行输出创建好的原始的列表&#x…

typescript --object对象类型

ts中的object const obj new Object()Object 这里的Object是Object类型,而不是JavaScript内置的Object构造函数。 这里的Object是一种类型,而Object()构造函数表示一个值。 Object()构造函数的ts代码 interface ObjectConstructor{readonly prototyp…

【JavaEE】JVM中垃圾回收机制详解

一.垃圾回收的基本概念 1.什么是垃圾回收机制. JVM(Java虚拟机)垃圾回收机制是Java内存管理的重要组成部分,它负责自动回收程序中不再使用的对象所占用的内存空间。这样可以有效地防止内存泄漏和内存溢出问题,提高程序的稳定性和…

电脑死机问题排查

情况描述:2024年6月2日下午16:04分电脑突然花屏死机,此情况之前遇到过三次,认为是腾讯会议录屏和系统自带录屏软件冲突导致。 报错信息:应用程序-特定 权限设置并未向在应用程序容器 不可用 SID (不可用)中运行的地址…

GPT-4o有点坑

GPT-4o有点坑 0. 前言1. GPT-4o简介2. GPT-4o带来的好处2.1 可以上传图片和文件2.2 更丰富的功能以及插件 3. "坑"的地方3.1 使用时间短3.2 GPT-4o变懒了 4. 总结 0. 前言 原本不想对GPT-4o的内容来进行评论的,但是看了相关的评论一直在说:技…

全国水系数据(更新到2024年5月)

上海市水系数据地图可视化 水系数据线图层(小河/溪流、江/河、运河、下水道/排水管) 水系数据面数据(水域、水库、河岸、湿地) 水系数据字段说明 可视化预览 北京市水系可视化 上海市水系可视化 广州市水系可视化 深圳市水系可视化…

Gin的快速入门和搭建

文章目录 Go的工程工程架构技术选型 Gin入门 Go的工程 基于Go生态,构建一个支持内容管理,内容加工、内容分发的内容库系统。 内容管理:增删改查内容加工:例如内容审核、推荐等内容分发:将内容可以推到不同的业务线 …

用例篇03

正交表 因素:存在的条件 水平:因素的取值 最简单的正交表:L4(2) 应用 allpairs 来实现正交表。 步骤: 1.根据需求找出因素和水平 2.将因素和水平写入到excel表格中(表格不需要保存)(推荐用…

集成学习算法笔记

一、引言 在机器学习和数据分析领域,集成学习算法因其能够显著提高模型预测性能而备受关注。然而,任何算法在应用过程中都不可避免地会遇到一些挑战和问题,集成学习算法也不例外。其中,最为常见且关键的两大问题便是欠拟合&#…