javascript设计模式 使用ES6语法

原创 2017年01月03日 10:26:45

参考《javascript设计模式》[美]Addy Osmani一书,下面介绍使用javascript经常会使用的主要设计模式。本博文为ES6语法的博客,还有使用ES5语法的【上】【下】两篇。
主要是以下几个设计模式:

  • Constructor Pattern 构造模式
  • Module Pattern 模块化模式
  • Revealing Module Pattern 揭露模块化模式
  • Singleton Pattern 单例模式
  • Observer Pattern 观察者模式
  • Mediator Pattern 中介者模式
  • Prototype Pattern 原型模式
  • Command Pattern 命令行模式
  • Facade Pattern 外观模式
  • Factory Pattern 工厂模式
  • Mixin Pattern 混入模式
  • Decorator Pattern 装饰者模式
  • Flyweight Pattern 享元模式

所有代码挂在我的github上,包含有ES5和ES6语法实现的内容。
https://github.com/zrysmt/javascript-design-pattern

1.Module Pattern 模块化模式

模块化很好理解,目前很多提供模块化的库如require.js(AMD),sea.js(CMD),现在我们就看看怎样自己编写的代码能够支持模块化。

let privateName = Symbol('privateName');//利用Symbol做成私有的变量
//直接用class类
class MyModule {
    set container(value) {
        this.value = value;
    }
    get container() {
        return this.value;
    }
    init() {
        this.value = 'ES6 module';
    }
    [privateName](){
      console.log("hi");
    }
}
export default Module;

2.Singleton Pattern 单例模式

确保实例化或者说是创建对象的时候只实例化/创建一次。

  • 一般的单例模式
let instance = null;
class mySingleton {
    constructor() {
        if (!instance) instance = this;
        return instance;
    }
    publicMethod() {
        console.log("The public can see me!");
    }
}

let singleton1 = new mySingleton();
  • 静态方法和单例模式

静态方法的解释不太容易说清楚,但可以从它的特点和用处来说明:
1)静态方法不会被继承
2)静态方法不用实例化(不用new)能够用直接调用([类名/对象名].[静态方法名])

class mySingleton {
    static getInstance() {
        if (!mySingleton.instance) {
            mySingleton.instance = new mySingleton();
        }
        return mySingleton.instance;
    }
    publicMethod() {
        console.log("The public can see me!");
    }
}
var cache = mySingleton.getInstance();

3.Observer Pattern 观察者模式

观察者一共有四个组件:

  • Subject: maintains a list of observers, facilitates adding or removing observers
    目标对象(类似接口,不具体实现,只有方法名)
  • Observer: provides a update interface for objects that need to be notified of a Subject’s changes of state
    观察者对象 主要是update方法(类似接口,不具体实现,只有方法名)
  • ConcreteSubject: broadcasts notifications to observers on changes of state, stores the state of ConcreteObservers
    具体目标对象,继承(实现)目标对象(实现接口)
  • ConcreteObserver: stores a reference to the ConcreteSubject, implements an update interface for the Observer to ensure state is consistent with the Subject’s
    具体观察者,继承(实现)观察者(实现接口)
/*Subject 目标*/
class Subject {
    addObserver() {
        throw new Error("This method must be overwritten!");
    }
    removeObserver() {
        throw new Error("This method must be overwritten!");    
    }
    notify() {
        throw new Error("This method must be overwritten!");
    }
}

class Observer {
    update() {
        throw new Error("This method must be overwritten!");
    }
}
//=============================================================
//具体的对象
class ControlCheckbox extends Subject {
    constructor() {
        super();
        this.observers = [];
    }
    addObserver(observer){
        this.observers.push(observer);
    }
    notify(context) {
        let observerCount = this.observers.length;
        for (let i = 0; i < observerCount; i++) {
           this.observers[i].update(context);
        }
    }
}


//具体的观察者
class AddedCheckboxs extends Observer{
    constructor(subject){
        super();
        console.log(subject);
        this.subject = subject;
        // this.subject.addObserver(this);
    }
    update(context){
        this.checked = context;
    }
}

//main test
let addBtn = document.getElementById("addNewObserver"),
    container = document.getElementById("observersContainer"),
    controlCheckboxDom = document.getElementById("mainCheckbox");
