经常使用js设计模式

最近太懒了,今年很久没有下班后好好学习前端了,回家也不怎么写代码了,找不到刚参加工做第一年的时候,下班后还搞到凌晨1 2点的激情了,好多东西也都快忘记了javascript

因此,决定写下文章,逼着本身继续前进。。。先复习下,从设计模式开始前端

1. 单例模式

单例模式的重点在于: 屡次实例化这个对象,都只会被实例一次,后面的实例都会共享第一次。java

class Person () {

  static isInstance () {  // 单例模式核心函数,若是 Person.instance 存在则直接返回,不存在就 实例化 Person 对象并赋值
    if (!Person.instance) {
      Person.instance = new Person()
    }
    return Person.instance
  }

  constructor () {
    console.log(`我被实例化了`)
  }

  age () {
    console.log(`我已经18岁了`)
  }
}

let a = Person.isInstance()     // 我被实例化了
a.age()                         // 我已经18岁了

let b = Person.isInstance()     // 这里不会从新实例化对象,直接返回 第一次实例化的对象
b.age()                         // 我已经18岁了
复制代码

2. 发布订阅

从一个例子来讲明(假设的例子)设计模式

macbook pro 有两个型号的尺寸,13寸和15寸,在新款电脑价格出来以前,咱们须要一次次去官网查看价格,有没有什么办法让官方价格一出来,咱们就收到推送呢?数组

分析:咱们要收到推送,首先要先订阅,好比我是要 13 寸仍是 15 寸,若是要 13 寸,就订阅 13寸的推送,订阅好后,官方公布价格,要能响应这些推送函数

class Computer {
   constructor () {
     this.subList = {} // 保存全部的订阅
   }
 
   /** * 添加订阅 * 根据上面的例子,订阅须要订阅对象,好比订阅13寸仍是15寸 * 还须要定义怎么样的形式接收到推送,好比邮箱仍是短信 * @param key 订阅对象 * @param fn 接收形式 */
   addSubscribe (key, fn) {
     // 判断是否已经有这类订阅消息,好比订阅13寸,则全部13寸的订阅则放入同个数组
     if (!this.sublist[key]) {
       this.sublist[key] = [] // 若是没有这类订阅,则为这类订阅初始化
     }
     this.sublist[key].push(fn) // 将推送形式存入这类订阅中
   }
 
   /** * 发布消息 * 根据上面的例子,发布的时候须要发布对象,好比13寸的电脑 * 还须要发布对象的价格 * @param key 对象 * @param price 价格 */
   publish (key, price) {
     let fns = this.sublist[key] // 拿到这类定义的全部推送形式
     if (!fns || fns.length === 0) { // 若是没有定义推送形式
       console.log(`尚未订阅${key}的推送`)
       return
     }
     // 遍历推送形式数组,推送全部的订阅
     fns.map((fn) => {
       fn.call(this, price)
     })
   }
 }
 
 // -------------------- 分割线 ---------------------------------------
 
 let shop = new Computer()
 // 订阅
 shop.addSubscribe('13寸', (price) => {
   console.log(`我是经过短信接收的,13寸的价格为${price}`)
 })
 
 shop.addSubscribe('13寸', (price) => {
   console.log(`我是经过邮件接收的,13寸的价格为${price}`)
 })
 
 shop.addSubscribe('15寸', (price) => {
   console.log(`15寸的价格为${price}`)
 })
 
 // 发布
 shop.publish('13寸', 12000)
 shop.publish('15寸', 15000)
 
 
 // 输出
 // 我是经过短信接收的,13寸的价格为12000
 // 我是经过邮件接收的,13寸的价格为12000
 // 15寸的价格为15000
复制代码

3. Revealing Module (揭示模块) 模式

揭示模块模式实际上是 Module模式的稍改进版,由于使用module模式时,必需要切换到对象字面量表示法来让某种方法变成公有方法。因此揭示模块模式就是为了改进这种状况而出现的。学习

揭示模块模式可以在私有范围内简单定义 "全部的函数和变量",并返回一个匿名对象,它拥有指向私有函数的指针,该函数是他但愿展现为公有的方法。ui

var Car = function () {
    var type = '小汽车';  // 定义私有变量
    function year () {  // 定义私有函数
        return '2017'
    };
    
    var color = 'red';  // 定义共有变量
    function money (num) {  // 共有函数
        console.log('这是辆' + this.color + '的' + type + ', 须要' + num + '人民币')
    }
    
    // 将暴露的公有指针指向到私有函数和属性上
    return {
        setColor: color,
        getfun: money
    }
} ();
// 调用
console.log(Car.setColor); // red
Car.getfun(20000);    // 这是辆red的小汽车, 须要20000人民币
Car.setColor = 'blue';  // 修改公有变量
console.log(Car.setColor); // blue
Car.getfun(15000);    // 这是辆blue的小汽车, 须要15000人民币
复制代码

