前端设计模式

工厂模式

工厂模式是 js 中最常用的一种用于创建对象的设计模式,其核心就是将逻辑封装在一个函数中不暴露创建对象的具体逻辑.
es5写法

  const Person = function () {
    const [name, age, sex] = [...arguments];
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.sayName = () => {
      console.log(`我叫 ${this.name},性别 ${this.sex},今年 ${this.age}`);
    }
  };

  const p1 = new Person('帅哥', 25, '男');
  const p2 = new Person('靓女', 25, '女');

  p1.sayName(); // 我叫 帅哥,性别 男,今年 25
  p2.sayName(); // 我叫 靓女,性别 女,今年 25

es6写法

  class Person {
    constructor(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    sayName() {
      console.log(`我叫 ${this.name},性别 ${this.sex},今年 ${this.age}`);
    }
  }

  const p1 = new Person('帅哥', 25, '男');
  const p2 = new Person('靓女', 25, '女');

  p1.sayName(); // 我叫 帅哥,性别 男,今年 25
  p2.sayName(); // 我叫 靓女,性别 女,今年 25

工厂模式的优缺点如下:

优点:能解决多个相似的问题。
缺点:不能知道对象识别的问题(对象的类型不知道)

单例模式

单例模式即一个类只能构造出唯一实例,单例模式的意义在于共享、唯一, Redux/Vuex 中的 Store、 或者业务场景中的购物车、登录框都是单例模式的应用。
ES5 写法:

  const Single = function (name, password) {
    this.name = name;
    this.password = password;
  };

  Single.prototype.login = (name, password) => {
    if (!this.only) {
      this.only = new Single(name, password);
    }
    return this.only;
  };

  let user1 = new Single().login('xiaoming', '123456');
  let user2 = new Single().login('xiaohua', '654321');

  console.log(user1 === user2); // true
  console.log(user1); // Single { name: 'xiaoming', password: '123456' }
  console.log(user2); // Single { name: 'xiaoming', password: '123456' }

es6写法:

  class SingletonLogin {
constructor(name, password) {
  this.name = name;
  this.password = password;
}
static getInstance(name, password) {
  // 判断对象是否已经被创建,若创建则返回旧对象
  if (!this.instance) {
    this.instance = new SingletonLogin(name, password);
  }
  return this.instance;
}
}

let obj1 = SingletonLogin.getInstance('xiaoming', '123456')
let obj2 = SingletonLogin.getInstance('xiaohua', '654321')

console.log(obj1 === obj2)    // true
console.log(obj1)           // SingletonLogin { name: 'xiaoming', password: '123456' }
console.log(obj2)           // SingletonLogin { name: 'xiaoming', password: '123456' }
代理模式和中介者模式
代理模式

代理模式强调的是个体。将原类(原方法体)进行封装,客户端只需要与代理进行交流,代理就是原类的一个替身。简而言之就是用一个对象代表另外一个对象。

比如:xiaoming 要给心动女孩送花,然后 xiaoming 不好意思,所以他摆脱他的好哥们帮忙送花,这个代理人就是好哥们。

  // 心动女孩
  const Girl = function () {
    this.name = '心动女孩';
    this.get = (person, gift) => {
      console.log(`${person} 送给 ${this.name} ${gift}`);
    };
  }

  // xiaoming
  const XiaoMing = function () {
    this.name = 'xiaoming';
    this.send = () => {
      return '99 朵玫瑰';
    }
  }

  // 好哥们
  const Friend = function (getUser) {
    this.getUser = new getUser();
    this.xiaoming = new XiaoMing();
    this.run = () => {
      this.getUser.get(this.xiaoming.name, this.xiaoming.send());
    }
  };

  // 好哥们帮忙给不同妹子送礼物
  const friend = new Friend(Girl);
  friend.run(); // 最终打印:xiaoming 送给 心动女孩 99 朵玫瑰
中介者模式

那么什么是中介者模式呢?

就是当好哥们想赚外快的时候,想起他曾经帮 xiaoming 跑过腿,那么他也可以帮其他人跑腿啊,于是好哥们变成了中间商,成立了快递公司。

  const Girl = function () {
    this.name = '心动女孩';
    this.get = (person, gift) => {
      console.log(`${person} 送给 ${this.name} ${gift}`);
    };
  }

  // xiaoming
  const XiaoMing = function () {
    this.name = 'xiaoming';
    this.send = () => {
      return '99 朵玫瑰';
    }
  }

  // 快递公司
  const Express = function (postUser, getUser) {
    this.postUser = new postUser();
    this.getUser = new getUser();
    this.run = () => {
      this.getUser.get(this.postUser.name, this.postUser.send());
    }
  };

  // 快递员
  const courier = new Express(XiaoMing, Girl);
  courier.run(); // 最终打印:xiaoming 送给 心动女孩 99 朵玫瑰

假设xiaoming 喜欢的女孩不在广州,那么就需要别人 “帮忙” 将礼物送给异地 girl。
而这种模式,就是中介者模式,作为一个中间人传递信息。
从中可以看出代理模式的好处:

代理对象可以代替本体被实例化,并使其可以被远程访问
可以把本体实例化推迟到真正需要的时候。

当然,有好就有坏,在代码中使用代理模式,这个中介会承担较多的责任,一旦中介(快递员)病了,那么你的花就送达不了了。
中介者使用场景:

var goods = {
  // 手机库存
  "red|32G": 3,
  "red|64G": 1,
  "blue|32G": 7,
  "blue|32G": 6,
};

// 中介者
var mediator = (function () {
  var colorSelect = document.getElementById("colorSelect");
  var memorySelect = document.getElementById("memorySelect");
  var numSelect = document.getElementById("numSelect");
  return {
    changed: function (obj) {
      switch (obj) {
        case colorSelect:
          // TODO
          break;
        case memorySelect:
          // TODO
          break;
        case numSelect:
          // TODO
          break;
      }
    },
  };
})();
colorSelect.onchange = function () {
  mediator.changed(this);
};
memorySelect.onchange = function () {
  mediator.changed(this);
};
numSelect.onchange = function () {
  mediator.changed(this);
};

发布订阅模式

发布-订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
看完定义有点懵?
现实生活中的发布-订阅模式,就好比 你 在网上看中一双鞋子,但是没货了,于是点击(订阅)了卖家的发货通知。等卖家有货之后,所有点击(订阅)了发货通知的买家,都会看到通知。
发布-订阅模式的优缺点:

  • 优点
    1.支持简单的广播通信。当对象状态发生改变时,会自动通知已经订阅过的对象,例如上面的举例。
    2.发布者与订阅者的耦合性降低。我是卖家我并不关心有多少人订阅了,我只要到货的时候,将货物数量更改下就行了。
  • 缺点
    1.耗时耗内存。创建订阅者需要消耗一定的时间和内存
    2.过度使用不好维护。

介绍Vue 中的 Object.defineProperty 和 Proxy 使用:

// 定义发布者
const seller = {};

// 缓存列表:存放订阅者的回调函数
seller.buyerList = [];

// 订阅方法
seller.listen = (user, fn) => {
  // 如果有人订阅了,那就将它存放到缓存列表中
  seller.buyerList.push(fn);
  console.log(`亲爱的 ${user},你已经订阅了新鞋发布`);
}

// 发布信息
seller.trigger = () => {
  const buyerList = seller.buyerList;
  for (let i in buyerList) {
    if (buyerList.hasOwnProperty(i)) {
      buyerList[i].call(this);
    }
  }
}

const buyer1 = seller.listen('xiaoming', () => console.log('颜色是黑色,尺码是 43 码')); // 亲爱的 xiaoming,你已经订阅了新鞋发布
const buyer2 = seller.listen('xiaozhang', () => console.log('颜色是红色,尺码是 44 码')); // 亲爱的 xiaozhang,你已经订阅了新鞋发布

// 假设 2 秒后到货
setTimeout(() => {
  seller.trigger();
}, 2000);

// 颜色是黑色,尺码是 43 码
// 颜色是红色,尺码是 44 码

这样就实现了简单的发布-订阅模式,如果卖家有不同商品,两个用户分别关注两个不同商品要怎么实现呢?小伙伴们可以思考思考。

Object.defineProperty 和 Proxy区别:

在 Vue 2.0 版本,我们通过 Object.defineProperty 来观察数据:

  const person = {
    name: 'xiaoming',
    age: 25,
  };

  Object.keys(person).forEach((key) => {
    Object.defineProperty(person, key, {
      enumerable: true,
      configurable: true,
      get: () => {
        console.log('get');
      },
      set: (newName) => {
        console.log(newName); //打印xiaozhang
      },
    });
  });

  person.name = 'xiaozhang'; // xiaozhang
  console.log(person); // { name: [Getter/Setter], age: [Getter/Setter] }


解释:

  • enumerable:对象属性是否可通过 for-in 循环,false 为不可循环,默认值为 true。
  • configurable:能否使用 ·、能否需改属性特性、或能否修改访问器属性,false 为不可重新定义,默认值为 true。

我们再来看看 Proxy:

  const queuedObserver = new Set();

  const observe = fn => queuedObserver.add(fn);
  const observeable = obj => new Proxy(obj, {
    set
  });

  const set = function (target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);
    queuedObserver.forEach(observer => observer());
    return result;
  };

  const person = observeable({
    name: 'xiaoming',
    age: 25,
  });

  const print = () => {
    console.log(`姓名:${person.name},年龄:${person.age}`);
  }
  observe(print);

  person.name = 'xiaozhang'; // 姓名:zhazhaliang,年龄:25

那么,为什么 Vue 3.0 要换成 Proxy 呢?(面试常考)

Object.defineProperty 缺点:
因为:
1.不能监听数组变化
2.必须遍历对象每个属性,而 Proxy 的目标是对象,这样就省去了属性的遍历。

最后,需要指正一点的是,ES6 Proxy 在模式上属于代理模式。

日常工作中,常见的观察者模式有:
Promise
Vue 的 Watch 声明周期钩子

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值