JS设计模式-透过现象看本质

news2024/11/19 9:30:49

JS设计模式-透过现象看本质

  • 设计模式
  • SOLID设计原则
  • 创建型
    • 构造器模式
    • 工厂模式 - 简单工厂
    • 工厂模式 - 抽象工厂(开发封闭原则)
      • 构造器和简单、抽象工厂的区别
    • 单例模式
    • 原型模式
  • 结构型
    • 装饰器模式
    • 适配器模式
    • 代理模式
      • 事件代理 - 事件冒泡
      • 虚拟代理 - 通过Image加载图片
      • 缓存代理 - 缓存计算结果
      • 保护代理 - getter,setter保护核心数据
      • 其他代理
  • 行为型
    • 策略模式
    • 状态模式
      • 策略和状态模式区别
    • 观察者模式
      • 观察者模式/发布订阅模式
    • 迭代器模式
      • 内部迭代器
      • 外部迭代器

设计模式

在这里插入图片描述

  • 作用:在进行开发时,通过各种设计模式,快速在脑海中映射出它对应的解决方法

SOLID设计原则

  • 单一功能原则
  • 开放封闭原则

对拓展开放,对修改封闭;软件实体(类、模块、函数)可以扩展,但是不可修改;

  • 里式替换原则
  • 接口隔离原则
  • 依赖反转原则

创建型

构造器模式

  • 概念:将创建属性的过程单独封装。
  • 应用场景:有大量同样属性的对象时候。
  • 举例
//学校给小明老师安排了录入学生的任务

//录入第一个学生信息
const liLei = {
    name: '李雷',
    age: 25,
    career:'music',
}
//录入第二个学生信息
const hanMeiMei = {
    name: '韩梅梅',
    age: 24,
    career:'sports',
}
//后面发现还有500+学生,而每个学生的信息都是name,age,于是小明写出了自动创建学生的 User 函数
function User(name , age ,career) {
    this.name = name
    this.age = age
    this.career= career
}
//进行一个简单的调用,让程序自动地去读取数据库里面一行行的员工信息,然后把拿到的姓名、年龄塞进User函数里,
const user = new User(name, age,career)

工厂模式 - 简单工厂

  • 概念:将创建对象的过程单独封装。
  • 应用场景:有构造函数的地方,在写了大量构造函数、调用了大量的 new的时候。
  • 目的:实现无脑传参
//学校又给小明老师多加了要求,要写清楚指定学生的职责,语文课代表要会背古诗,音乐课代表要会唱歌等

//小明思考,那就再加个构造器
function SportsUser(name , age) {
    this.name = name
    this.age = age
    this.career = 'sports' 
    this.work = ['跑步','跳远', '打羽毛球'] 
}
function musicUser(name, age) {
    this.name = name 
    this.age = age
    this.career = 'music'
    this.work = ['高音', '低音', '中音']
}

