JavaScript 设计模式 – 通过示例进行解释

大家好!在本文中,我将解释什么是设计模式以及它们为何有用。

  • 目录

    什么是设计模式?

    创意设计模式

    单例模式

    工厂方法模式

    抽象工厂模式

    建造者模式

    原型模式

    结构设计模式

    适配器模式

    装饰模式

    立面图案

    代理模式

    行为设计模式

    责任链模式

    迭代器模式

    观察者模式


    什么是设计模式?

设计模式因《设计模式:可重用面向对象软件的元素》一书而流行起来,该书由四位 C++ 工程师于 1994 年出版。

本书探讨了面向对象编程的功能和陷阱,并描述了 23 种有用的模式,您可以实现这些模式来解决常见的编程问题。

这些模式不是算法或特定的实现。它们更像是想法、观点和抽象概念,在某些情况下可用于解决特定类型的问题。

模式的具体实现可能会根据许多不同的因素而有所不同。但重要的是它们背后的概念,以及它们如何帮助我们为我们的问题找到更好的解决方案。

话虽这么说,请记住这些模式是在考虑 OOP C++ 编程的情况下想到的。当涉及更现代的语言(如 JavaScript 或其他编程范例)时,这些模式可能不会同样有用,甚至可能会向我们的代码添加不必要的样板文件。

尽管如此,我认为了解它们作为一般编程知识还是有好处的。

旁注:如果您不熟悉编程范例或OOP,我最近写了两篇关于这些主题的文章。

无论如何...现在我们已经完成了介绍,设计模式分为三个主要类别:创建模式、结构模式和行为模式。让我们简要探讨一下它们。

创意设计模式

创建模式由用于创建对象的不同机制组成。

单例模式

Singleton是一种设计模式,可确保一个类只有一个不可变的实例。简单地说,单例模式由一个无法复制或修改的对象组成。当我们想要为我们的应用程序提供一些不可变的单点事实时,它通常很有用。

举例来说,我们希望将应用程序的所有配置都放在一个对象中。我们希望禁止对该对象进行任何复制或修改。

实现此模式的两种方法是使用对象文字和类:

const Config = {start: () => console.log('App has started'),update: () => console.log('App has updated'),}// We freeze the object to prevent new properties being added and existing properties being modified or removedObject.freeze(Config)Config.start() // "App has started"Config.update() // "App has updated"Config.name = "Robert" // We try to add a new keyconsole.log(Config) // And verify it doesn't work: { start: [Function: start], update: [Function: update] }

使用对象字面量

class Config {constructor() {}start(){ console.log('App has started') }  update(){ console.log('App has updated') }}const instance = new Config()Object.freeze(instance)

使用类

工厂方法模式

工厂方法模式提供了一个用于创建对象的接口,这些对象在创建后可以进行修改。最酷的一点是,创建对象的逻辑集中在一个地方,从而简化并更好地组织我们的代码。

这种模式被大量使用,也可以通过两种不同的方式实现,通过类或工厂函数(返回对象的函数)。

class Alien {constructor (name, phrase) {this.name = name        this.phrase = phrase        this.species = "alien"}fly = () => console.log("Zzzzzziiiiiinnnnnggggg!!")sayPhrase = () => console.log(this.phrase)}const alien1 = new Alien("Ali", "I'm Ali the alien!")console.log(alien1.name) // output: "Ali"

使用类

function Alien(name, phrase) {this.name = name    this.phrase = phrase    this.species = "alien"}Alien.prototype.fly = () => console.log("Zzzzzziiiiiinnnnnggggg!!")Alien.prototype.sayPhrase = () => console.log(this.phrase)const alien1 = new Alien("Ali", "I'm Ali the alien!")console.log(alien1.name) // output "Ali"console.log(alien1.phrase) // output "I'm Ali the alien!"alien1.fly() // output "Zzzzzziiiiiinnnnnggggg"

使用工厂函数

