《Go 语言第一课》课程学习笔记(十二)

news2024/11/19 5:34:40

函数

Go 函数与函数声明

  • 在 Go 语言中,函数是唯一一种基于特定输入,实现特定任务并可返回任务执行结果的代码块(Go 语言中的方法本质上也是函数)。
  • 在 Go 中,我们定义一个函数的最常用方式就是使用函数声明。
    在这里插入图片描述
    • 第一部分是关键字 func,Go 函数声明必须以关键字 func 开始。
    • 第二部分是函数名。
      • 函数名是指代函数定义的标识符,函数声明后,我们会通过函数名这个标识符来使用这个函数。
      • 在同一个 Go 包中,函数名应该是唯一的。
      • 首字母大写的函数名指代的函数是可以在包外使用的,小写的就只在包内可见。
    • 第三部分是参数列表。
      • 参数列表中声明了我们将要在函数体中使用的各个参数。
      • 参数列表紧接在函数名的后面,并用一个括号包裹。
      • 它使用逗号作为参数间的分隔符,而且每个参数的参数名在前,参数类型在后,这和变量声明中变量名与类型的排列方式是一致的。
      • Go 函数支持变长参数,也就是一个形式参数可以对应数量不定的实际参数。
    • 第四部分是返回值列表。
      • 返回值承载了函数执行后要返回给调用者的结果,返回值列表声明了这些返回值的类型,返回值列表的位置紧接在参数列表后面,两者之间用一个空格隔开。
      • Fprintf 函数的返回值列表不仅声明了返回值的类型,还声明了返回值的名称,这种返回值被称为具名返回值。
      • 多数情况下,我们不需要这么做,只需声明返回值的类型即可。
    • 最后,放在一对大括号内的是函数体,函数的具体实现都放在这里。
      • 不过,函数声明中的函数体是可选的。
      • 如果没有函数体,说明这个函数可能是在 Go 语言之外实现的,比如使用汇编语言实现,然后通过链接器将实现与声明中的函数名链接到一起。
  • 函数声明中的函数名其实就是变量名,函数声明中的 func 关键字、参数列表和返回值列表共同构成了函数类型。而参数列表与返回值列表的组合也被称为函数签名,它是决定两个函数类型是否相同的决定因素。因此,函数类型也可以看成是由 func 关键字与函数签名组合而成的。
    • 通常,在表述函数类型时,我们会省略函数签名参数列表中的参数名,以及返回值列表中的返回值变量名。
      func(io.Writer, string, ...interface{}) (int, error)
      
    • 如果两个函数类型的函数签名是相同的,即便参数列表中的参数名,以及返回值列表中的返回值变量名都是不同的,那么这两个函数类型也是相同类型。
    • 每个函数声明所定义的函数,仅仅是对应的函数类型的一个实例。
    • 函数字面值由函数类型与函数体组成,它特别像一个没有函数名的函数声明,因此我们也叫它匿名函数。

函数参数的那些事儿

  • 函数参数列表中的参数,是函数声明的、用于函数体实现的局部变量。
    • 由于函数分为声明与使用两个阶段,在不同阶段,参数的称谓也有不同。
    • 在函数声明阶段,我们把参数列表中的参数叫做形式参数(Parameter,简称形参),在函数体中,我们使用的都是形参;
    • 而在函数实际调用时传入的参数被称为实际参数(Argument,简称实参)。
      在这里插入图片描述
  • Go 语言中,函数参数传递采用是值传递的方式。
    • 所谓“值传递”,就是将实际参数在内存中的表示逐位拷贝(Bitwise Copy)到形式参数中。
    • 对于像整型、数组、结构体这类类型,它们的内存表示就是它们自身的数据内容,因此当这些类型作为实参类型时,值传递拷贝的就是它们自身,传递的开销也与它们自身的大小成正比。
    • 但是像 string、切片、map 这些类型就不是了,它们的内存表示对应的是它们数据内容的“描述符”。当这些类型作为实参类型时,值传递拷贝的也是它们数据内容的“描述符”,不包括数据内容本身,所以这些类型传递的开销是固定的,与数据内容大小无关。这种只拷贝“描述符”,不拷贝实际数据内容的拷贝过程,也被称为“浅拷贝”。
    • 不过函数参数的传递也有两个例外,当函数的形参为接口类型,或者形参是变长参数时,简单的值传递就不能满足要求了,这时 Go 编译器会介入:对于类型为接口类型的形参,Go 编译器会把传递的实参赋值给对应的接口类型形参;对于为变长参数的形参,Go 编译器会将零个或多个实参按一定形式转换为对应的变长形参。
    • 在 Go 中,变长参数实际上是通过切片来实现的。所以,我们在函数体中,就可以使用切片支持的所有操作来操作变长参数,这会大大简化了变长参数的使用复杂度。

