JS设计模式

380 阅读4分钟

创建型模式

工厂模式

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象,它提供了一种创建对象的最佳方式。
个人理解类似于工厂车间,可以生产指定产品

function函数示例:

function factoryMode(name, age) {
 const obj = { name, age };
 return obj;
}
const zs = factoryMode('张三', 18);
const ls = factoryMode('李四', 20)

构造函数模式

调用构造函数 new fn() ,返回拥有指定特征的对象实例

和工厂模式有相同之处,不同如下:

1.没有显式的创建对象

2.直接将属性和方法赋给了this对象

3.没有return语句

new 做了以下4个步骤

1.创建一个新的对象

2.将构造函数的作用域付给新对象(因此this就指向了这个新对象)

3.执行构造函数中的代码(为这个新对象添加属性)

4.返回新对象

function函数示例:

function ConstructorMode(name, age) {
  this.name = name;
  this.age = age;
};
const zs = new ConstructorMode('张三', 18);
const ls = new ConstructorMode('李四', 20)

原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。它提供了一种创建对象的最佳方式。

将公共的方法,属性放的原型(prototype)上,避免重复定义,便于维护

function PrototypeMode(name, age) {
  this.name = name;
  this.age = age;
}
PrototypeMode.prototype.sayName = function () { 
 console.log('my name is', this.name);
}
const zs = new PrototypeMode('张三', 18);
const ls = new PrototypeMode('李四', 20);
zs.sayName();
ls.sayName();

单例模式

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象,它提供了一种创建对象的最佳方式。

funciton函数示例:

function singletonMode(name, age) {
  let instance = {};
  return function (name, age) {
    if (!instance.name) {
      instance.name = name;
      instance.age = age;
    }
    return instance;
  }
}
const instance = singletonMode();
const zs = instance('张三', 18);
const ls = instance('李四', 20);
console.log(zs === ls); // true

class类示例:

class SingletonModeClass {
  static instanceClass = {};
  constructor(name, age) {
    if (!this.constructor.instanceClass.name) {
      this.constructor.instanceClass.name = name;
      this.constructor.instanceClass.age = age;
    }
    return this.constructor.instanceClass;
  }
}
const zs2 = new SingletonModeClass('张三', 18);
const ls2 = new SingletonModeClass('李四', 20);
console.log(zs2 === ls2); // true

结构型模式

装饰者模式

装饰者模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。即:在不改变原有对象的基础之上,将功能附加到对象上

function函数示例:

function Dog() {
  this.name = '小黑';
  this.age = 2;
  this.skill = function () {
    console.log('汪汪汪');
  }
}
const xiaohei = new Dog();
function addWalk(dog) {
  const skill = dog.skill;
  dog.skill = function () {
    skill();
    console.log('我可以走');
  }
};
addWalk(xiaohei);
function addRun(dog) {
  const skill = dog.skill;
  dog.skill = function () {
    skill();
    console.log('我可以跑');
  }
};
addRun(xiaohei);
xiaohei.skill();

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。

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

即访问一个对象,不会直接访问到这个对象,而是访问到它的代理对象上,通过代理对象预处理业务逻辑,然后通知被代理对象

优点

1. 职责清晰

2. 高扩展性

3. 可以保护对象

4. 优化性能,减少开销很大的对象,可缓存结果

ES6 proxy 示例:

const person = {
  name: "张三",
  age: 18
};
const proxy = new Proxy(person, {  
get: function (target, propKey, receiver) {
    console.log(target, propKey, receiver);
    if (propKey in target) {
      return target[propKey];
    } else {
      throw new ReferenceError(`没有${propKey}属性`);
    }
  },
  set: function (target, propKey, value, receiver) {
    console.log(target, propKey, value, receiver);
    console.log(`${propKey}属性被设置成了20`);
    target[propKey] = 20;
  }
});
proxy.name       "张三"
proxy.sex        Uncaught ReferenceError: 没有sex属性
proxy.age = 100  age属性被设置成了20

比如明星的很多事务,都是经纪人帮忙处理,比如买东西,是否接受某个代言等