抽象工厂模式

抽象工厂模式允许我们在不指定具体类的情况下生成相关对象系列。当我们需要创建仅共享某些属性和方法的对象时,它非常有用。

它的工作方式是通过呈现一个与客户端交互的抽象工厂。该抽象工厂根据相应的逻辑调用相应的具体工厂。该具体工厂就是返回最终对象的工厂。

基本上,它只是在工厂方法模式上添加了一个抽象层,以便我们可以创建许多不同类型的对象,但仍然与单个工厂函数或类交互。

让我们通过一个例子来看看。假设我们正在为一家汽车公司建模一个系统,该公司当然生产汽车,但也生产摩托车和卡车。

// We have a class or "concrete factory" for each vehicle typeclass Car {constructor () {this.name = "Car"this.wheels = 4}turnOn = () => console.log("Chacabúm!!")}class Truck {constructor () {this.name = "Truck"this.wheels = 8}turnOn = () => console.log("RRRRRRRRUUUUUUUUUMMMMMMMMMM!!")}class Motorcycle {constructor () {this.name = "Motorcycle"this.wheels = 2}turnOn = () => console.log("sssssssssssssssssssssssssssssshhhhhhhhhhham!!")}// And and abstract factory that works as a single point of interaction for our clients// Given the type parameter it receives, it will call the corresponding concrete factoryconst vehicleFactory = {createVehicle: function (type) {switch (type) {case "car":return new Car()case "truck":return new Truck()case "motorcycle":return new Motorcycle()default:return null}}}const car = vehicleFactory.createVehicle("car") // Car { turnOn: [Function: turnOn], name: 'Car', wheels: 4 }const truck = vehicleFactory.createVehicle("truck") // Truck { turnOn: [Function: turnOn], name: 'Truck', wheels: 8 }const motorcycle = vehicleFactory.createVehicle("motorcycle") // Motorcycle { turnOn: [Function: turnOn], name: 'Motorcycle', wheels: 2 }

建造者模式

Builder模式用于按“步骤”创建对象通常我们会有函数或方法向我们的对象添加某些属性或方法。

这种模式最酷的一点是我们将属性和方法的创建分离到不同的实体中。

如果我们有一个类或工厂函数,我们实例化的对象将始终具有该类/工厂中声明的所有属性和方法。但是使用构建器模式,我们可以创建一个对象并仅应用我们需要的“步骤”,这是一种更灵活的方法。

这与对象组合有关,这是我在这里讨论的主题。

// We declare our objectsconst bug1 = {name: "Buggy McFly",phrase: "Your debugger doesn't work with me!"}const bug2 = {name: "Martiniano Buggland",phrase: "Can't touch this! Na na na na..."}// These functions take an object as parameter and add a method to themconst addFlyingAbility = obj => {obj.fly = () => console.log(`Now ${obj.name} can fly!`)}const addSpeechAbility = obj => {obj.saySmthg = () => console.log(`${obj.name} walks the walk and talks the talk!`)}// Finally we call the builder functions passing the objects as parametersaddFlyingAbility(bug1)bug1.fly() // output: "Now Buggy McFly can fly!"addSpeechAbility(bug2)bug2.saySmthg() // output: "Martiniano Buggland walks the walk and talks the talk!"

原型模式

原型模式允许您使用另一个对象作为蓝图来创建一个对象,并继承其属性和方法

如果您使用 JavaScript 一段时间,您可能熟悉原型继承以及 JavaScript 的工作原理。

最终结果与我们使用类获得的结果非常相似,但具有更多的灵活性,因为属性和方法可以在对象之间共享,而不依赖于同一个类。