函数支持多返回值

  • **和其他主流静态类型语言,比如 C、C++ 和 Java 不同,Go 函数支持多返回值。**多返回值可以让函数将更多结果信息返回给它的调用者,Go 语言的错误处理机制很大程度就是建立在多返回值的机制之上的。
  • 函数返回值列表从形式上看主要有三种:
    func foo() // 无返回值
    func foo() error // 仅有一个返回值
    func foo() (int, string, error) // 有2或2个以上返回值
    
    • 如果一个函数没有显式返回值,那么我们可以像第一种情况那样,在函数声明中省略返回值列表。
    • 而且,如果一个函数仅有一个返回值,那么通常我们在函数声明中,就不需要将返回值用括号括起来,如果是 2 个或 2 个以上的返回值,那我们还是需要用括号括起来的。
    • Go 标准库以及大多数项目代码中的函数,都选择了使用普通的非具名返回值形式。但在一些特定场景下,具名返回值也会得到应用。比如,当函数使用 defer,而且还在 defer 函数中修改外部函数返回值时,具名返回值可以让代码显得更优雅清晰。

函数是“一等公民”

  • 函数在 Go 语言中属于“一等公民(First-Class Citizen)”。
    • 特征一:Go 函数可以存储在变量中。
    • 特征二:支持在函数内创建并通过返回值返回。
      • Go 函数不仅可以在函数外创建,还可以在函数内创建。
      • 而且由于函数可以存储在变量中,所以函数也可以在创建后,作为函数返回值返回。
      • 闭包本质上就是一个匿名函数或叫函数字面值,它们可以引用它的包裹函数,也就是创建它们的函数中定义的变量。然后,这些变量在包裹函数和匿名函数之间共享,只要闭包可以被访问,这些共享的变量就会继续存在。
    • 特征三:作为参数传入函数。
    • 特征四:拥有自己的类型。
      // $GOROOT/src/net/http/server.go
      type HandlerFunc func(ResponseWriter, *Request)
      // $GOROOT/src/sort/genzfunc.go
      type visitFunc func(ast.Node) ast.Visitor
      

Go 语言是如何进行错误处理的?

  • Go 函数增加了多返回值机制,来支持错误状态与返回信息的分离,并建议开发者把要返回给调用者的信息和错误状态标识,分别放在不同的返回值中。
  • 虽然,在 Go 语言中,我们依然可以像传统的 C 语言那样,用一个整型值来表示错误状态,但 Go 语言惯用法,是使用 error 这个接口类型表示错误,并且按惯例,我们通常将 error 类型返回值放在返回值列表的末尾。

error 类型与错误值构造

  • error 接口是 Go 原生内置的类型,它的定义如下:
    // $GOROOT/src/builtin/builtin.go
    type interface error {
    	Error() string
    }
    
    • 任何实现了 error 的 Error 方法的类型的实例,都可以作为错误值赋值给 error 接口变量。
    • 那这里,问题就来了:难道为了构造一个错误值,我们还需要自定义一个新类型来实现 error 接口吗?
    • Go 语言的设计者显然也想到了这一点,他们在标准库中提供了两种方便 Go 开发者构造错误值的方法: errors.New 和 fmt.Errorf。
      err := errors.New("your first demo error")
      errWithCtx = fmt.Errorf("index %d is out of bounds", i)
      
    • 这两种方法实际上返回的是同一个实现了 error 接口的类型的实例,这个未导出的类型就是 errors.errorString,它的定义是这样的:
      // $GOROOT/src/errors/errors.go
      type errorString struct {
      	s string
      } 
      func (e *errorString) Error() string {
      	return e.s
      }
      
    • 大多数情况下,使用这两种方法构建的错误值就可以满足我们的需求了。虽然这两种构建错误值的方法很方便,但它们给错误处理者提供的错误上下文(Error Context)只限于以字符串形式呈现的信息,也就是 Error 方法返回的信息。
    • 但在一些场景下,错误处理者需要从错误值中提取出更多信息,帮助他选择错误处理路径,显然这两种方法就不能满足了。这个时候,我们可以自定义错误类型来满足这一需求。
      // $GOROOT/src/net/net.go
      type OpError struct {
      	Op string
      	Net string
      	Source Addr
      	Addr Addr
      	Err error
      }
      