let controlCheckbox = new ControlCheckbox();
controlCheckboxDom.onclick = function(){
    controlCheckbox.notify(controlCheckboxDom.checked);//通知了变化
}

addBtn.onclick = function(){
    var check = document.createElement("input");
    check.type = "checkbox";
    //新增的每一个都应该实现观察者
    console.info(controlCheckbox.observers);//查看是否添加上
    check.update = AddedCheckboxs.prototype.update;
    controlCheckbox.addObserver(check);//添加到观察者列表上去
    container.appendChild(check);
}

其实我们现在用的最多的是它的变体-发布-订阅模式
简单解释下该模式,比如我们订阅了某些微信公众号,然后就等着别人发布信息,我们就能立刻接受到信息了

//发布-订阅模式
class Pubsub {
    constructor(){
        this.subUid = 0; //订阅的id值
        this.topics = {};//存放所有订阅者
    }
    publish(topic, args) {
        if (!this.topics[topic]) {
            return false;
        }

        let subscribers = this.topics[topic],
            len = subscribers ? subscribers.length : 0;

        while (len--) {
            subscribers[len].func(topic, args);
        }

        return this;
    }

    subscribe(topic, func) {
        if (!this.topics[topic]) {
            this.topics[topic] = [];
        }

        let token = (++this.subUid).toString();
        this.topics[topic].push({
            token: token,
            func: func
        });
        return token;
    }
    unsubscribe(token) {
        for (let m in this.topics) {
            if (this.topics[m]) {
                for (let i = 0, j = this.topics[m].length; i < j; i++) {
                    if (this.topics[m][i].token === token) {
                        this.topics[m].splice(i, 1);
                        return token;
                    }
                }
            }
        }
        return this;
    }
}

//usage
let messageLogger = function(topics, data) {
    console.log("Logging: " + topics + ": " + data);
};
let pubsub = new Pubsub();
let subscription = pubsub.subscribe("inbox/newMessage", messageLogger);

pubsub.publish("inbox/newMessage", "hello world!");

4.Mediator Pattern 中介者模式

该模式和发布订阅模式非常像,这里就不再重复了

5.Command Pattern 命令行模式

命令行模式就是类似控制台输入命令的方式。说白点就是我们只使用一个方法,第一个参数是我们实际调用的方法,后面的参数是作为该调用方法的参数。

class CarManager {
    requestInfo(model, id) {
        return "The information for " + model + " with ID " + id + " is foobar";
    }
    buyVehicle(model, id) {
        return "You have successfully purchased Item " + id + ", a " + model;
    }
    arrangeViewing(model, id) {
        return "You have successfully booked a viewing of " + model + " ( " + id + ") ";
    }
    execute(name) {
        let carManager = new CarManager();
        return carManager[name] && carManager[name].apply(carManager, [].slice.call(arguments, 1));
    }
}
let carManager = new CarManager();
console.log(carManager.execute("arrangeViewing", "Ferrari", "14523"));
console.log(carManager.execute("requestInfo", "Ford Mondeo", "54323"));
console.log(carManager.execute("requestInfo", "Ford Escort", "34232"));

6.Facade Pattern 外观模式

外观模式也是只暴露一个很简单的方法,然后该方法在内部执行,调用内部的其他方法,jquery使用了很多这种模式,如().css.ajax()等。

//装饰者模式
class Facade {
    _get() {
        console.log("current value:" + this.i);
    }
    _set(val) {
        this.i = val;
    }
    _run() {
        console.log("running");
    }
    _jump() {
        console.log("jumping");
    }

    facade(args) {
        this._set(args.val);
        this._get();
        if (args._run) {
            this._run();
        }
    }
}
let fa = new Facade();
fa.facade({ run: true, val: 10 });

7.Factory Pattern 工厂模式

怎么解释呢?工厂模式就是创建一个大型的制作工厂,然后其它的对象从这个工厂中产生。

class VehicleFactory {
    constructor() {
        this.vehicleClass = Car;
    }
    createVehicle(options) {
        if (options.vehicleType === "car") {
            this.vehicleClass = Car;
        } else {
            this.vehicleClass = Truck;
        }
        return new this.vehicleClass(options);
    }
}