// We declare our prototype object with two methodsconst enemy = {attack: () => console.log("Pim Pam Pum!"),flyAway: () => console.log("Flyyyy like an eagle!")}// We declare another object that will inherit from our prototypeconst bug1 = {name: "Buggy McFly",phrase: "Your debugger doesn't work with me!"}// With setPrototypeOf we set the prototype of our objectObject.setPrototypeOf(bug1, enemy)// With getPrototypeOf we read the prototype and confirm the previous has workedconsole.log(Object.getPrototypeOf(bug1)) // { attack: [Function: attack], flyAway: [Function: flyAway] }console.log(bug1.phrase) // Your debugger doesn't work with me!console.log(bug1.attack()) // Pim Pam Pum!console.log(bug1.flyAway()) // Flyyyy like an eagle!

结构设计模式

结构模式是指如何将对象和类组装成更大的结构。

适配器模式

适配器允许两个具有不兼容 接口的对象相互交互。

例如,假设您的应用程序查询一个返回XML 的API ,并将该信息发送到另一个 API 来处理该信息。但处理 API 需要JSON。由于两个接口不兼容,因此您无法发送收到的信息。你需要先适应它

我们可以用一个更简单的例子来形象化相同的概念。假设我们有一系列城市和一个返回这些城市拥有的最大居民数量的函数。我们数组中的居民数量以百万为单位,但我们要添加一个新城市,其居民没有百万转换:

// Our array of citiesconst citiesHabitantsInMillions = [{ city: "London", habitants: 8.9 },{ city: "Rome", habitants: 2.8 },{ city: "New york", habitants: 8.8 },{ city: "Paris", habitants: 2.1 },] // The new city we want to addconst BuenosAires = {city: "Buenos Aires",habitants: 3100000}// Our adapter function takes our city and converts the habitants property to the same format all the other cities haveconst toMillionsAdapter = city => { city.habitants = parseFloat((city.habitants/1000000).toFixed(1)) }toMillionsAdapter(BuenosAires)// We add the new city to the arraycitiesHabitantsInMillions.push(BuenosAires)// And this function returns the largest habitants numberconst MostHabitantsInMillions = () => {return Math.max(...citiesHabitantsInMillions.map(city => city.habitants))}console.log(MostHabitantsInMillions()) // 8.9

装饰模式

装饰模式允许您通过将新行为放置在包含行为的包装对象内来将新行为附加到对象上。如果您对 React 和高阶组件 (HOC) 有一定的了解,这种方法可能会让您感到熟悉。

从技术上讲,React 中的组件是函数,而不是对象。但是,如果我们考虑一下 React Context 或Memo的方式,我们可以看到我们将一个组件作为子组件传递给这个 HOC,并且由于这个子组件能够访问某些功能。

在此示例中,我们可以看到 ContextProvider 组件正在接收子项作为 props:

import { useState } from 'react'import Context from './Context'const ContextProvider: React.FC = ({children}) => {const [darkModeOn, setDarkModeOn] = useState(true)const [englishLanguage, setEnglishLanguage] = useState(true)return (<Context.Provider value={{darkModeOn,setDarkModeOn,englishLanguage,setEnglishLanguage}} >{children}</Context.Provider>)}export default ContextProvider

然后我们将整个应用程序围绕它:

export default function App() {return (<ContextProvider><Router><ErrorBoundary><Suspense fallback={<></>}><Header /></Suspense><Routes><Route path='/' element={<Suspense fallback={<></>}><AboutPage /></Suspense>}/><Route path='/projects' element={<Suspense fallback={<></>}><ProjectsPage /></Suspense>}/><Route path='/projects/helpr' element={<Suspense fallback={<></>}><HelprProject /></Suspense>}/><Route path='/projects/myWebsite' element={<Suspense fallback={<></>}><MyWebsiteProject /></Suspense>}/><Route path='/projects/mixr' element={<Suspense fallback={<></>}><MixrProject /></Suspense>}/><Route path='/projects/shortr' element={<Suspense fallback={<></>}><ShortrProject /></Suspense>}/><Route path='/curriculum' element={<Suspense fallback={<></>}><CurriculumPage /></Suspense>}/><Route path='/blog' element={<Suspense fallback={<></>}><BlogPage /></Suspense>}/><Route path='/contact' element={<Suspense fallback={<></>}><ContactPage /></Suspense>}/></Routes></ErrorBoundary></Router></ContextProvider>)}