Go 语言的几种错误处理的惯用策略

  • 策略一:透明错误处理策略
    • Go 语言中的错误处理,就是根据函数 / 方法返回的 error 类型变量中携带的错误值信息做决策,并选择后续代码执行路径的过程。
    • 最简单的错误策略莫过于完全不关心返回错误值携带的具体上下文信息,只要发生错误就进入唯一的错误处理执行路径:
      err := doSomething()
      if err != nil {
      	// 不关心err变量底层错误值所携带的具体上下文信息
      	// 执行简单错误处理逻辑并返回
      	... ...
      	return err
      }
      
    • 这也是 Go 语言中最常见的错误处理策略,80% 以上的 Go 错误处理情形都可以归类到这种策略下。
  • 策略二:“哨兵”错误处理策略
    • 当错误处理方不能只根据“透明的错误值”就做出错误处理路径选取的情况下,错误处理方会尝试对返回的错误值进行检视,于是就有可能出现下面代码中的反模式:
      data, err := b.Peek(1)
      if err != nil {
      	switch err.Error() {
      	case "bufio: negative count":
      		// ... ...
      		return
      	case "bufio: buffer full":
      		// ... ...
      		return
      	case "bufio: invalid use of UnreadByte":
      		// ... ...
      		return
      	default:
      		// ... ...
      		return
      	}
      }
      
    • 反模式就是,错误处理方以透明错误值所能提供的唯一上下文信息(描述错误的字符串),作为错误处理路径选择的依据。但这种“反模式”会造成严重的隐式耦合。这也就意味着,错误值构造方不经意间的一次错误描述字符串的改动,都会造成错误处理方处理行为的变化,并且这种通过字符串比较的方式,对错误值进行检视的性能也很差。
    • Go 标准库采用了定义导出的(Exported)“哨兵”错误值的方式,来辅助错误处理方检视(inspect)错误值并做出错误处理分支的决策。
      // $GOROOT/src/bufio/bufio.go
      var (
      	ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
      	ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
      	ErrBufferFull = errors.New("bufio: buffer full")
      	ErrNegativeCount = errors.New("bufio: negative count")
      )
      data, err := b.Peek(1)
      if err != nil {
      	switch err {
      	case bufio.ErrNegativeCount:
      		// ... ...
      		return
      	case bufio.ErrBufferFull:
      		// ... ...
      		return
      	case bufio.ErrInvalidUnreadByte:
      		// ... ...
      		return
      	default:
      		// ... ...
      		return
      	}
      }
      
  • 策略三:错误值类型检视策略
    • 由于错误值都通过 error 接口变量统一呈现,要得到底层错误类型携带的错误上下文信息,错误处理方需要使用 Go 提供的类型断言机制(Type Assertion)或类型选择机制(Type Switch),这种错误处理方式,称之为错误值类型检视策略。
    • 一般自定义导出的错误类型以XXXError的形式命名。和“哨兵”错误处理策略一样,错误值类型检视策略,由于暴露了自定义的错误类型给错误处理方,因此这些错误类型也和包的公共函数 / 方法一起,成为了 API 的一部分。
    • 一旦发布出去,开发者就要对它们进行很好的维护。而它们也让使用这些类型进行检视的错误处理方对其产生了依赖。
    • 标准库 errors 包提供了As函数给错误处理方检视错误值。As函数类似于通过类型断言判断一个 error 类型变量是否为特定的自定义错误类型。
  • 策略四:错误行为特征检视策略
    • 在 Go 标准库中,我们发现了这样一种错误处理方式:将某个包中的错误类型归类,统一提取出一些公共的错误行为特征,并将这些错误行为特征放入一个公开的接口类型中。这种方式也被叫做错误行为特征检视策略。
    • 错误处理方只需要依赖这个公共接口,就可以检视具体错误值的错误行为特征信息,并根据这些信息做出后续错误处理分支选择的决策。

