Javascript设计模式

12/6/2021 设计模式

# 创建型

# 工厂模式

let Factory = function (type,content){
    if(this instanceof Factory){
        let s = new this[type](content);
        return s;
    }else{
        return new Factory(type,content);
    }
}
Factory.prototype={
    a:function(){},
    b:function(){},
    //...
}
1
2
3
4
5
6
7
8
9
10
11
12
13

省去了创建时的new步骤,对类进行封装

# 建造者模式

function Person(name,work){
    let person = new Human();
    person.name = new Named(name);
    person.work = new Work(work);
    return person;
}
1
2
3
4
5
6

过程化创建对象

# 原型模式

var vehiclePrototype = {
    model:"car1",
    getModel: function () {
        console.log('车辆模具是:' + this.model);
    }
};

var vehicle = Object.create(vehiclePrototype,{
    "model":{
        value:"car2"
    }
});
vehicle.getModel();
1
2
3
4
5
6
7
8
9
10
11
12
13

将共有属性附加到原型上,其实继承也可以

# 单例模式

class DB{
    static getInstance(){
        if(!DB.instance){
            DB.instance = new DB();
        }
        return DB.instance;
    }
    constructor() {
        console.log(1);
    }
}
let a1 = DB.getInstance();
let a2 = DB.getInstance();
1
2
3
4
5
6
7
8
9
10
11
12
13

类只实例化一次,减少开销

# 结构型

# 外观模式

function addEvent(dom,type,fn){
    if(dom.addEventListener){
        dom.addEventListener(type,fn,false);
    }else if(dom.attachEvent){
        dom.attachEvent('on'+type,fn);
    }else{
        dom['on'+type] = fn;
    }
}
1
2
3
4
5
6
7
8
9

为复杂子系统接口提供一个高级的统一接口(封装来兼容功能)

# 适配器模式

class GooleMap {
    show() {
        console.log('渲染谷歌地图')
    }
}

class BaiduMap {
    display() {
        console.log('渲染百度地图')
    }
}


// 定义适配器类, 对BaiduMap类进行封装
class BaiduMapAdapter {
    show() {
        var baiduMap = new BaiduMap()
        return baiduMap.display() 
    }
}

function render(map) {
    if (map.show instanceof Function) {
        map.show()
    }
}

render(new GooleMap())
render(new BaiduMapAdapter())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

用来做接口的适配

# 代理模式

class MyImage {
    constructor() {
        this.img = new Image()
        document.body.appendChild(this.img)
    }
    setSrc(src) {
        this.img.src = src
    }
}

class ProxyImage {
    constructor() {
        this.proxyImage = new Image()
    }

    setSrc(src) {
        let myImageObj = new MyImage()
        myImageObj.img.src = 'file://xxx.png'
        this.proxyImage.src = src
        this.proxyImage.onload = function() {
            myImageObj.img.src = src
        }
    }
}

var proxyImage = new ProxyImage()
proxyImage.setSrc('http://xxx.png')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

为对象提供一个中间人来控制对对象的访问,如Es6中的Proxy

# 装饰着模式

let decorator = (type,fn)=>{
    let input = document.querySelector("type");
    if(typeof input.onclick === 'function'){
        let oldFn = input.onclick;
        input.onclick = function(){
            oldFn();
            fn();
        }
    }
}
decorator('tel_input',fn);
decorator('name_input',fn2);
1
2
3
4
5
6
7
8
9
10
11
12

在不改变对象的基础上,通过对其进行包装扩展

# 桥接者模式

function changeColor(dom,color,bg){
    dom.style.color = color;
    dom.style.background = bg;
}
1
2
3
4

强调抽离公共逻辑进行解耦

# 享元模式