之后,使用该useContext钩子,我可以从应用程序中的任何组件访问上下文中定义的状态。

const AboutPage: React.FC = () => {const { darkModeOn, englishLanguage } = useContext(Context)return (...)}export default AboutPage

同样,这可能不是本书作者在撰写此模式时所想到的确切实现,但我相信想法是相同的。将一个对象放置在另一个对象中,以便它可以访问某些功能。;)

立面图案

外观模式为库、框架或任何其他复杂类集提供了简化的接口。

嗯……我们可能可以为此举出很多例子,对吗?我的意思是,React 本身或任何无数的库几乎用于与软件开发相关的任何内容。特别是当我们考虑声明式编程时,一切都是为了提供抽象,从而隐藏开发人员眼中的复杂性。

一个简单的例子就是 JavaScript 的mapsortreducefilter函数,它们的工作原理就像for底层的良好循环一样。

另一个例子可以是当今用于 UI 开发的任何库,例如MUI。正如我们在下面的示例中看到的,这些库为我们提供了带有内置特性和功能的组件,可以帮助我们更快、更轻松地构建代码。

但所有这些在编译后都会变成简单的 HTML 元素,这是浏览器唯一能理解的东西。这些组件只是抽象概念,旨在让我们的生活更轻松。

import * as React from 'react';import Table from '@mui/material/Table';import TableBody from '@mui/material/TableBody';import TableCell from '@mui/material/TableCell';import TableContainer from '@mui/material/TableContainer';import TableHead from '@mui/material/TableHead';import TableRow from '@mui/material/TableRow';import Paper from '@mui/material/Paper';function createData(name: string,calories: number,fat: number,carbs: number,protein: number,) {return { name, calories, fat, carbs, protein };}const rows = [createData('Frozen yoghurt', 159, 6.0, 24, 4.0),createData('Ice cream sandwich', 237, 9.0, 37, 4.3),createData('Eclair', 262, 16.0, 24, 6.0),createData('Cupcake', 305, 3.7, 67, 4.3),createData('Gingerbread', 356, 16.0, 49, 3.9),];export default function BasicTable() {return (<TableContainer component={Paper}><Table sx={{ minWidth: 650 }} aria-label="simple table"><TableHead><TableRow><TableCell>Dessert (100g serving)</TableCell><TableCell align="right">Calories</TableCell><TableCell align="right">Fat&nbsp;(g)</TableCell><TableCell align="right">Carbs&nbsp;(g)</TableCell><TableCell align="right">Protein&nbsp;(g)</TableCell></TableRow></TableHead><TableBody>{rows.map((row) => (<TableRowkey={row.name}sx={{ '&:last-child td, &:last-child th': { border: 0 } }}><TableCell component="th" scope="row">{row.name}</TableCell><TableCell align="right">{row.calories}</TableCell><TableCell align="right">{row.fat}</TableCell><TableCell align="right">{row.carbs}</TableCell><TableCell align="right">{row.protein}</TableCell></TableRow>))}</TableBody></Table></TableContainer>);}

代理模式

代理模式为另一个对象提供替代或占位符。这个想法是控制对原始对象的访问,在请求到达实际原始对象之前或之后执行某种操作。

再说一遍,如果您熟悉ExpressJS,这可能会让您感到熟悉。Express是一个用于开发NodeJS API的框架,它的特点之一就是使用中间件。中间件只不过是我们可以在任何请求到达端点之前、中间或之后执行的代码片段。

让我们看一个例子。这里我有一个验证身份验证令牌的函数。不要太关注它是如何做到这一点的。只需知道它接收令牌作为参数,一旦完成,它就会调用该next()函数。