怎么让函数更简洁健壮?

  • 健壮性的“三不要”原则
    • 原则一:不要相信任何外部输入的参数。
      • 为了保证函数的健壮性,函数需要对所有输入的参数进行合法性的检查。
      • 一旦发现问题,立即终止函数的执行,返回预设的错误值。
    • 原则二:不要忽略任何一个错误。
      • 在我们的函数实现中,也会调用标准库或第三方包提供的函数或方法。
      • 对于这些调用,我们不能假定它一定会成功,我们一定要显式地检查这些调用返回的错误值。
      • 一旦发现错误,要及时终止函数执行,防止错误继续传播。
    • 原则三:不要假定异常不会发生。
      • 异常不是错误。错误是可预期的,也是经常会发生的,我们有对应的公开错误码和错误处理预案,但异常却是少见的、意料之外的。
      • 虽然异常发生是“小众事件”,但是我们不能假定异常不会发生。所以,函数设计时,我们就需要根据函数的角色和使用场景,考虑是否要在函数内设置异常捕捉和恢复的环节。

Go 函数的异常处理设计

  • 在 Go 语言中,异常这个概念由 panic 表示。
    • panic 指的是 Go 程序在运行时出现的一个异常情况。
    • 如果异常出现了,但没有被捕获并恢复,Go 程序的执行就会被终止,即便出现异常的位置不在主 Goroutine 中也会这样。
  • 在 Go 中,panic 主要有两类来源,一类是来自 Go 运行时,另一类则是 Go 开发人员通过 panic 函数主动触发的。
    • 无论是哪种,一旦 panic 被触发,后续 Go 程序的执行过程都是一样的,这个过程被 Go 语言称为 panicking。
    • 当函数 F 调用 panic 函数时,函数 F 的执行将停止。不过,函数 F 中已进行求值的 deferred 函数都会得到正常执行,执行完这些 deferred 函数后,函数 F 才会把控制权返还给其调用者。
    • 对于函数 F 的调用者而言,函数 F 之后的行为就如同调用者调用的函数是 panic 一样,该 panicking过程将继续在栈上进行下去,直到当前 Goroutine 中的所有函数都返回为止,然后 Go 程序将崩溃退出。
  • Go 也提供了捕捉 panic 并恢复程序正常执行秩序的方法,我们可以通过 recover 函数来实现这一点。
    func bar() {
    	defer func() {
    		if e := recover(); e != nil {
    			fmt.Println("recover the panic:", e)
    		}
    	}()
    	println("call bar")
    	panic("panic occurs in bar")
    	zoo()
    	println("exit bar")
    }
    
    • 我们在一个 defer 匿名函数中调用 recover 函数对 panic 进行了捕捉。
    • recover 是 Go 内置的专门用于恢复 panic 的函数,它必须被放在一个 defer 函数中才能生效。
    • 如果 recover 捕捉到 panic,它就会返回以 panic 的具体内容为错误上下文信息的错误值。如果没有 panic 发生,那么 recover 将返回 nil。而且,如果 panic 被 recover 捕捉到,panic 引发的 panicking 过程就会停止。

