JavaScript设计模式

前言

最近看了一下《JavaScript设计模式》这本书,书中有很多知识点,一时之间消化不了,先记下来。
ps:另有部分内容参考Tom大叔博客深入理解JavaScript系列

构造器(Constructor)模式

Object构造器用于创建特定类型的对象——准备好对象以备使用,同时可接受构造器可以使用的参数,以在第一次创建对象时,设置成员属性和方法的值。

function Car(model, year, miles) {
    this.model = model;
    this.year = year;
    this.miles = miles;

    this.toString = function() {
        return this.model + " has done "+ this.miles + "miles";
    };
}

上面这就是一个简单的构造器模式版本,但有一个性能方面的问题:toString()这样的方法是为每个Car创建的新对象而分别重新定义的,如果同时创建多个对象情况下,资源浪费比较严重,所以我们一般使用下面的带原型(prototype)的构造器模式。
ps:一般推荐构造函数名以大写字母开头,以便区分普通函数。

function Car(model, year, mille) {
    this.model = model;
    this.year = year;
    this.mile = mile;
}

// 注意这里使用Object.prototype.newMethod为了避免重新定义prototype对象
Car.prototype.toString = function() {
    return this.model + " has done " + this.miles + " miles";
};

var landRover = new Car("Land Rover", 2015, 10000);

现在toString的单一实例就能够在所有Car对象之间共享。
上述在实例化landRover这个新对象的过程:

  1. 创建一个新对象,并让this指针指向这个新对象
  2. 将构造函数的prototype对象成员赋值给新创建对象的原型链上
  3. 执行构造函数体内的代码,初始化这个新对象
  4. 返回创建的新对象

模块(Module)模式

在js中,Module模式用于进一步模拟类的概念,通过这种方式,能够使一个单独的对象拥有公有/私有方法和变量,从而屏蔽来自全局作用域的特殊部分。(利用自执行函数和闭包概念)

var myNamespace = (function() {

    //私有计数器变量
    var myPrivateVar = 0;

    // 私有函数
    var myPrivateMethod = function(foo) {
        console.log(foo);
    };

    return {

        //公有变量
        myPublicVar: "foo",
        mYPublicFunction: function(bar) {
            myPrivateVar ++;
            myPrivateMethod(bar);
        }
    };
})();

单例(Singleton)模式

“单例”顾名思义就是它限制了类的实例化次数只能一次。而最简单的单例模式就是对象字面量方式。

var mySingleton = {
    property: "myBlog",
    method: function() {}
};

而上述类的静态实例(对象)和Singleton之间区别:当Singleton可以作为一个静态的实例实现时,它也可以延迟构建,直到需要使用静态实例时,可以节约资源或内存。

//方式1
var mySingleton1 = (function() {
    //实例保持了Singleton的一个应用
    var instance;
    function init() {
        var privateVariable = "I'm private";
        function privateMethod() {
            console.log("I'm private");
        }

        return {
            publicProperty: "public",
            publicMethod: privateMethod
        };
    }

    return {
        getInstance: function() {
            if (!instance) {
                instance = init();
            }
            return instance;
        }
    };
})();

//方式2
var mySingleton2 = function() {
    //缓存实例
    var instance = this;
    //其它内容
    this.property = "value";

    //重写构造函数
    mySingleton2 = function() {
        return instance;
    };
};

观察者(Observer)模式

观察者模式又被称为发布/订阅(publish/subscribe)者模式,其中,一个对象(subject)维持一系列依赖于它的对象(观察者),将有关状态的任何变更自动通知给他们。使用观察者模式可以将应用程序分解为更小、更松散耦合的块,以促进代码管理和潜在复用。ps:该模式最直接的应用就是DOM事件绑定。

var pubSub = {};
(function(q) {
    var topics = {},
    subUid = -1;

    // 发布或广播事件,包含特定的topic名称和参数(比如传递的数据)
    q.publish = function(topic, args) {
        if (!topics[topic]) {
            return false;
        }
        var subscribers = topics[toppic],
            len = subscribers ? subscribers.length : 0;
        while (len--) {
            subscribers[len].func(topic, args);
        }
        return this;
    };

    // 通过特定的名称和回调函数订阅事件,topic/event触发时执行事件
    q.subscribe = function(topic, func) {
        if (!topics[topic]) {
            topics[topic] = [];
        }
        var token = (++subUid).toString;
        topics[topic].push({
            token: token,
            func: func
        });
        return token;
    };

    // 基于订阅上的标记引用,通过特定topic取消订阅
    q.unsubscribe = function(token) {
        for (var m in topics) {
            if (topics[m]) {
                for (var i = 0, j = topics[m].length; i < j; i++) {
                    if (topics[m][i].token === token) {
                        topics[m].splice(i, 1);
                        return token;
                    }
                }
            }
        }
        return false;
    };
})(pubSub);

另外,可以通过jQuery事件绑定on/off方法很容易实现Publish/Subscribe模式:

