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'

如果有问题,希望指出

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容