var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){
   this.id = id;
   this.title = title;
   this.author = author;
   this.genre = genre;
   this.pageCount = pageCount;
   this.publisherID = publisherID;
   this.ISBN = ISBN;
   this.checkoutDate = checkoutDate;
   this.checkoutMember = checkoutMember;
   this.dueReturnDate = dueReturnDate;
   this.availability = availability;
};
Book.prototype = {
  getTitle: function () {
     return this.title;
  },
  getAuthor: function () {
     return this.author;
  },
  getISBN: function (){
     return this.ISBN;
  },
  updateCheckoutStatus: function( bookID, newStatus, checkoutDate , checkoutMember, newReturnDate ){
     this.id  = bookID;
     this.availability = newStatus;
     this.checkoutDate = checkoutDate;
     this.checkoutMember = checkoutMember;
     this.dueReturnDate = newReturnDate;
  },
  extendCheckoutPeriod: function( bookID, newReturnDate ){
      this.id =  bookID;
      this.dueReturnDate = newReturnDate;
  },
  isPastDue: function(bookID){
     var currentDate = new Date();
     return currentDate.getTime() > Date.parse( this.dueReturnDate );
   }
};
//如果使用每一个对象来管理一本书,迟早内存要寄
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//把每本书自身信息设置为内在状态,外在状态为借阅信息
var Book = function ( title, author, genre, pageCount, publisherID, ISBN ) {
    this.title = title;
    this.author = author;
    this.genre = genre;
    this.pageCount = pageCount;
    this.publisherID = publisherID;
    this.ISBN = ISBN;
};
//为每一个ISBN的书创建一个单例,节省重复ISBN构建的对象
var BookFactory = (function () {
  var existingBooks = {}, existingBook;
  return {
    createBook: function ( title, author, genre, pageCount, publisherID, ISBN ) {
      existingBook = existingBooks[ISBN];
      if ( !!existingBook ) {
        return existingBook;
      } else {
        var book = new Book( title, author, genre, pageCount, publisherID, ISBN );
        existingBooks[ISBN] = book;
        return book;

      }
    }
  };
});
// 外在状态单例,管理函数被一个外在对象所拥有而不是每一个实例对象
var BookRecordManager = (function () {
  var bookRecordDatabase = {};
  return {
    addBookRecord: function ( id, title, author, genre, pageCount, publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate, availability ) {
      var book = bookFactory.createBook( title, author, genre, pageCount, publisherID, ISBN );
      bookRecordDatabase[id] = {
        checkoutMember: checkoutMember,
        checkoutDate: checkoutDate,
        dueReturnDate: dueReturnDate,
        availability: availability,
        book: book
      };
    },
    updateCheckoutStatus: function ( bookID, newStatus, checkoutDate, checkoutMember, newReturnDate ) {
      var record = bookRecordDatabase[bookID];
      record.availability = newStatus;
      record.checkoutDate = checkoutDate;
      record.checkoutMember = checkoutMember;
      record.dueReturnDate = newReturnDate;
    },
    extendCheckoutPeriod: function ( bookID, newReturnDate ) {
      bookRecordDatabase[bookID].dueReturnDate = newReturnDate;
    },
    isPastDue: function ( bookID ) {
      var currentDate = new Date();
      return currentDate.getTime() > Date.parse( bookRecordDatabase[bookID].dueReturnDate );
    }
  };
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

使用共享事物,尽可能减少内存开销。享元模式分为外部状态和内部状态,通过设置不同的外部状态使得相同的对象具备一些不同的特性,而内部状态设置为相同部分。

# 行为型

# 观察者模式

class Subject {
  constructor() {
    this.observers = [];  // 观察者列表
  }
  // 添加
  add(observer) {
    this.observers.push(observer);
  }
  // 删除
  remove(observer) {
    let idx = this.observers.findIndex(item => item === observer);
    idx > -1 && this.observers.splice(idx, 1);
  }
  // 通知
  notify() {
    for (let observer of this.observers) {
      observer.update();
    }
  }
}

// 观察者类
class Observer {
  constructor(name) {
    this.name = name;
  }
  // 目标对象更新时触发的回调
  update() {
    console.log(`目标者通知我更新了,我是:${this.name}`);
  }
}

// 实例化目标者
let subject = new Subject();

// 实例化两个观察者
let obs1 = new Observer('前端开发者');
let obs2 = new Observer('后端开发者');

// 向目标者添加观察者
subject.add(obs1);
subject.add(obs2);

// 目标者通知更新
subject.notify();  
// 输出:
// 目标者通知我更新了,我是前端开发者
// 目标者通知我更新了,我是后端开发者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 策略模式

var fnA = function(val) {
    return val * 1
}

var fnB = function(val) {
    return val * 2
}

var fnC = function (val) {
    return val * 3
}


var calculate = function(fn, val) {
    return fn(val)
}

console.log(calculate(fnA, 100))// 100
console.log(calculate(fnB, 100))// 200
console.log(calculate(fnC, 100))// 300
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

工具,算法类的封装

class OffLightState {
    constructor(light) {
        this.light = light
    }
    pressBtn() {
        this.light.setState(this.light.weekLightState)
        console.log('开启弱光')
    }
}
class WeekLightState {
    constructor(light) {
        this.light = light
    }
    pressBtn() {
        this.light.setState(this.light.strongLightState)
        console.log('开启强光')
    }
}
class StrongLightState {
    constructor(light) {
        this.light = light
    }
    pressBtn() {
        this.light.setState(this.light.offLightState)
        console.log('关闭电灯')
    }
}

class Light {
    constructor() {
        this.offLightState = new OffLightState(this)
        this.weekLightState = new WeekLightState(this)
        this.strongLightState = new StrongLightState(this)
        this.currentState = null
    }
    setState(newState) {
        this.currentState = newState
    }
    init() {
        this.currentState = this.offLightState
    }
}

let light = new Light()
light.init()
var btn = document.getElementById('btn')
btn.onclick = function() {
    light.currentState.pressBtn()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

根据状态改变对象的行为,很容易增加新内容

# 架构篇

模块化、解耦、层次

# MVC

model|view|controller

# 数据层

MVC.model = function(){
    var M={};
    M.data={
        slideBar:[
            {
                //data1
            },
            {
                //data2
            }
        ]
    }
    M.conf={
        slideBarCloseAnimate:false
    }
    return {
        //接口(数据对象操作方法)方法
    }
}();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 视图层

MVC.view = function(){
    var M = M.model;
    var V = {
        createSlideBar:function(){
            //创建侧边栏
        }
    }
    return function(v){
        V[v]();
    }
}();
1
2
3
4
5
6
7
8
9
10
11

# 控制层

MVC.ctrl = function(){
	var V = MVC.view;
    var M = MVC.model;
    var C = {
        initSlideBar:function(){
            V('createSlideBar')
            //其他功能如点击和动画功能
        }
    }
    //遍历每个C的方法并执行
}();
1
2
3
4
5
6
7
8
9
10
11

但其实我们能发现视图层和数据层会耦合在一起。降低了视图创建的灵活性和复用性

# MVP

model|view|Presenter

view不直接使用model的数据,而是通过presenter层实现对model层的访问。所有层次的交互都发生在presenter层。

# 数据层

MVP.model = function(){
	var M = {};
    M.data = {}
    M.conf = {}
    return {
        getData:function(){},
        setConf:function(){},
        //...其他接口方法
    }
}()
1
2
3
4
5
6
7
8
9
10

# 视图层

MVP.view = function(){
    return //创建的模板html
}()
1
2
3

# 管理层

MVP.presenter = function(){
    var V = MVP.view;
    var M = MVP.model;
    var C = {};//模块
    return{
        init:function(){
            //遍历内部管理器
            for(var i in C){
               C[i]&&C[i](M,V,i); 
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
var C = {
    nav:function(M,V){
        var data = M.getData('nav');
        var tpl = V('创建模板的依据')//通过一个通用方法处理data,tpl
        //添加各种事件动画
    }
}
1
2
3
4
5
6
7
8

# MVVM

model|view|viewModel

量身定做一套视图模型,并在试图模型中添加创建属性和方法,为视图层绑定数据实现交互。直接通过创建视图实现页面需求。

# 视图层

<div class="first" data-bind="type:'slider',data:demo1"></div>
<div class="second" data-bind="type:'slider',data:demo2"></div>
<div class="third" data-bind="type:'progressbar',data:demo3"></div>
1
2
3

# 视图模型层

var VM = function(){
    var method = {
        //创建组件的策略方法
        progressbar:function(dom,data){
            //创建dom绑定数据,绑定事件,添加动画...
        },
        slider:function(dom,data){
            
        }
    }
    function getBindData(dom){
		var data = dom.getAttribute('data-bind');
        return !!data&&(new Function(`return ({${data}}))`)();//这里实现了数据层数据传输到视图层
    }
    return function(){
        //遍历所有UI组件
        //根据自定义属性中的组件类型,渲染该组件
        ctx=getBindData(dom[i])
        method[dom.type]&&mthod[dom.type](doms[i],ctx)
    }();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 数据层

var demo1={
    position:60,
    totle:200
}
window.onload=function(){
    VM();
}
1
2
3
4
5
6
7