const jwt = require('jsonwebtoken')module.exports = function authenticateToken(req, res, next) {const authHeader = req.headers['authorization']const token = authHeader && authHeader.split(' ')[1]if (token === null) return res.status(401).send(JSON.stringify('No access token provided'))jwt.verify(token, process.env.TOKEN_SECRET, (err, user) => {if (err) return res.status(403).send(JSON.stringify('Wrong token provided'))req.user = user      next()})}

该函数是一个中间件,我们可以通过以下方式在 API 的任何端点中使用它。我们只需将中间件放置在端点地址之后和端点函数声明之前:

router.get('/:jobRecordId', authenticateToken, async (req, res) => {try {const job = await JobRecord.findOne({_id: req.params.jobRecordId})res.status(200).send(job)} catch (err) {res.status(500).json(err)}})

这样,如果没有提供token或者提供了错误的token,中间件将返回相应的错误响应。如果提供了有效的令牌,中间件将调用该next()函数,接下来将执行端点函数。

我们可以在端点本身中编写相同的代码并验证其中的令牌,而不必担心中间件或任何东西。但问题是现在我们有了一个可以在许多不同端点中重用的抽象。

再说一次,这可能不是作者想要的确切想法,但我相信这是一个有效的例子。我们控制对象的访问,以便我们可以在特定时刻执行操作。

行为设计模式

行为模式控制不同对象之间的通信和职责分配。

责任链模式

责任链沿着处理程序链传递请求。每个处理程序决定要么处理请求,要么将其传递给链中的下一个处理程序。

对于此模式,我们可以使用与之前相同的示例,因为 Express 中的中间件在某种程度上是处理请求或将其传递给下一个处理程序的处理程序。

如果您想要另一个示例,请考虑您有某些信息需要通过多个步骤进行处理的任何系统。在每个步骤中,不同的实体负责执行操作,并且只有满足特定条件时信息才会传递给另一个实体。

一个使用 API 的典型前端应用程序可以作为示例:

  • 我们有一个负责渲染 UI 组件的函数。

  • 渲染后,另一个函数会向 API 端点发出请求。

  • 如果端点响应符合预期,则信息将传递到另一个函数,该函数以给定方式对数据进行排序并将其存储在变量中。

  • 一旦该变量存储了所需的信息,另一个函数就会负责将其呈现在 UI 中。

我们可以看到这里有许多不同的实体如何协作执行某个任务。他们每个人都负责该任务的一个“步骤”,这有助于代码模块化和关注点分离。👌👌

迭代器模式

迭代用于遍历集合中的元素。对于当今使用的编程语言来说,这听起来可能微不足道,但情况并非总是如此。

for不管怎样,我们可以用来迭代数据结构( 、forEachfor...offor...inmapreducefilter等)的任何 JavaScript 内置函数都是迭代器模式的示例。

与我们编写的任何遍历算法一样,可以迭代更复杂的数据结构(例如树或图)。

观察者模式

观察者模式允许您定义订阅机制来通知多个对象有关它们正在观察的对象所发生的任何事件基本上,这就像在给定对象上有一个事件侦听器,当该对象执行我们正在侦听的操作时,我们会执行某些操作。

React 的 useEffect 钩子可能是一个很好的例子。useEffect 的作用是在我们声明的那一刻执行给定的函数。

该钩子分为两个主要部分,可执行函数和依赖项数组。如果数组为空(如下例所示),则每次渲染组件时都会执行该函数。

  useEffect(() => { console.log('The component has rendered') }, [])

如果我们在依赖数组中声明任何变量,则仅当这些变量发生更改时该函数才会执行。

  useEffect(() => { console.log('var1 has changed') }, [var1])

即使是普通的旧式 JavaScript 事件监听器也可以被视为观察者。此外,响应式编程和像RxJS这样的库,用于处理系统中的异步信息和事件,也是这种模式的很好的例子。

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

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

相关文章

PostgreSQL docker compose安装配置