class Car {
    constructor(options) {
        // some defaults
        options = options || "";
        this.doors = options.doors || 4;
        this.state = options.state || "brand new";
        this.color = options.color || "silver";
    }
}
class Truck {
    constructor(options) {
        this.state = options.state || "used";
        this.wheelSize = options.wheelSize || "large";
        this.color = options.color || "blue";
    }
}
//usage 
let carFactory = new VehicleFactory();
let car = carFactory.createVehicle({
    vehicleType: "car",
    color: "yellow",
    doors: 6
});

8.Mixin Pattern 混入模式

简单解释下,混入就是将一个对象的方法复制给另外一个对象。

//http://es6.ruanyifeng.com/#docs/class#Mixin模式的实现
function mix(...mixins) {
  class Mix {}
  for (let mixin of mixins) {
    copyProperties(Mix, mixin);
    copyProperties(Mix.prototype, mixin.prototype);
  }
  return Mix;
}
function copyProperties(target, source) {
  for (let key of Reflect.ownKeys(source)) {
    if ( key !== "constructor" && key !== "prototype" && key !== "name"
    ) {
      let desc = Object.getOwnPropertyDescriptor(source, key);
      Object.defineProperty(target, key, desc);
    }
  }
}
//使用-继承即可
class DistributedEdit extends mix(Loggable, Serializable) {
  // ...
}

9.Decorator Pattern 装饰者模式

装饰模式是只针对一个基本的对象,添加一些修饰。如下面的是对MacBook,加内存(Memory函数装饰)增加75美元,雕刻(Engraving函数装饰)增加200美元,买保险(Insurance函数装饰)增加250美元。

class MacBook {
    cost() {
        return 997;
    }
    screenSize() {
        return 11.6;
    }
}

function Memory(macbook) {
    let v = macbook.cost();
    macbook.cost = function() {
        return v + 75;
    };
}
// Decorator 2
function Engraving(macbook) {

    let v = macbook.cost();
    macbook.cost = function() {
        return v + 200;
    };
}
// Decorator 3
function Insurance(macbook) {
    let v = macbook.cost();
    macbook.cost = function() {
        return v + 250;
    };
}
let mb = new MacBook();
Memory(mb);
Engraving(mb);
Insurance(mb);
console.log(mb.cost());// Outputs: 1522
console.log(mb.screenSize());// Outputs: 11.6

10.Flyweight Pattern 享元模式

享元模式我感觉就是共享一些数据或者方法,有一个工厂可以管理

  • Flyweight
    享元对象(类似于接口),提供的可以共享的属性/方法;
  • Concrete Flyweight
    具体享元对象,实现接口,具体实现享元对象的方法;
  • Flyweight Factory
    享元工厂对象,创建并管理flyweight对象

实现接口的方法,由于js没有,我们需要模拟。
这部分没有必要完全使用ES6语法,请参考我的上一篇博客。

//在js模拟存虚拟的继承,类似java中的implements
Function.prototype.implementsFor = function(parentClassOrObject) {
    if (parentClassOrObject.constructor === Function) {
        this.prototype = new parentClassOrObject();
        this.prototype.consturctor = this;
        this.prototype.parent = parentClassOrObject.prototype;
    }else {
        //纯虚拟继承
        this.prototype = parentClassOrObject;
        this.prototype.constructor = this;
        this.prototype.parent = parentClassOrObject;
    }
}

// Flyweight object 享元对象
var CoffeeOrder = {
    // Interfaces 接口
    serveCoffee: function(context) {},
    getFlavor: function() {}
};
// ConcreteFlyweight object that creates ConcreteFlyweight 具体享元对象
function CoffeeFlavor(newFlavor) {
    var flavor = newFlavor;
    if (typeof this.getFlavor === "function") {
        this.getFlavor = function() {
            return flavor;
        };
    }
    if (typeof this.serveCoffee === "function") {
        this.serveCoffee = function(context) {
            console.log("Serving Coffee flavor " + flavor + " to table number " + context.getTable());
        };
    }
}
// Implement interface for CoffeeOrder 实现接口
CoffeeFlavor.implementsFor(CoffeeOrder);
// tableNumber 订单数 辅助器
function CoffeeOrderContext(tableNumber) {
    return {
        getTable: function() {
            return tableNumber;
        }
    };
}
//享元工厂对象
//创建并管理flyweight对象
function CoffeeFlavorFactory() {
    var flavors = {},
        length = 0;
    return {
        getCoffeeFlavor: function(flavorName) {
            //这是个单例模式
            var flavor = flavors[flavorName];
            if (flavor === undefined) {
                flavor = new CoffeeFlavor(flavorName);//创建flyweight对象
                flavors[flavorName] = flavor;
                length++;
            }
            return flavor;
        },
        getTotalCoffeeFlavorsMade: function() {
            return length;
        }
    };
}

