JS设计模式(持续更新中)

1.单例模式

定义

一个类只产生唯一的实例

实现

  1. es5

    function Single(name) {
        this.instance = null;
        this.name = name;
        this.init();
    }
    Single.prototype.init = function() {
        console.log(this.name)
    }
    Single.getInstance = function(name) {
        if (!this.instance) {
            this.instance = new Single(name);
        }
        return this.instance;
    };
    var a = Single.getInstance('test_a')
    var b = Single.getInstance('test_b')
    console.log(a,b,a===b)
    

    这里会发现只打印了一次this.name,且this.nametest_a,而a和b是两个一样的对象

  2. es6

    class Single {
        constructor(name) {
            this.instance = null;
            this.name = name;
            this.init();
        }
        //保证可以直接引用getInstance方法,
        static getInstance(name) {
            if (!this.instance) {
             this.instance = new Single(name);
         }
             return this.instance;
        }
    }
    var a = Single.getInstance('test_a')
    var b = Single.getInstance('test_b')
    console.log(a,b,a===b)
    
  3. 惰性单例

    保证在需要的时候才创建对象实例

    var single = function (name) {
        console.log(name)
        return name
    };
    var getSingle = function (fn) {
        var result;
        return function () {
            return result || (result = fn.apply(this, arguments))
        }
    }
    var test = getSingle(single)
    test('test_a');
    test('test_b');
    

🌰

  1. 弹框

    假设一个场景,每次点击按钮弹出一个弹框,要通过创建一个div实现,创建时不必每次都创建一个新的div

    <button id="btn">创建</button>
    <script>
        var single = function () {
            var div = document.createElement("div");
            div.style.border = '1px solid #000';
            div.style.width = '200px';
            div.style.height = '80px';
            document.body.appendChild(div);
            return div;
        };
        var getSingle = function (fn) {
            var result;
            return function () {
                return result || (result = fn.apply(this, arguments))
            }
        }
        var createSingle = getSingle(single);
        document.getElementById("btn").onclick = function () {
            var div = createSingle();
            //div.style
        }
    </script>
    
  1. 验证

    一般经常写react项目,这中场景很少会遇到,我在想,做一些信息验证的时候是不是可以用到这种模式。

    假设我有很多地方有同样的逻辑,例如验证一部分信息,当然可以调用一个公共的函数,可是如果是一个异步的逻辑,用到Promise,每次都要new Promise,这样在相同的条件下就可以执行一次new Promise就可以了。

    class Verify {
        constructor() {
            this.instance = null;
            this.conditions = null;
        }
        static getInstance(conditions) {
            // 验证条件
            if (this.conditions !== conditions) {
                this.conditions = conditions;
                this.instance = new Promise(function (resolve, reject) {
                    //...
                })
            }
            return this.instance;
        }
    }
    // 其他地方调用
    let promise = Verify.getInstance(/*conditions*/);
    promise.then((res) => {
        //...
    })
    

2.工厂模式

定义

简单工厂:由一个工厂对象决定创建某一产品对象类的实例

工厂方法:对产品类抽象,创建多产品类的实例

抽象工厂:对类的工厂抽象,创建产品类簇,不会创建某一类产品实例

实现

  1. 简单工厂模式

    假如有很多类很类似,可以分为一类,我们就可以用简单工厂模式解决

    var Apple = function() {
        this.name = 'Apple';
    }
    Apple.prototype = {
        eat:function() {
            //...
        }
    }
    var Banana = function() {
        this.name = 'Banana';
    }
    Banana.prototype = {
        eat:function() {
            //...
        }
    }
    //定义一个水果工厂
    var Fruit = function(name) {
        switch(name){
            case 'apple':
                return new Apple();
            case 'banana':
                return new Banana();
        }
    }
    //当需要某个具体对象时,调用水果工厂就可以
    var apple = Fruit('apple')
    
  2. 工厂方法模式

    var Fruit = function(type) {
        //第一次进入时this是window
        if(this instanceof Fruit) {
            var fruit = new this[type]();
            return fruit;
         } else {
            return new Fruit(type);
         }
    }
    //工厂方法函数的原型中创建对象
    Fruit.prototype = {
         Apple: function() {
           this.name = "Apple",
           this.eat = function() {/* ... */}
         },
         Banana: function() {
           this.name = "Banana",
           this.eat = function() {/* ... */}
         },
    }
    var apple = Fruit('Apple');
    

    使用工厂方法模式,产品类就变成了抽象类。每次创建新的产品类只需要修改工厂函数的原型。

  3. 抽象工厂模式

    所谓抽象,就是父类抽象共同方法特性,但是具体实现由每个子类去实现该方式。

    let Food = function(subType, superType) {
         //是否存在抽象类
         if(typeof Food[superType] === 'function') {
            //缓存类
         function F() {};
         //继承父类属性和方法
         F.prototype = new Food[superType] ();
         //将子类的constructor指向子类
         subType.constructor = subType;
         //子类原型继承父类
         subType.prototype = new F();
         } else {
             throw new Error('抽象类不存在!')
         }
    }
    
    Food.Fruit = function() {
         this.type = 'fruit';
    }
    Food.Fruit.prototype = {
         eat: function() {
           return new Error('抽象方法不能调用');
         }
    }
    
    //水果子类
    function Fruit(name) {
         this.name = name;
    }
    //抽象工厂实现WechatUser类的继承
    Food(Fruit, 'Fruit');
    //重写抽象方法
    Fruit.prototype.eat = function() {
      //...
    }
    
    let apple = new Fruit('apple');
    
  4. 比较

    工厂方法是对简单工厂的抽象

    抽象工厂是在工厂方法的基础上进一步抽象