(function ($) {
    var o = $({});
    $.subscribe = function () {
        o.on.apply(o, arguments);
    };
    $.unsubscribe = function () {
        o.off.apply(o, arguments);
    };
    $.publish = function () {
        o.trigger.apply(o, arguments);
    };
} (jQuery));

中介者(Mediator)模式

如果一个系统的各个组件之间看起来有太多的直接关系,也许是时候需要一个中心控制点,以便各个组件可以通过这个中心控制点进行通信。而中介者模式促进松散 耦合的方式是:确保组件的交互是通过这个中心点来处理的,而不是通过显示地应用彼此。这种模式可以帮助我们解耦系统并提高组件的可重用性。现实世界中典型的一个例子就是机场的交通控制系统,机场控制塔就是充当这个中间点身份。
另一个例子是DOM事件冒泡和事件委托。如果系统中所有的订阅针对的是文档document而不是单个node节点,则这个文档会有效地充当中介者。更高级别的对象承担了向订阅者通知有关交互事件的责任,而不是绑定到单个节点的事件。

var mediator = (function() {
    var topics = {},
        subscribe = function(topic, fn) {
            if (!topics[topic]) {
                topics[topic] = [];
            }
            topics[topic].push({
                context: this,
                callback: fn
            });
            return this;
        },
        publish = function(topic) {
            var args;
            if (!topics[topic]) {
                return false;
            }
            args = [].prototype.slice.call(arguments, 1);
            for (var i = 0, l = topics[topic].length; i < l; i++) {
                var subscription = topics[topic][i];
                subscription.callback.apply(subscription.context, args);
            }
            return this;
        };
        return {
            Publish: publish,
            Subscribe: subscribe,
            installTo: function(obj) {
                obj.subscribe = subscribe;
                obj.publish = publish;
            }
        };
})();

中介者与观察者
观察者模式中,不存在封装约束的单一对象。观察者Observer和具体类Subject是一起配合来维护约束的,沟通是通过多个观察者和多个具体类来交互的:每个具体类通常包含多个观察者,而有时候具体类里的一个观察者也是另一个观察者的具体类。而中介者模式通过限制对象严格通过Mediator进行通信来实现这一目的。

原型(Prototype)模式

原型模式是一种基于现有对象模板,通过克隆方式创建对象的模式。常见模式如下:不包含任何初始化的概念,仅是将对象链接至原型。

var beget = (function() {
    function F() {};
    return function(proto) {
        F.prototype = proto;
        return new F();
    };
})();

另外,ECMA5标准中定义了一个方法,Object.create(proto, [propertiesObject])创建一个拥有指定原型和若干个指定属性的对象,该方法可以轻易实现对象继承,而不用通过一个空函数做过渡。

工厂模式

工厂模式不显示地要求使用一个构造函数,通过提供一个通用的接口来创建对象,我们可以指定所希望创建的工厂对象的类型。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。

var Car = (function () {
    var Car = function (options) {
        this.doors = options.doors || 4;
        this.state = options.state || "brand new";
        this.color = options.color || "silver";
    };
    return function (options) {
        return new Car(options);
    };
})();

何时使用:

  • 当对象或组件设置涉及高复杂性
  • 当需要根据所在的不同环境轻松生成对象的不同实例时
  • 当处理很多共享相同属性的小型对象或组件时

装饰着(Decorator)模式

装饰着模式提供了将行为动态添加至系统的现有类的能力,并不严重依赖于创建对象的方式,而是关注扩展其额外功能。基本想法是:向基本对象添加(装饰)属性或方法,而不是进行子类化。
装饰者用于通过重载方法的形式添加新功能,该模式可以在被装饰者前面或者后面加上自己的行为以达到特定的目的,用于给不同的对象各自添加新行为。

// 被装饰的对象构造函数
function MacBook() {
    this.cost = function() {return 997;};
    this.screenSize = function() {return 11.6};
}

// Decorator 1
function Memory(macbook) {
    var v = macbook.cost();
    macbook.cost = function() {return v + 75;};
}

// Decorator 2
function Engraving(macbook) {
    var v = macbook.cost();
    macbook.cost = function() {return v + 200;};
}

// Decorator 3
function Insurance(macbook) {
    var v = macbook.cost();
    macbook.cost = function() {return v + 250;};
}

享元(Flyweight)模式

享元模式旨在通过与相关的对象共享尽可能多的数据来减少应用程序中内存的使用(如:应用程序配置,状态等)。
享元模式的应用方式有两种。第一种是用于数据层,处理内存中保存的大量相似对象的共享数据。第二种是用于DOM层,Flyweight可以用作中央事件管理器,来避免将事件处理程序添加到父容器中的每个子元素上,而是将事件处理程序附加到这个父容器上。
具体可以参考汤姆大叔的这篇博文——深入理解JavaScript系统(37):设计模式之享元模式

posted @ 2015-07-11 10:30  qingguoing  阅读(154)  评论(0编辑  收藏  举报