//写到后面小明又想,那么多课代表,难道要写10多个,还要人为去判断该用哪个吗,于是乎,他又想到了工厂模式!
//学生信息函数
function User(name , age) {
    this.name = name
    this.age = age
    this.career = career 
    this.work = work
}
function Factory(name, age, career) {
    let work
    switch(career) {
        case 'sports':
            work =  ['跑步','跳远', '打羽毛球'] 
            break
        case 'music':
            work = ['高音', '低音', '中音']
            break
        case 'xxx':
            // 其它课代表的职责分配
            ...
            
    return new User(name, age, career, work)
}
//进行一个简单的调用,让程序自动地去读取数据库里面一行行的员工信息,然后把拿到的姓名、年龄塞进User函数里,
const Factory= new Factory(name, age, career)

工厂模式 - 抽象工厂(开发封闭原则)

简单工厂的弊端,以上节代码为例

  • 数据管理混乱,普通学生和职能学生都在一起,且每个可能又不一样的权限,安全委员拥有钥匙权限,而其他没有的,且后期会有其他,转学生,交换生等,新增一个就要去重写函数,会过于庞大及混乱,不易维护
  • 不易迭代测试,所有逻辑都在一个函数体,改变一个需要全部回归测试
定义
抽象工厂(抽象类,它不能被用于生成具体实例)用于声明最终目标产品的共性。在一个系统里,抽象工厂可以有多个(大家可以想象我 们的手机厂后来被一个更大的厂收购了,这个厂里除了手机抽象类,还有平板、游戏机抽象类等等),每一个抽象工厂对应的这一类的产品,被称为“产品族”。
具体工厂(用于生成产品族里的一个具体的产品)继承自抽象工厂、实现了抽象工厂里声明的那些方法,用于创建具体的产品的类。
抽象产品(抽象类,它不能被用于生成具体实例)上面我们看到,具体工厂里实现的接口,会依赖一些类,这些类对应到各种各样的具体的细粒度产品(比如操作系统、硬件等),这些具体产品类的共性各自抽离,便对应到了各自的抽象产品类。
具体产品(用于生成产品族里的一个具体的产品所依赖的更细粒度的产品)比如我们上文中具体的一种操作系统、或具体的一种硬件等。
  • 实例:假如现在我要开一个店,类型和人员都不确定,我只知道店必须有这两部分组成,所以我先来一个抽象类来约定住这台店的基本组成。
// 抽象工厂类【抽象类】
class BaseFactory {
  // 提供店铺类型的接口
  createStore() {
    throw new Error("抽象方法【createStore】不允许直接调用,需要重写");
  }
  // 提供服务人员的接口
  createServicePeople() {
    throw new Error("抽象方法【createServicePeople】不允许直接调用,需要重写");
  }
}
// 商店类【抽象类】
class Store {
  getAddress() {
    throw new Error("抽象方法不允许直接调用,需要重写");
  }
}

// 员工类【抽象类】
class Staff {
  getStaff() {
    throw new Error("抽象方法不允许直接调用,需要重写");
  }
}

// 定义完抽象类后,开始创建具体工厂
//我的第一家产业 /火锅店,万达一楼77号,服务员李四
// 具体工厂实现类 
class AchieveBaseFactory extends BaseFactory {
  createStore() {
    // 返回店铺类型
    return new HotPotStore();
  }
  createServicePeople() {
    // 返回服务人员信息
    return new WaiterStaff();
  }
}

// 创建服务员【实现类】
class WaiterStaff extends Staff {
  getStaff() {
    return "服务员, 李四";
  }
}

// 创建火锅商店【实现类】
class HotPotStore extends Store {
  getAddress() {
    return "火锅店,万达一楼77号";
  }
}
let myIndustry = new AchieveBaseFactory();


//我现在又有钱了,我再开一家 咖啡店,万象城二楼99号 厨师,张三   
// 具体工厂实现类 
class NewAchieveBaseFactory extends BaseFactory {
  createStore() {
    return new CafeStore();
  }
  createServicePeople() {
    return new ChefStaff();
  }
}
// 创建咖啡商店【实现类】
class CafeStore extends Store {
  getAddress() {
    return "咖啡店,万象城二楼99号";
  }
}
// 创建厨师【实现类】
class ChefStaff extends Staff {
  getStaff() {
    return "厨师,张三";
  }
}
let newMyIndustry = new NewAchieveBaseFactory();

构造器和简单、抽象工厂的区别

定义
构造函数模式必须通过new去创建对象,解决的是多个对象实例的问题
工厂模式内部封装了创建对象的行为,主要用于无脑传参,解决的是多个类的问题-不符合开闭
抽象工厂抽象类的作用是用于定义范围和规则,通过继承重写的方式去具体实现功能-符合开闭

单例模式

  • 单例模式:不管创建多少次永远返回第一次创建的唯一实例
  • 实现单例方式:闭包、
  • 市场上哪些lib使用了单例:redux、vuex,保证全局只有一个store

保证一个类只有一个实例,实现方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象

const Singleton = function(name) {
  this.name = name;
}
// 引入代理类
const ProxySingleton = (function(){
  let instance;
  return function(name){
	if(!instance){
      instance = new Singleton(name);
	}
	return instance
  }
})();
// 使用 & 验证
const a = new ProxySingleton('instance1');
const b = new ProxySingleton('instance2');
console.log(a === b);		// true

原型模式

  • 用原型实例指向创建对象的类,使用于创建新的对象的类共享原型对象的属性以及方法
    在 JavaScript 中,每个构造函数都拥有一个prototype属性,它指向构造函数的原型对象
    这个原型对象中有一个 constructor 属性指回构造函数;
    每个实例都有一个__proto__属性,当我们使用构造函数去创建实例时,实例的__proto__属性就会指向构造函数的原型对象。
    具体来说,当我们这样使用构造函数创建一个对象时:
// 创建一个Dog构造函数
function Dog(name, age) {
  this.name = name
  this.age = age
}
Dog.prototype.eat = function() {
  console.log('肉骨头真好吃')
}
// 使用Dog构造函数创建dog实例
const dog = new Dog('旺财', 3)

在这里插入图片描述

// 输出"肉骨头真好吃"
dog.eat()
// 输出"[object Object]"
dog.toString()

明明没有在 dog 实例里手动定义 eat 方法和 toString 方法,它们还是被成功地调用了。
这是因为当我试图访问一个 JavaScript 实例的属性/方法时,它首先搜索这个实例本身;
当发现实例没有定义对应的属性/方法时,它会转而去搜索实例的原型对象;
如果原型对象中也搜索不到,它就去搜索原型对象的原型对象,这个搜索的轨迹,就叫做原型链。
在这里插入图片描述

结构型

装饰器模式

  • 在不改变原对象的基础上,通过对其进行包装拓展,使得原有对象可以动态具有更多功能,从而满足用户的更复杂需求求
  • 只添加不修改
  • 单一职责原则:拆分职责,方便复用
    在这里插入图片描述
  • 假设我们有一个计算函数,它的计算过程非常耗时,我们希望能够给它添加缓存功能,以提高性能。我们可以使用装饰器模式来实现这个功能
//定义装饰器函数
function cache(fn) {
  const cache = new Map();
  return function (num) {
    if (cache.has(num)) {
      console.log('Cache hit!');
      return cache.get(num);
    } else {
      console.log('Cache miss!');
      const result = fn(num);
      cache.set(num, result);
      return result;
    }
  };
}
//计算函数
function calculate(num) {
  console.log('Calculating...');
  let result = 0;
  for (let i = 0; i < num; i++) {
    result += i;
  }
  return result;
}

const cachedCalculate = cache(calculate);
console.log(cachedCalculate(10000000)); // Calculating... Cache miss! 49999995000000
console.log(cachedCalculate(10000000)); // Cache hit! 49999995000000
//当我们第一次调用cachedCalculate函数时,它会执行计算函数,并将结果缓存起来。当我们再次调用cachedCalculate函数时,它会直接从缓存中获取结果,而不需要重新计算

适配器模式

  • 如不同手机插口的转接头
  • 将一个类的接口转换成客户端所期望的接口,以便客户端可以使用这个类
  • 适配器在内部调整,装饰器在外部约束
// 定义一个需要被适配的函数
function square(x) {
  return x * x;
}

// 定义一个适配器函数,将输入参数转换为需要被适配函数的参数格式
function squareAdapter(obj) {
  return square(obj.num);
}

// 定义一个对象,它的接口不符合需要被适配函数的接口
let obj = {
  value: 5,
};

// 使用适配器函数将对象的接口转换为需要被适配函数的接口,并调用被适配函数
let result = squareAdapter({ num: obj.value });
console.log(result); // 25

//square() 是需要被适配的函数,它接受一个数字并返回它的平方。
//但是,我们有一个对象 obj ,它的接口不符合 square() 函数的接口(需要数字)。
//因此,我们需要编写一个适配器函数 squareAdapter() ,将obj对象的接口转换为 square() 函数的接口。

代理模式

为其他对象提供一种代理以控制对这个对象的访问。

事件代理 - 事件冒泡

虚拟代理 - 通过Image加载图片

为了延迟对象的创建或加载,而使用一个代理对象来代替真实对象,等到需要使用对象时才会真正地创建或加载。虚拟代理广泛应用在网络请求、大数据处理、图片加载等场景中.

//预加载图片
const image = (function () {
  const imgNode = document.createElement('img');
  document.body.appendChild(imgNode);
  return {
    setSrc: function (src) {
      imgNode.src = src;
    },
  };
})();

// 代理容器
const proxyImage = (function () {
  let img = new Image();
  // 加载完之后将设置为添加的图片
  img.onload = function () {
    image.setSrc(this.src);
  };
  return {
    setSrc: function (src) {
      image.setSrc('loading.gif');
      img.src = src;
    },
  };
})();
proxyImage.setSrc('https://image/path/file.jpg');
//如果使用 image.setSrc('https://image/path/file.jpg'),那么在图片被加载好之前,页面中有一段比较长的空白时间。于是我们引入 proxyImage,通过这个代理对象,在图片被真正加载好之前,页面中将出现 loading.gif 来占位,提示用户图片正在加载

缓存代理 - 缓存计算结果

为了避免重复计算或网络请求,而使用一个代理对象来缓存计算结果或网络请求结果,等到需要时直接调用缓存内容

let multi = function() {
  let result = 1;
  for (let i = 0; i < arguments.length; i++) {
    result *= arguments[i];
  }
  return result;
}
 
let proxyMulti = (function(){
  let cache = {};
  return function() {
    let args = Array.prototype.join.call(arguments, ',');
    if(args in cache) {
      return cache[args];
    }
    return cache[args] = multi.apply(this, arguments);
  }
})();
 
proxyMulti(1,2,3,4); // 24
proxyMulti(1,2,3,4); // 24(从缓存中直接读取结果)

保护代理 - getter,setter保护核心数据

为了控制用户的访问权限,而使用一个代理对象来做出决策或验证,等到确定用户有足够权限时再执行对真实对象的访问

// 例子:代理接听电话,实现拦截黑名单
var backPhoneList = ['189XXXXX140'];       // 黑名单列表
// 代理
var ProxyAcceptPhone = function(phone) {
    // 预处理
    console.log('电话正在接入...');
    if (backPhoneList.includes(phone)) {
        // 屏蔽
        console.log('屏蔽黑名单电话');
    } else {
        // 转接
        AcceptPhone.call(this, phone);
    }
}
// 本体
var AcceptPhone = function(phone) {
    console.log('接听电话:', phone);
};

// 外部调用代理
ProxyAcceptPhone('189XXXXX140'); 
ProxyAcceptPhone('189XXXXX141'); 

其他代理

  • 防火墙代理: 控制网络资源的访问,保护主机不让“坏人”接近。
  • 远程代理: 为一个对象在不同的地址空间提供局部代表, 在 Java 中,远程代理可以时另一个虚拟机中的对象。
  • 智能引用代理: 取代了简单的指针,它在访问对象时执行一些附加操作,比如计算一个对象被引用的次数。
  • 写时复制代理: 通常用于复制一个庞大对象的情况。写时复制代理延迟了复制的过程,当对象被真正修改时,才对它进行复制操作。写时- - 复制代理时虚拟代理的一种变体, DLL (操作系统中的动态链接库)时其典型运用场景。

行为型

策略模式

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

  • 原来写法
//实现一个计算员工奖金的程序,效绩为 S 则发基本工资的4倍,A 则3倍,以此类推。正常实现
        let bonus = function (performance, salary) {
            if(performance === "S") {
                return salary*4;
            }
            if(performance === "A") {
                return salary*3;
            }
            if(performance === "B") {
                return salary*2;
            }
        }
  • 优化写法
//该实现存在显著的缺点,如果随着效绩 的扩展,比如增加C,D,E, if 分支不断累加,使得代码越来越庞大
//使用策略
        // js中函数也是对象,直接将 strategy 定义为函数
        let strategy = {
            "S": function ( salary ){
                return salary*4;
            },
            "A": function ( salary ) {
                return salary*3;
            },
            "B": function ( salary ) { 
                return salary*2;
            }
        }
        let calculateBonus = function ( level, salary ) {
            return strategy[ level ]( salary );
        }
        console.log(calculateBonus('A', 20000)) // 6000

状态模式

当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化

  • 原来写法
//有一个咖啡机,有不同的开关,同一个开关按钮,在不同的状态下,表现出来的行为是不一样的
- 美式咖啡态(american):只吐黑咖啡
- 普通拿铁态(latte):黑咖啡加点奶
- 香草拿铁态(vanillaLatte):黑咖啡加点奶再加香草糖浆
- 摩卡咖啡态(mocha):黑咖啡加点奶再加点巧克力

changeState(state) {
    // 记录当前状态
    this.state = state;
    if(state === 'american') {
      // 这里用 console 代指咖啡制作流程的业务逻辑
      console.log('我只吐黑咖啡');
    } else if(state === 'latte') {
      console.log(`给黑咖啡加点奶`);
    } else if(state === 'vanillaLatte') {
      console.log('黑咖啡加点奶再加香草糖浆');
    } else if(state === 'mocha') {
      console.log('黑咖啡加点奶再加点巧克力');
    }
}


  • 优化写法
//该实现存在如下显著的缺点
1.状态之间的切换关系,是靠ifelse语句,增加或者修改一个状态可能需要改变若干个操作,这使代码难以阅读和维护

class CoffeeMaker {
  constructor() {
    /**
    这里略去咖啡机中与咖啡状态切换无关的一些初始化逻辑
  **/
    // 初始化状态,没有切换任何咖啡模式
    this.state = 'init';
    // 初始化牛奶的存储量
    this.leftMilk = '500ml';
  }
  stateToProcessor = {
    that: this,
    american() {
      // 尝试在行为函数里拿到咖啡机实例的信息并输出
      console.log('咖啡机现在的牛奶存储量是:', this.that.leftMilk)
      console.log('我只吐黑咖啡');
    },
    latte() {
      this.american()
      console.log('加点奶');
    },
    vanillaLatte() {
      this.latte();
      console.log('再加香草糖浆');
    },
    mocha() {
      this.latte();
      console.log('再加巧克力');
    }
  }

  // 关注咖啡机状态切换函数
  changeState(state) {
    this.state = state;
    if (!this.stateToProcessor[state]) {
      return;
    }
    this.stateToProcessor[state]();
  }
}

const mk = new CoffeeMaker();
mk.changeState('latte');


策略和状态模式区别

策略模式和状态模式的区别在于它们所关注的点不同,策略模式关注的是算法或行为的切换,状态模式关注的是对象的状态的切换。

观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新

// 创建一个观察者(订阅者)对象
class Observer {
  constructor() {
    this.observers = [];
  }

  // 添加观察者
  subscribe(callback) {
    this.observers.push(callback);
  }

  // 移除观察者
  unsubscribe(callback) {
    this.observers = this.observers.filter(observer => observer !== callback);
  }

  // 通知观察者
  notify(data) {
    this.observers.forEach(observer => observer(data));
  }
}

// 创建一个主题(被观察者)对象
class Subject {
  constructor() {
    this.observers = new Observer();
    this.state = 0;
  }

  // 设置状态并通知观察者
  setState(state) {
    this.state = state;
    this.observers.notify(this.state);
  }
}

// 创建观察者实例
const observerA = data => console.log(`Observer A: ${data}`);
const observerB = data => console.log(`Observer B: ${data}`);
const observerC = data => console.log(`Observer C: ${data}`);

// 创建主题实例
const subject = new Subject();

// 订阅观察者
subject.observers.subscribe(observerA);
subject.observers.subscribe(observerB);

// 设置主题状态,触发通知
subject.setState(1);

// 取消订阅 observerA
subject.observers.unsubscribe(observerA);

// 再次设置主题状态,触发通知
subject.setState(2);

// 添加一个新观察者
subject.observers.subscribe(observerC);

// 再次设置主题状态,触发通知
subject.setState(3);

应用场景:

  • DOM事件:可以将事件处理程序作为观察者,将事件对象作为主题,当事件发生时,通知所有的事件处理程序。
  • jQuery的自定义事件:可以使用观察者模式实现自定义事件,当自定义事件发生时,通知所有的注册事件处理程序。
  • 状态管理库(例如Redux、Vuex):状态管理库中的store可以作为主题,组件可以作为观察者,当状态发生变化时,通知所有的观察者。

观察者模式/发布订阅模式

  • 观察者模式(Observer Pattern)定义了一种一对多的关系,让多个订阅者对象同时监听某一个发布者,或者叫主题对象,这个主题对象的状态发生变化时就会通知所有订阅自己的订阅者对象,使得它们能够自动更新自己。
  • 发布订阅模式别名,非常形象地诠释了观察者模式里两个核心的角色要素——发布者和订阅者。
    发布-订阅模式有一个调度中心

迭代器模式

  • 提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示
  • map foreach every some

内部迭代器

  • 优点:内部迭代器不关心具体的实现,调用时非常方便
  • 缺点:因为内部迭代器的迭代规则已经被提前规定好了,如果我们想同时迭代2个数组,下面的each函数是无法实现的
function each(arr, callback) {
  // 对arr循环遍历,每一次遍历调用callback
  for (let i = 0, l = arr.length; i < l; i++) {
    callback.call(arr[i], i, arr[i]);
  }
}

  • 判断2个数组里元素的值是否完全相等
function compare(arr1, arr2) {
  // 如果两个数组长度不相同,不可能相等
  if (arr1.length !== arr2.length) {
    console.log("arr1和arr2不相等");
    return;
  }
  each(arr1, function (i, n) {
    // i为arr1每一项索引,n为arr1每项的值
    if (n !== arr2[i]) {
      console.log("arr1和arr2不相等");
      return;
    }
    console.log("arr1和arr2相等");
  });
}

外部迭代器

必须显式地请求迭代下一个元素,它增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,我们可以手工控制迭代的过程或者顺序

function Iterator(obj) {
  var current = 0; // 记录当前的索引

  // 下一个位置的索引
  var next = function () {
    current += 1;
  };

  // 是否已经迭代完成
  var isDone = function () {
    return current >= obj.length;
  };

  // 获取当前位置的数据
  var getCurrentItem = function () {
    return obj[current];
  };

  return {
    next,
    isDone,
    getCurrentItem,
  };
}

  • 判断2个数组里元素的值是否完全相等
function compare(iterator1, iterator2) {
  while (!iterator1.isDone() && !iterator2.isDone()) {
    if (iterator1.getCurrentItem() !== iterator2.getCurrentItem()) {
      console.log("arr1和arr2不相等");
      return;
    }
    iterator1.next();
    iterator2.next();
  }
  console.log("arr1和arr2相等");
}

var iterator1 = Iterator([1, 2, 3]);
var iterator2 = Iterator([2, 3, 4]);

compare(iterator1, iterator2); // arr1和arr2不相等

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

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

相关文章

前端开发攻略---用原生JS在网页中也能实现语音识别

1、语音识别的过程 语音识别涉及三个过程&#xff1a;首先&#xff0c;需要设备的麦克风接收这段语音&#xff1b;其次&#xff0c;语音识别服务器会根据一系列语法 (基本上&#xff0c;语法是你希望在具体的应用中能够识别出来的词汇) 来检查这段语音&#xff1b;最后&#xf…

【Qt 学习笔记】Qt常用控件 | 输入类控件 | Text Edit的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 输入类控件 | Text Edit的使用及说明 文章编号&#xff…

Jsoncpp搭建交叉编译环境(移植到arm)

1. 官网下载源码 github地址&#xff1a;GitHub - open-source-parsers/jsoncpp at update 2. 交叉编译环境 当前平台/开发平台-编译环境&#xff1a; [rootlocalroot ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootlocalroot ~]# uname -a Lin…

大型语言模型LLM的数据管理与应用

大型语言模型&#xff08;LLM&#xff09;风靡全球&#xff0c;尤其是 OpenAI 的最新发展。LLMs 的魅力来自于其理解、解释和生成人类语言的能力&#xff0c;而这曾被认为是人类的专属领域。像 CoPilot 这样的工具正在迅速融入开发人员的日常生活&#xff0c;而以 ChatGPT 为动…

使用 Redux 管理全局状态

Redux 是个状态集中管理框架&#xff0c;状态可以跨组件共享&#xff0c;状态更新后&#xff0c;调用监听器。其实状态可以认为就是个全局对象&#xff0c;为什么要做一个框架来管理呢&#xff1f;如果我们自己使用一个全解字典来管理状态是不是也行&#xff1f;如果不做任何控…

不同语言在算法使用方面的差异(Java 、C++篇)

由于我认为的会了是能得到结果了&#xff0c;所以我亲自去把题解的C代码给改成了Java的&#xff0c;尽管代码和逻辑上的高度统一。编译器还是报错了。 第三个死都过不去。而且后面的还超时了。 这使我十分怀疑是不是超时或者空间不够所导致的。但是去问讯飞星火&#xff0c;它…

自有道 更从容——林肯携“四大美式客厅”登陆北京国际车展,以传世豪华 优雅从容为品牌注入全新内涵

【北京 2024年4月25日】传世豪华&#xff0c;优雅从容。今日&#xff0c;第十八届北京国际汽车展览会正式启幕。作为美式豪华品牌的引领者&#xff0c;林肯在此次北京车展为“豪华 自有其道”注入全新内涵&#xff0c;并以车展首创的四大美式客厅形式诠释不同的人生境界&#x…

AWS制作WordPress在国内外的利弊?

AWS作为全球领先的云计算服务供应商&#xff0c;为WordPress提供了强大且灵活的托管环境&#xff0c;使用AWS来搭建和运行WordPress无疑是个不错的选择。即便如此使用AWS制作还是会有些许利弊&#xff0c;九河云作为AWS的合作伙伴来为读者们仔细探讨AWS在WordPress的利弊。 利&…

diskMirror docker 使用容器部署 diskMirror 服务器!!!

Welcome to diskMirror-docker 获取项目 这个项目是 diskMirror-spring-boot 镜像版本的项目&#xff0c;您可以使用下面的命令将此项目编译为一个镜像&#xff01; # 进入到您下载的源码包目录 cd diskMirror-docker# 点击脚本来进行版本的设置以及对应版本的下载 设置 和 编…

Linux 安装 nvm,并使用 Jenkins 打包前端

文章目录 nvm是什么nvm下载nvm安装设置 nvm 环境变量设置 Jenkins 打包命令 nvm是什么 nvm全英文也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm和n都是node.js版本管理工具&#xff0c;为了解决node.js各种版本存在不兼容现象可以通过它可以安装…

artifactory配置docker本地存储库

​一、概述 本地 Docker 存储库是我们部署和托管内部 Docker 镜像的位置。实际上&#xff0c;它是一个 Docker 注册表&#xff0c;能够托管的 Docker 镜像的集合。通过本地存储库&#xff0c;你可以保存、加载、共享和管理自己的 Docker 镜像&#xff0c;而无需依赖于外部的镜像…

简单的jmeter脚本自动化

1、创建线程组&#xff0c;定义自定义变量&#xff0c;保存请求默认值 2、用csv编写测试用例 3、使用csv文件读取测试用例 4、创建http请求 5、添加相应断言 6、运行结果

vue中使用echarts实现X轴动态时间(天)的折线图表

项目要求x轴以一天为间隔&#xff0c;时间是动态返回的数据&#xff0c;折线图平滑展示 实现代码如下&#xff1a; <div class"echarts-main"><v-chart ref"echarts" :options"options" /> </div>// 局部引入vue-echarts im…

ios CI/CD 持续集成 组件化专题一 iOS 将图片打包成bundle

一、 创建 选择 macos 下的Bundle 二 、取名点击下一步 三、Base SDK 选择ios 四 、Build Active Architecture Only 五、Installation后面的内容删除 六、Skip Install 选择NO 七、Strip Debug Symbols During Copy 中"Release"项设置为 "YES" 八、COM…

ansible-copy用法

目录 概述实践 概述 ansible copy 常用用法举例 实践 # with_fileglob 是 Ansible 中的一个循环关键字&#xff0c;用于处理文件通配符匹配的结果 # 遍历 addons/files/*.txt文件 # dest: /data/soft/test 目录要事先存在,才能正常的copy - name: Copy configuration filescop…

【threejs教程8】threejs添加3D场景键盘控制

【完整效果代码位于文章末】 目录 准备工作 目标 步骤1&#xff1a;初始化按键状态对象 步骤2&#xff1a;监听键盘事件 步骤3&#xff1a;编写事件处理函数 步骤4&#xff1a;更新相机移动方向 总结 完整代码如下 在3D应用开发中&#xff0c;用户交互是一个关键…

JS实现对用户名、密码进行正则表达式判断,网页跳转

目标&#xff1a;使用JS实现对用户名和密码进行正则表达式判断&#xff0c;用户名和密码正确时&#xff0c;进行网页跳转。 用户名、密码的正则表达式检验 HTML代码&#xff1a; <button type"submit" id"login-btn" /*onclick"login();alidate…

openstack界面简单修改

openstack Ubuntu主题登录界面修改修改登陆界面背景登录框边缘添加透明效果修改登录界面logo更换站点图片更换项目logo图片 本实验基于VMware17&#xff0c;使用Ubuntu2310搭建openstack-B版 Ubuntu主题 以下配置只对Ubuntu主题生效 登录界面修改 原界面 关闭登录界面域名输…

HTTP与HTTPS 对比,区别详解(2024-04-25)

一、简介 HTTP&#xff08;超文本传输协议&#xff0c;Hypertext Transfer Protocol&#xff09;是一种用于从网络传输超文本到本地浏览器的传输协议。它定义了客户端与服务器之间请求和响应的格式。HTTP 工作在 TCP/IP 模型之上&#xff0c;通常使用端口 80。 HTTPS&#xf…

百度沈抖:智能,生成无限可能

4月16日&#xff0c;Create 2024百度AI开发者大会在深圳举行。会上&#xff0c;百度集团执行副总裁、百度智能云事业群总裁沈抖正式发布新一代智能计算操作系统——百度智能云万源。它能管理万卡规模的集群&#xff0c;极致地发挥GPU、CPU的性能&#xff1b;它有强大的大模型作…