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方法非常easy实现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):设计模式之享元模式

原文地址:https://www.cnblogs.com/lxjshuju/p/7216941.html