docker-compose.yml如下&#xff1a; version: 3services:postgres:image: postgres:15.4healthcheck:test: [ "CMD", "pg_isready", "-q", "-d", "postgres", "-U", "root" ]timeout: 45sinterval: 1…

修改Docker镜像默认下载地址

1、安装完docker desktop后&#xff0c;先不要打开 2、新建目录 D:\ProgramData\Docker 3、在C:\Users\你的用户名\AppData\Local下&#xff0c;打开cmd或者powershell执行以下命令&#xff0c;命令语法略有不同。 powershell命令&#xff1a; cmd /c mklink /J Docker D:\Pro…

1-5 AUTOSAR数据交换文件ARXML

目录 一、Arxml文件 二、各类ARXML文件 一、Arxml文件 arxml文件是AUTOSAR&#xff08;Automotive Open System Architecture&#xff09;标准定义的XML文件&#xff0c;用于描述汽车电子系统中的软件组件、通信接口和参数配置等信息。 arxml文件的主要作用是在AUTOSAR架构下…

golang教程 beego框架笔记一

安装beego 安装bee工具 beego文档 # windos 推荐使用 go install github.com/beego/bee/v2master go get -u github.com/beego/bee/v2masterwindows使用安装bee工具时碰到的问题&#xff1b; 环境配置都没有问题&#xff0c;但是执行官网的命令&#xff1a;go get -u github…

打造高效的私密论坛网站:Cpolar内网穿透+HadSky轻量级搭建指南

文章目录 前言1. 网站搭建1.1 网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3 Cpolar稳定隧道&#xff08;本地设置&#xff09;2.4 公网访问测试 总结 前言 经过多年的基础…

大数据和数据要素有什么关系?

大数据与数据要素之间存在密切的关系。大数据是指海量、多样化、高速生成的数据&#xff0c;而数据要素是指构成数据的基本元素或属性。数据要素包括但不限于数据的类型、结构、格式、单位、精度等。 大数据的产生和应用离不开数据要素的支持。数据要素确定了数据的基本特征和…

【网络基础】——HTTPS

目录 HTTPS背景知识 HTTPS是什么&#xff1f; 加密解密 为什么要加密 常见的加密方式 对称加密 非对称加密 数据摘要&&数据指纹 数字签名 HTTPS工作过程探究 方案1&#xff1a;只使用对称加密 方案2&#xff1a;只使用非对称加密 方案3&#xff1a;双方…

3分钟看懂NPDP| 超全版

NPDP考试是由美国产品开发与管理协会(PDMA)发起的产品经理国际资格认证考试&#xff0c;在中国国内由外专局培训中心举办考试&#xff0c;考生在报名参加NPDP考试前&#xff0c;需要了解NPDP&#xff0c;下面为大家详细介绍&#xff0c;供大家参考。 什么是NPDP? 产品经理国际…

RabbitMQ实现数据库与ElasticSearch的数据同步和分享文件过期处理

&#x1f388; 1 参考文档 RabbitMQ实现数据库与ElasticSearch的数据同步 | Hannya。-CSDN 企业级开发项目实战——基于RabbitMQ实现数据库、elasticsearch的数据同步 | 波总说先赚它一个小目标-CSDN SPringBoot集成RabbitMQ实现30秒过期删除功能 | 军大君-CSDN &#x1f50d; …

conda和Python的虚拟环境如何结合使用,以及二者之间到底有什么区别?

问题描述 今天在复现streamlit的代码时&#xff08;参考Streamlit 讲解专栏&#xff08;一&#xff09;&#xff1a;安装以及初步应用&#xff09;&#xff0c;根据这篇博文指导&#xff0c;要先用以下指令创建一个虚拟环境&#xff1a; # 创建虚拟环境&#xff08;使用venv&a…

【python】读取.dat格式文件