优势this

该模式可使脚本语法更加一致。在模块代码底部,它也会很容易指出哪些函数和变量能够被公开访问,从而改善可读性。spa

缺点

若是一个私有函数引用一个公有函数,在须要打补丁时,公有函数是不能被覆盖的。这是由于私有函数将继续引用私有实现,改模式并不适用于公有成员,只适用于函数。

4. 构造器模式

javascript 不支持类的概念,但它支持与对象一块儿用特殊的 construtor(构造器)函数。经过在构造器前面加 new 关键字,告诉 javascript 像使用构造器同样实例化一个新对象,而且对象成员由该函数定义。

1. 基本构造器模式

// 定义一个Car构造函数
function Car (color, year, money) {
    this.color = color;
    this.year = year;
    this.money = money;
    // 建立一个 toString 方法
    this.toString = function () {
      return 'this car is ' + this.color + ' create ' + this.year + ' need ' + this.money;
    }
}
// 建立 Car实例
var one = new Car('red', 2009, 200000);
var two = new Car('blue', 2016, 120000);
console.log(one.toString());
// this car is red create 2009 need 200000
console.log(two.toString());
// this car is blue create 2016 need 120000
复制代码

这是一个简单的构造器模式版本,可是会有一些问题:

  1. 继承变得困难
  2. 每次新定义对象而使用 Car 构造器时 toString()都会分别从新定义

这是很不理想的,咱们须要 toString() 这样的函数在全部的 Car 之间共享。因此下面升级一下这个设计模式。

2. 带原型的 Constructor

js中有一个名为 prototype 的属性。调用 js 构造器建立一个对象后,新的对象就会具备构造器原型的全部属性。经过这种方式,能够建立多个 Car 对象,并访问相同的原型。

将上面代码修改以下:

function Car (color, year, money) {
    this.model = model;
    this.year = year;
    this.money = money;
  }

  Car.prototype.toString = function () {
    return 'this car is ' + this.color + ' create ' + this.year + ' need ' + this.money;
  }
  var one = new Car('red', 2009, 200000);
  var two = new Car('blue', 2016, 120000);
  console.log(one.toString());
  // this car is red create 2009 need 200000
  console.log(two.toString());
  // this car is blue create 2016 need 120000
复制代码

这样 toString() 就能在全部的Car对象之间共享。

5. 模块 (module) 模式

Module 模式最初被定义为一种在传统软件工程中为类提供私有和共有的封装方法。

在 javascript 中,Module模式用于进一步模拟类的概念,经过这种方式,可以使一个单独的对象拥有 共有 / 私有方法和变量,从而屏蔽来自全局做用域的特殊部分。产生的结果是:函数名与页面上其余脚本定义的函数冲突的可能性下降。

Module 模式的实现

var Car = (function () {
    // 定义私有变量
    var type = '小汽车';
    // 定义私有函数
    var year = function () {
      return '2017'
    };
    // 返回一个暴露出的共有对象
    return {
      color: 'red',  // 共有变量
      money: function (num) {  // 共有函数
        console.log('这是辆' + this.color + '的' + type + ', 须要' + num + '人民币')
      }
    }
  })();
  // 调用
  console.log(Car.color); // red
  Car.money(20000);    // 这是辆red的小汽车, 须要20000人民币
  Car.color = 'blue';  // 修改公有变量
  console.log(Car.color); // blue
  Car.money(15000);    // 这是辆blue的小汽车, 须要15000人民币
复制代码

在上面代码中,咱们定义了一个Car来模拟一个类, Car中定义一个私有变量 type 和一个私有方法 year,这两个私有成员在 Car 的外部是访问不到的,只能在这个函数内部被访问;经过 return 一个对象,将公有的变量 color 和共有的函数 money 暴露在函数外面,可以让外部修改和访问。

Module 模式的优势:

  • 只有咱们的模块才能享有拥有私有函数的自由。由于他们不会暴露于页面的其他部分(只会暴露咱们输出的 API),咱们认为它们是真正私有的。
  • 鉴于函数每每已申明并命名,在试图找到有哪些函数抛出异常时,这将使得在调试器中显示调用堆栈变得更容易。

Module 模式的缺点:

  • 因为咱们访问共有和私有成员的方式不一样,当咱们想改变可见性时,实际咱们必需要修改每个曾经使用过该成员的地方。

------ 分割线-----------------------------------

未完待续。。。

相关文章
相关标签/搜索