// 定义一个鞋子类
const Shoes = function (name) {
  this.name = name;
};
Shoes.prototype.getName = function () {
  return this.name;
};
// 添加了一个business方法,通过当前的时间来判断是否能买到鞋子
Shoes.prototype.business = function () {
  const curTime = new Date().getHours();
  return curTime >= 8 && curTime <= 20 ? `买到一双${this.getName()}` : '非营业时间!';
}
// 定义一个助理对象
const assistant = {
  buyShoes: function (shoes) {
    star.buyShoes(shoes.business());
  },
  pickUpWork: function (work) { 
   if (work.amt < 100000) {
      star.pickUpWork('出场费小于10万不接')
    } else if (!work.brand.includes('china')) {
      star.pickUpWork('不是国产品牌不接')
    } else {
      star.pickUpWork(`接了一个${work.brand}的活动,出场费${work.amt}`)
    }
  }
};
// 定义一个明星对象
const star = {
  buyShoes: function (res) {
    console.log(res);
  },
  pickUpWork: function (res) {
    console.log(res); 
 }};
assistant.buyShoes(new Shoes('鳄鱼皮鞋')); // 买了一双鳄鱼皮鞋||非营业时间!
assistant.pickUpWork({ amt: 100, brand: '李宁 china' }); // 出场费小于10万不接
assistant.pickUpWork({ amt: 1000000, brand: 'Nike' }); // 不是国产品牌不接
assistant.pickUpWork({ amt: 1000000, brand: '李宁 china' }); // 接了一个李宁 china的活动,出场费1000000

还有比如大图加载前会有一大块空白,这个时候一般要显示loading图或者占位图,也可以用代理模式的思想完成,如下:

const myImage = (function () {
  // 创建dom
  const imgNode = document.createElement('img');
  document.body.appendChild(imgNode);
  return {
    setSrc: function (src) {
      imgNode.src = src;
    }
  }
})();
const preImage = (function () {
  // 创建虚拟图片dom
  const img = new Image();
  // 要加载的图片完成后,替换src
  img.onload = function () {
    myImage.setSrc(img.src);
  };
  return {
    // 先给dom设置loading图片
    setSrc: function (src) {
      myImage.setSrc('https://z3.ax1x.com/2021/03/29/cCjyPf.png');
      // 设置虚拟图片dom的src
      img.src = src;
    }
  }
})();
preImage.setSrc('https://s1.ax1x.com/2020/05/09/YlFytA.jpg');

适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁,它结合了两个独立接口的功能,就像常见的mac转接头。示例如下:

// 普通接口
class Mac {
   getInterface() {
    return 'Mac接口';
  }
}
// 转换器
class Converter {
  constructor() {
    // 转换接口
    this.MacInstance = new Mac();
  }
  transfer() {
    // 获取要转换的接口
    const MacInterface = this.MacInstance.getInterface();
    return `将${MacInterface}转为通用接口`
  }
}
// 实例化转换器
const converter = new Converter();
// 调用转换功能
console.log(converter.transfer()); // 将Mac接口转为通用接口

行为型模式

观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。

class类示例:

// 被观察者,调用setState时触发观察者update
class Subject {
  constructor() {
    this.count = 0;
    // 观察者集合
    this.observerList = [];
  }
  // 获取state
  getCount() {
    return this.count;
  }
  // 设置state
  setState(count) {
    this.count = count;
    this.notifyAllObservers();
  }
  // 添加观察者
  addObserver(observer) {
    this.observerList.push(observer);
  }
  // 通知观察者更新
  notifyAllObservers() {
    this.observerList.forEach(observer => {
      observer.update();
    })
  }}
// 观察者 被观察者调用setState时触发update
class Observer {
  constructor(name, subject) {
    this.name = name;
    // 保存被观察者
    this.subject = subject;
    // 被观察者添加this观察者
    this.subject.addObserver(this);
  }
  // 更新
  update() {
    console.log(`${this.name}更新,count=${this.subject.getCount()}`)
  }
}
let subjectObj = new Subject();
let observer1 = new Observer('observer1', subjectObj);
let observer2 = new Observer('observer2', subjectObj);
subjectObj.setState(1);
subjectObj.setState(2);

以上就是个人总结的JS中的设计模式,如果有错误或者不严谨的地方,请给予指正,十分感谢!