import binascii# 打开二进制文件以只读二进制模式 with open(EXCEL/文件.dat, rb) as file:binary_data file.read()print(binary_data)# 将二进制数据转换为十六进制字符串 hex_data binascii.hexlify(binary_data).decode(utf-8) # binary_data 现在包含了文件的二进制内容…

git标签基础

打标签:git可以给仓库历史中某个提交打上标签,以示重要,比较有代表人们会使用这个功能来标记发布结点(V1.0,V2.0) 列出本地标签: git tag --list git tag -l "V1.85*" 列出远端仓库的中所有标签 git ls-remote --tags给标签添加一些描述信息 git tag -a v1.3 -m …

热释电矢量传感器设计

1 概述 使用4个热释电传感器组成一个2X2的矩阵。通过曲线的相位差、 峰峰值等特征量来计算相关信息。本文使用STM32单片机设计、制作了热释电传感器矩阵&#xff1b;使用C#.NET设计了上位机软件。为以上研究做了试验平台。 2 硬件电路设计 2.1 热释电传感器介绍 热释电红外…

c++异步框架workflow分析

简述 workflow项目地址 &#xff1a; https://github.com/sogou/workflow workflow是搜狗开源的一个开发框架。可以满足绝大多数日常服务器开发&#xff0c;性能优异&#xff0c;给上层业务提供了易于开发的接口&#xff0c;却只用了少量的代码&#xff0c;举重若轻&#xff…

电子元器件选型与实战应用—04 LDO选型、特性及应用

文章目录 1. 参数详解1.1 输出电流1.2 输入电压1.3 输出电压1.4 功耗1.5 PSRR1.6 精度1.7 耗散功率1.8 热阻2. 特性详解2.1 OVP2.2 UVLO2.3 压差2.4 线性瞬态响应2.5 动态负载调整2.6 效率2.7 软启动2.8 输出自放电3. 工作原理4. 和DC-DC区别5. 使能脚设计6. layout7. 二极管和…

Fastjson_1.2.24_unserialize_rce漏洞复现

fastjson_1.2.24_unserialize_rce 说明内容漏洞编号CNVD-2017-02833漏洞名称FastJson < 1.2.24 远程代码执行漏洞评级高危影响范围1.2.24漏洞描述通过parseObject/parse将传入的字符串反序列化为Java对象时由于没有进行合理检查修复方案升级组件&#xff0c;打补丁&#xf…

9.11C高级day4

实现一个对数组求和的函数&#xff0c;数组通过实参传递给函数 sum0 function add() {for i in $*  #$*接收传来的所有参数do((sumi))doneecho $sum }#定义一个数组 arr(1 2 3 4 5 7 9)#向函数传参 add ${arr[*]}写一个函数&#xff0c;输出当前用户的uid和gid&#xff0c;并…

SQL6 查找学校是北大的学生信息

描述 题目&#xff1a;现在运营想要筛选出所有北京大学的学生进行用户调研&#xff0c;请你从用户信息表中取出满足条件的数据&#xff0c;结果返回设备id和学校。 示例&#xff1a;user_profile iddevice_idgenderageuniversityprovince12138male21北京大学Beijing23214male…

干了三年的功能测试,让我女朋友跑了,太难受了...

简单概括一下 先说一下自己的情况&#xff0c;普通本科&#xff0c;19年通过校招进入深圳某软件公司&#xff0c;干了3年多的功能测试&#xff0c;21年的那会&#xff0c;因为大环境不好&#xff0c;我整个人心惊胆战的&#xff0c;怕自己卷铺盖走人了&#xff0c;我感觉自己不…

【RabbitMQ】介绍及消息收发流程

介绍 RabbitMQ 是实现 AMQP&#xff08;高级消息队列协议&#xff09;的消息中间件的一种&#xff0c;最初起源于金融系统&#xff0c;用于在分布式系统中存储转发消息&#xff0c;在易用性、扩展性、高可用性等方面表现不俗。 RabbitMQ 主要是为了实现系统之间的双向解耦而实…