// Sample usage: 测试
testFlyweight()
function testFlyweight() {
    // The flavors ordered. 已订购的flavors
    var flavors = new CoffeeFlavor(),
        // The tables for the orders. 
        tables = new CoffeeOrderContext(),
        // Number of orders made 订单数量
        ordersMade = 0,
        // The CoffeeFlavorFactory instance
        flavorFactory;
    //flavorIn 订单物的名称
    function takeOrders(flavorIn, table) {
        flavors[ordersMade] = flavorFactory.getCoffeeFlavor(flavorIn);
        //flavorFactory管理者创建好后(管理者也做了处理)返回给CoffeeFlavor
        tables[ordersMade++] = new CoffeeOrderContext(table);
    }

    flavorFactory = new CoffeeFlavorFactory();

    takeOrders("Cappuccino", 2);
    takeOrders("Cappuccino", 2);
    takeOrders("Frappe", 1);
    takeOrders("Frappe", 1);
    takeOrders("Xpresso", 1);
    for (var i = 0; i < ordersMade; ++i) {
        flavors[i].serveCoffee(tables[i]);
    }
    console.log("total CoffeeFlavor objects made: " + flavorFactory.getTotalCoffeeFlavorsMade());
}

所有代码挂在我的github上,包含有ES5和ES6语法实现的内容。
https://github.com/zrysmt/javascript-design-pattern

版权声明:本文为博主原创文章,转载请注明出处和原文链接。

相关文章推荐

React Native es6 单例例子

单例是在程序设计非常基础的东西,用React Native做了一个简单单例的例子,只是玩玩而已,也做了一个笔记。   单例的类,中间定了一个name的属性。 let instance = null...
  • arjick
  • arjick
  • 2016-06-19 16:25
  • 3704

JAVA中extends与implements详解

JAVA中extends与implements有啥区别? 1. 在类的声明中,通过关键字extends来创建一个类的子类。一个类通过关键字implements声明自己使用一个或者多个接口。 ext...

extends和implements区别

extends与implements的不同 1、在类的声明中,通过关键字extends来创建一个类的子类。 一个类通过关键字implements声明自己使用一个或者多个接口。  exte...
  • tolcf
  • tolcf
  • 2015-05-28 22:25
  • 18877

IE的URL的最大长度限制和如何解决URL最大长度的限制

做Web开发时经常涉及到页面之间的转跳,页面之间的转跳就会涉及到页面之间参数的传递,通过URL传递参数是常用的方法之一,但是微软说:"Maximum URL length is 2,083 chara...

javascript设计模式【上】

参考《javascript设计模式》[美]Addy Osmani一书,下面介绍使用javascript经常会使用的主要设计模式。本博文是使用ES5语法的【上】篇,还有一个【下】篇,ES6语法会单独写个...

JavaScript-观察者模式(publish/subscribe)

JavaScript-观察者模式(publish/subscribe)观察者模式又叫做发布订阅模式,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生改变时就会通...

ECMAScript 6规范总结(长文慎入)

闲话 学习ES6的动机起于对其promise标准的好奇,它与jQuery源码中Deferred不同,而且在异步编程中加入了Generator,在后续ES7中更有Async。这勾起我强烈的兴趣了解ES...

es6 javascript的 class 类的基本语法

1 概述 JavaScript 语言的传统方法是通过构造函数, 定义并生成新对象。 下面是一个例子。 function Point(x, y) { this.x = x; this.y =...

JavaScript设计模式 Item 6 --单例模式Singleton

1、单例模式的使用场景在使用一种模式之前,我们最好要知道,这种模式的使用场景。用了这么久的单例模式,竟全然不知!用它具体有哪些好处呢? 1.可以用它来划分命名空间(这个就是就是经常用的了) 2.利用分...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)