搞懂装饰器Decorator,控制反转IOC和依赖注入DI

如今的前端领域已经从后端语言中汲取了众多设计模式和概念,使得前端开发早已超越了早期jQuery那种简单纯粹的时期。在面试中,我们时常需要提及这些新颖的术语和设计模式,以展现自己对现代前端技术的深入理解。接下来,我将分享一下自己对于装饰器(Decorator)、控制反转(IOC)以及依赖注入(DI)的认知,若有不当之处,还请各位专家指正。

控制反转IOC

控制反转是一种设计原则,其核心思想是将原本由代码直接操控的对象的调用权交给第三方(如IoC容器)来控制,以解耦代码,提高可维护性。在前端开发中,我们可以通过依赖注入等方式实现控制反转,使得组件之间的依赖关系更加清晰和灵活。

控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。

依赖注入DI

依赖注入是一种设计模式,依赖注入是实现控制反转的一种具体方式。在依赖注入中,我们通过构造函数、属性或者方法等方式将依赖的对象或服务传递给需要它们的对象。这种方式有助于减少代码的耦合度,提高代码的可测试性和可维护性。

装饰器Decorator

首先要强调下这个是TypeScript上搞出来的并不是说javascript原生就支持。

装饰器模式允许我们动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更为灵活。在前端开发中,装饰器常用于增强类或者对象的功能,而不必修改其原有的代码。这种设计模式有助于提高代码的可维护性和复用性。

在语法上,装饰器有如下几个特征。

  1. 第一个字符(或者说前缀)是@,后面是一个表达式。
  2. @后面的表达式,必须是一个函数(或者执行后可以得到一个函数)。
  3. 这个函数接受所修饰对象的一些相关值作为参数。
  4. 这个函数要么不返回值,要么返回一个新对象取代所修饰的目标对象。

理论总结

看了上面的名词解释可能你还是不知道在说什么,简单一点来说就是,装饰器是ts提供的一种语法,控制反转是为解耦代码,依赖注入是实现控制反转的一种实现方法。

上代码

正常的js代码

js 复制代码
//a.js
class A {
  constructor(count, delay) {
    this.count = count;
    this.delay = delay;
  }
  deploy(event) {
    console.log(`${this.count}'eat----' ${event}`)
  }
}

//b.js
class B {
  constructor(dis, cy) {
    this.dis = dis;
    this.cy = cy;
  }
  start() {
    console.log(`start`)
  }
  stop() {
    console.log(`stop`)
  }
}

// use.js
import A from './a.js';
import B from './b.js';
class Use {
  constructor(count, delay, dis, cy) {
    console.log('use');
    this.a = new A(count, delay)
    this.b = new B(dis, cy)
  }
  run(){
    this.b.start()
  }

}
//main.js
import Use from './use.js';
const main = new Use(1,2,3,4);
main.run()

上面是我们一般写代码时会出现的方式,这是没什么问题的功能都可以实现,他作为业务代码可以,做为框架或者公共项目是不能这样干的。 这个代码问题是如果我要给A加一个属性xx这个时候除了A自己的构造函数需要修改连带Use这个构造函数也需要修改对于维护就会变的麻烦和修改范围太多,所以要解决这个问题就需要改造代码这个改造就是用控制反转思想。

控制反转修改的代码

js 复制代码
//a.js
class A {
  constructor(count, delay) {
    this.count = count;
    this.delay = delay;
  }
  deploy(event) {
    console.log(`${this.count}'eat----' ${event}`)
  }
}

//b.js
class B {
  constructor(dis, cy) {
    this.dis = dis;
    this.cy = cy;
  }
  start() {
    console.log(`start`)
  }
  stop() {
    console.log(`stop`)
  }
}

// use.js
class Use {
  constructor(a,b) {
    console.log('use');
    this.a = a;
    this.b = b;
  }
  run(){
    this.b.start()
  }

}

//main.js
import A from './a.js';
import B from './b.js';
import Use from './use.js';

const a = new A(1, 2);
const b = new B(3, 4);
const main = new Use(a,b);
main.run()

这个代码现在如果要修改A或者B就不需要去修改Use了只需要在IoC容器就是main修改。

代码只是表达思想不做其他考量

如果我们代码用ts去写可以更加的简化

使用ts写代码

ts 复制代码
//a.ts
class A {
  constructor(public count:number,  public delay:number) {
    console.log('初始化A')
  }
  deploy(event:Event) {
    console.log(`${this.count}'eat----' ${event}`)
  }
}
//b.s
class B {
  constructor(public dis:number, public cy:number) {
    console.log('初始化B')
  }
  start() {
    console.log(`start`)
  }
  stop() {
    console.log(`stop`)
  }
}

//use.ts
class Use {
  constructor(public a:A,public b:B) {
    console.log('初始化cUse')
  }
  run(){
    this.b.start()
  }

}
//main.js
import A from './a.ts';
import B from './b.ts';
import Use from './use.js';

const a = new A(1, 2);
const b = new B(3, 4);
const main = new Use(a,b);
main.run()

装饰器

就如上面的ts代码一样,在ts中实现依赖注入就会非常的简单,通过装饰器各大框架如nestjs,redi都实现了自己的依赖注入,这里就不说了大家自己去官方网站看吧。

对于装饰器了解更多大家可以看阮大神的文章装饰器

有了装饰器想修改类构造方法我们只需要返回一个函数就可以了

js 复制代码
function countInstances(value:any, context:any) {
let instanceCount = 0;

const wrapper = function (...args:any[]) {
instanceCount++;
const instance = new value(...args);
instance.count = instanceCount;
return instance;
} as unknown as typeof MyClass;

wrapper.prototype = value.prototype; // A
return wrapper;
}

@countInstances
class MyClass {}

const inst1 = new MyClass();
inst1 instanceof MyClass // true
inst1.count // 1
相关推荐
丁总学Java10 分钟前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
It'sMyGo20 分钟前
Javascript数组研究09_Array.prototype[Symbol.unscopables]
开发语言·javascript·原型模式
懒羊羊大王呀21 分钟前
CSS——属性值计算
前端·css
李是啥也不会36 分钟前
数组的概念
javascript
无咎.lsy1 小时前
vue之vuex的使用及举例
前端·javascript·vue.js
fishmemory7sec1 小时前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec1 小时前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
豆豆2 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
JUNAI_Strive_ving2 小时前
番茄小说逆向爬取
javascript·python
看到请催我学习2 小时前
如何实现两个标签页之间的通信
javascript·css·typescript·node.js·html5