使用 defer 简化函数实现

  • defer 是 Go 语言提供的一种延迟调用机制,defer 的运作离不开函数。
    • 在 Go 中,只有在函数(和方法)内部才能使用 defer;
    • defer 关键字后面只能接函数(或方法),这些函数被称为 deferred 函数。
    • defer 将它们注册到其所在 Goroutine 中,用于存放 deferred 函数的栈数据结构中,这些 deferred 函数将在执行 defer 的函数退出前,按后进先出(LIFO)的顺序被程序调度执行。
      在这里插入图片描述
    • 而且,无论是执行到函数体尾部返回,还是在某个错误处理分支显式 return,又或是出现 panic,已经存储到 deferred 函数栈中的函数,都会被调度执行。所以说,deferred 函数是一个可以在任何情况下为函数进行收尾工作的好“伙伴”。
  • defer 使用的几个注意事项
    • 第一点:明确哪些函数可以作为 deferred 函数
      • 对于自定义的函数或方法,defer 可以给与无条件的支持,但是对于有返回值的自定义函数或方法,返回值会在 deferred 函数被调度执行的时候被自动丢弃。
      • append、cap、len、make、new、imag 等内置函数都是不能直接作为 deferred 函数的,而 close、copy、delete、print、recover 等内置函数则可以直接被 defer 设置为 deferred 函数。
      • 对于那些不能直接作为 deferred 函数的内置函数,我们可以使用一个包裹它的匿名函数来间接满足要求。
    • 第二点:注意 defer 关键字后面表达式的求值时机
      • defer 关键字后面的表达式,是在将 deferred 函数注册到 deferred 函数栈的时候进行求值的。
    • 第三点:知晓 defer 带来的性能损耗
      • defer 让我们进行资源释放(如文件描述符、锁)的过程变得优雅很多,也不易出错。但在性能敏感的应用中,defer 带来的性能负担也是我们必须要知晓和权衡的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虚拟直播从0到1全套解决方案,码住这个神器,小白也能轻松涨粉过万!

直播行业历经十余年生长,用户规模持续上升,成为平台经济领域的中流砥柱。同时,元宇宙的兴起也推动了虚拟现实等先进技术在直播行业的应用,越来越多的老板开始尝试使用虚拟直播完成各类形式的直播活动,与传统型的直播间…

Web核心基础总结

尚硅谷课程:293-尚硅谷-Filter-什么是Filter过滤器_哔哩哔哩_bilibili JavaWeb核心技术点: Servlet程序、Filter过滤器、Listener监听器、jsp页面、EL表达式、JSTL标签库、jQuery框架、Cookie技术、Session会话、JSON使用、Ajax请求, 第一…

独立服务编排逻辑引擎:jvs-logic服务原子组件介绍

逻辑引擎的本质是可视化的服务编排是指使用图形化工具、配置和管理原子服务间的工作步骤和调用关系。这种方法可以快速的实现业务功能,使非开发人员也能轻松地创建和管理服务的工作流程。 可视化服务编排工具提供了直观的界面和丰富的功能,以及便捷的执行…

【 ARMv9 Cluster BUS QoS 配置】

文章目录 ARM Cluster QoS ARM Cluster QoS QoS(Quality of Service,服务质量)在 ARM 架构中,主要指的是一种机制,它可以控制和管理系统资源(如内存、总线带宽等)的使用,以满足各种…

关于#include<bits/stdc++.h>的说明

关于#include<bits/stdc.h>的说明 在看C/C竞赛题时&#xff0c;经常看到#include<bits/stdc.h>。什么意思呢&#xff1f; 有人称<bits/stdc.h>为万能头文件。其 优点&#xff1a;   1、在竞赛中节约时间。   2、减少了编写所有必要头文件的工作量。  …

SSM - Springboot - MyBatis-Plus 全栈体系(二)

第一章 Maven 三、Maven 核心功能依赖和构建管理 1. 依赖管理和配置 Maven 依赖管理是 Maven 软件中最重要的功能之一。Maven 的依赖管理能够帮助开发人员自动解决软件包依赖问题&#xff0c;使得开发人员能够轻松地将其他开发人员开发的模块或第三方框架集成到自己的应用程…

安全帽人脸联动闸机开关算法

安全帽人脸联动闸机开关算法通过yolov7python网络模型深度学校框架 &#xff0c;安全帽人脸联动闸机开关算法能够判断人员是否穿戴规定的工装是不是现场人员&#xff0c;当穿戴合规且为现场人员&#xff0c;闸机门禁才打开。YOLO的结构非常简单&#xff0c;就是单纯的卷积、池化…

PostgreSQL中字符串与ASCII码互转

PostgreSQL中字符串与ASCII码互转 一、字符串转ASCII码&#xff1a;ascii ( text ) → integer二、ASCII码转字符串&#xff1a;chr ( integer ) → text 一、字符串转ASCII码&#xff1a;ascii ( text ) → integer 返回参数的第一个字符的数字代码。在UTF8编码中&#xff0c…