🌰

暂时想到对于功能相似组件的封装,(动态判断加载不同的组件、样式,角色权限,页面路由等等)可以用简单的工厂模式。一般不会用到抽象工厂。

3.建造者模式

定义

将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。

实现

//定义一个肉类
var Meat = function (name) {
    this.name = name
}
Meat.prototype.getPrice = function () {
    var price = 0;
    switch (this.name) {
        case 'Chicken':
            price = 10;
            break;
        case 'Pork':
            price = 20;
            break;
        case 'Beef':
            price = 30;
            break;
        default:
            break;
    }
    return price
}
//定义一个菜类
var Vegetables = function (name) {
    this.name = name
}
Vegetables.prototype.getPrice = function () {
    var _this = this;
    var price = 0;
    switch (this.name) {
        case 'Cabbage':
            price = 1;
            break;
        case 'Carrots':
            price = 2;
            break;
        case 'Potatoes':
            price = 3;
            break;
        default:
            break;
    }
    return price
}
//定义食物类
var Food = function(meat,vegetables) {
    this.meat = meat
    this.vegetables = vegetables
}
Food.prototype.getPrice = function() {
    var _this = this;
    var meat = new Meat(this.meat)
    var vegetables = new Vegetables(this.vegetables)
    return meat.getPrice() + vegetables.getPrice()
}
var food = new Food('Pork','Potatoes')
food.getPrice()

主要实现分布构建一个复杂的对象,使各个部分之间相互解耦

🌰

实现一个复杂的组件,例如react中拆分组件、实现笛卡尔积(如上)

不同工种薪资计算:

var Human = function(name) {
    this.name = name;
}
var Work = function(work) {
    var _this = this;
    (function(){
        switch(work){
            case 'Primary':
                _this.work = 'Primary';
                _this.price = 100;
                break;
            case 'Senior':
                _this.work = 'Senior';
                _this.price = 150;
                break;
            default:
                _this.price = 0;
                break;
        }
    })(work,_this)
}
Work.prototype.getPrice = function(time) {
    return time*this.price
}
var Person = function(name,work) {
    this.name = new Human(name);
    this.work = new Work(work)
}
var person = new Person('Tom','Senior')
console.log(person.name.name)
console.log(person.work.work)
console.log(person.work.getPrice(10))

4.观察者模式

定义

观察者模式又称发布-订阅(Publish/Subscribe)模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。例如dom事件,也是js和dom之间实现的一种观察者模式。

实现

function Observer() {
    //订阅列表
    this.subscribes = {};
}

Observer.prototype = {
    //订阅事件
    listen: function(eventType, fn){
        var _this = this;
        if(!(eventType in _this.subscribes)) {
            _this.subscribes[eventType] = [];
        }
        _this.subscribes[eventType].push(fn);
        return this;
    },
    // 触发事件(发布事件)
    publish: function(eventType){
        var _this = this;
        var fns = Array.prototype.slice.call(arguments,1);
        for(var i = 0; i < _this.subscribes[eventType].length; i++) {
            _this.subscribes[eventType][i].apply(_this,fns);
        }
        return this;
    },
    // 删除订阅事件
    remove: function(eventType, fn){
        var currentEvent = this.subscribes[eventType];
        var len = 0;
        if (currentEvent) {
            len = currentEvent.length;
            for (var i = len - 1; i >= 0; i--){
                if (currentEvent[i] === fn){
                    currentEvent.splice(i, 1);
                }
            }
        }
        return this;
    }
}
//订阅事件A
var o = new Observer();
o.listen('test', function(data){
    console.log(data);
});
var callback = function(data) {
    console.log(data)
}
o.listen('test', callback);
o.publish('test', 'first');
o.remove('test', callback);
o.publish('test', 'second');

5.原型模式

定义

原型实例指向创建对象的类,创建新的对象可以共享原型对象的属性和方法

实现

var Food = function(name) {
    this.name = name
}
Food.prototype.eat = function(){
    //...
}
var food1 = new Food('food1');
var food2 = new Food('food2');
console.log(food1.eat === food2.eat)//true


//Object.create
var food = {
    eat: function () { },
    name:''
};
// 使用Object.create创建
var food1 = Object.create(food);
food.name = 'food1'

如果有问题,希望指出

推荐阅读更多精彩内容