I IntelliJ IDEA 2023.2 最新解锁方式,支持java20

在 IntelliJ IDEA 2023.1 中&#xff0c;我们根据用户的宝贵反馈对新 UI 做出了大量改进。 我们还实现了性能增强&#xff0c;从而更快导入 Maven&#xff0c;以及在打开项目时更早提供 IDE 功能。 新版本通过后台提交检查提供了简化的提交流程。 IntelliJ IDEA Ultimate 现在支…

解决Docker镜像国内无法下载问题

近期由于docker镜像仓库禁止国内下载&#xff0c;国内各平台均无法下载更新最新docker镜像。为解决此问题&#xff0c;大家可以使用dockerproxy代理下载。 使用方法&#xff1a; 大家只需打开docker proxy代理网站&#xff0c;在快捷命令中输入所需镜像和版本&#xff0c;点击…

基于野狗算法优化的BP神经网络(预测应用) - 附代码

基于野狗算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于野狗算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.野狗优化BP神经网络2.1 BP神经网络参数设置2.2 野狗算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

23款奔驰S500加装车载冰箱系统,快乐就是这么朴实无华呀

凉爽餐饮随时触手可及。容积10升的可拆卸冷藏箱与后排扶手和谐融合。如此一来&#xff0c;即使在炎炎夏日&#xff0c;也可享受沁凉的冷饮。

cookie和session是什么,区别是什么?

cookie HTTP 协议中的 Cookie 包括 Web Cookie 和浏览器 Cookie&#xff0c;它是服务器发送到 Web 浏览器的⼀ ⼩块数据。服务器发送到浏览器的 Cookie&#xff0c;浏览器会进⾏存储&#xff0c;并与下⼀个请求⼀起发送到服务器。通 常&#xff0c;它⽤于判断两个请求是否来⾃…

ThinkPHP 文件上传 fileSystem 扩展的使用

ThinkPHP 文件上传 ThinkPHP 文件上传 扩展 filesystem一、安装 FileSystem 扩展二、认识 filesystem 配置文件 config/filesystem.php三、上传验证&#xff08;涉及到验证器的知识点&#xff09;四、文件上传demo ThinkPHP 文件上传 扩展 filesystem ThinkPHP 为我们 提供了 …

mybatisplus分页查询

简单的分页查询 步骤一&#xff1a;引入依赖 在项目的构建文件&#xff08;例如Maven的pom.xml&#xff09;中添加MyBatis-Plus的依赖&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/…

基于YOLOV8模型和Mot20数据集的行人目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOV8模型和Mot20数据集的行人目标检测系统可用于日常生活中检测与定位行人&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训…

vued3

vue的生命周期 创建 挂载&#xff0c;更新&#xff0c;销毁 工程模块化 全局注册&#xff0c;所有组件内都可以使用 创建vue文件&#xff08;三个部门组成&#xff09; main.js中进行全局注册 加上scope&#xff0c;只影响当前组件 事件监听 跨层级之间交流

并行计算之MPI简介以及基本通信案例(阻塞,非阻塞)

MPI是什么 Message Passing Interface 是一种消息传递编程模型&#xff0c;是这种模型的代表和事实上的标准&#xff0c;用于编写并行程序。主要思想是将一个程序分解为多个进程&#xff0c;这些进程相互通信并协作完成任务。MPI可以在多台计算机或者多个计算节点上执行&#…

js中forEach和map的区别:forEach不会改变原数组,而map会改变数组?错了错了

1.提出思考&#xff1f;forEach不会改变原数组&#xff0c;而map会改变数组&#xff1f; 看到掘金上一篇文章觉得很有意思&#xff1a;大致是描述一般面试官问js中forEach和map的区别&#xff1f;都会回答forEach不会改变原数组&#xff0c;而map会改变&#xff0c;我也一直对…

理虚实一体化全栈全场景云计算应用实训室解决方案

一、 云计算应用统概述 云计算应用系统是指基于云计算技术构建的应用系统&#xff0c;它将软件、数据、计算和存储资源部署在云服务器上&#xff0c;通过网络根据应用按照一定策略为用户提供相关服务。云计算应用系统广泛应用于各个领域&#xff0c;包括但不限于金融、教育、政…