js设计模式

1、类的链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    let Animal = function (){
    	this.name = 'xx';
    	this.email = 'xx.xx.xx';
    };
    Animal.prototype = {
        // 多人开发时注意不要直接重写prototype,
        // 更好的做法是在prototype上挂载一个用于扩充它的方法:
        // Function.prototype.addMethod = (name, fn)=>{this[name = fn;];return this;};
        // var function = new Function();
        // function.addMethod('checkName', ()=>{...});
    	constructor: Animal,
    	checkName: () => {
    		console.log(this.name);
    		return this;
    	},
    	checkEmail: () => {
    		console.log(this.email);
    		return this;
    	}
    };
    
    let cat = new Animal();
    cat.checkEmail().checkName();//xx.xx.xx xx

返回实例,避免调用多次原型的方法。

2、闭包实现可访问私有变量的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    let Book = (()=>{
        let bookNum = 0;//静态私有变量
        function checkBook(name) { }//静态私有方法
        function _book(newId) {
            let name, price;
            function checkId(id){ }
            this.getName = ()=>{ }
            this.id = newId;
            this.setName(name);
        }
        _book.prototype = {
            isJsBook: false,
            display: ()=>{ }
        };
        return _book;
    });

这样在闭包中创建的构造函数,既是闭包又是可实例对象的函数,可访问到类函数作用域中的变量,如bookName。

3、对象的多继承

1
2
3
4
5
6
7
8
9
    Object.prototype.mixExtend = (...args) => {
        let i=1, len = args.length, arg;
        for (; i<len; i++) {
            arg = args[i];
            for(let property in arg) {
                this[property] = arg[property];
            }
        }
    }

4、类的安全模式

1
2
3
4
5
6
7
8
    let Factory = function(type, content) {
        if (this instanceof Factory) {
            let s = new this[type](content);
            return s;
        } else {
            return new Factory(type, content);
        }
    };

当在创建实例没有添加new关键字时,会通过instanceof判断this是否处于Factory原型链上,不是的话,再用new创建实例。

5、单例模式

1
2
3
4
5
6
7
8
9
10
11
    let A = {
        Methods: {
            method1: () =>{},
            method2: () =>{},
        },
        Ajax: {
            get: () =>{},
            post: () =>{},
        }
        //...
    };

这种模式可以有效的保护自己的代码不被修改,并且不影响到其他人的代码,非常适合维护,并且也很适合保护静态变量。

6、装饰者模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    let decorator = function(dom, fn) {
        let input = document.getElementById(dom);
        if (typeof input.onclick === 'function') {
            let oldClickFn = input.onclick;
            input.onclick = function() {
                oldClickFn();
                fn();
            }
        } else {
            input.onclick = fn;
        }
    };
    decorator('name_input', () => {
        document.getElementById('error_msg').style.display = 'none';
    });

装饰者者,再不修改原对象的基础上,通过对其进行包装拓展(添加属性或方法)使原有对象可以满足用户的更复杂需求。

7、享元模式

1
2
3
4
5
6
7
8
    let FlyWeight = {
        moveX: (x) => {
            this.x = x;
        },
        moveY: (y) => {
            this.y = y;
        }
    };

就是通过创造一个享元类,让其他类继承这个享元类,或者创建一些享元类的实例,从而获得很多具有相同属性、方法的对象。

减少了其他类重写时造成的不必要开销,减少内存被大量占用的可能,避免程序中的数据重复,优化了性能。

8、观察者模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    let Observer = (function(){
        let _message = {};
        return {
            regist: (type, fn)=>{//注册事件
                if (typeof __message[type] === 'undefined') {
                    __message[type] = [fn];
                } else {
                    __message[type].push(fn);
                }
            },
            fire: (type, args = [])=>{
                if (!__message[type]) {
                    return;
                }
                let events = {
                    type,
                    args
                }, i = 0; len = __message[type].length;
                for (; i<len; i++) {
                    __message[type][i].call(this, events);
                }
            },
            remove: ()=>{//清除事件
                if (__message[type] instanceof Array) {
                    let i = __message[type].length -1;
                    for (; i>=0; i--) {
                        __message[type][i] === fn && __message[type].splice(i, 1);
                    }
                }
            },
        }
    });
1
2
3
4
    Observer.regist('test', (e)=>{
        console.log(e.type, e.args.msg);
    });
    Observer.fire('test', {msg: '参数'});

这种模式又称发布-订阅模式,定义了一种依赖关系,解决了主体对象与观察者之间功能的耦合。通过这种模式,可以解决团队开发中最重要的模块通信问题。这是模块间解耦的一种可行方案。

9、状态模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    let Status = function() {
        let _currentState = {},
        states = {
            name: function() {
                //...
            },
            age: function() {
                //...
            },
            //...
        };
        let Action = {
            changeState: (...args) => {
                _currentState = {};
                if (args.length) {
                    for (let i=0; i<args.length; i++) {
                        _currentState[args[i]] = true;
                    }
                }
                return this;
            },
            goes: () => {
                Object.keys(_currentState).forEach((item, index)=>{
                    states[index] && states[index]();
                });
            }
        };
        return {
            change: Action.changeState,
            goes: Action.goes,
        }
    };
    let status = new Status();
    status.change('name').goes().change('age').goes();

状态模式解决了程序中臃肿的分支判断语句问题,将每个分支转化为一种状态独立出来,方便每种状态的管理又不至于每次执行时遍历所有分支。在程序中到底产出哪种行为结果,决定于选择哪种状态。

10、策略模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    let Price = function() {
        let stragtegy = {
            return30: (price) => {
                return price + parseInt(price / 100) * 30;
            },
            percent90: (price) => {
                return price *100 *90 / 10000;
            },
            //...
        };
        return (algorithm, price) => {
            return stragtegy[algorithm] && stragtegy[algorithm](price);
        }
    };

    let price = Price('return30', '313.45');

这种模式可以实现一些动画的封装、表单验证等功能,其特色是创建一系列策略算法,每组算法处理的业务都是相同的,并且相互独立,只是处理过程和结果不一样,所以它们是互相可以替代的,这样也解决了算法和使用者之间的耦合。

其缺点是,需要使用者充分了解每种算法的实现及功能,增加了使用成本。

11、备忘录模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    let Data = function() {
        let cache = {};
        return function(id, fn) {//id为唯一代表数据的映射
            if (cache[id]) {//代表缓存过此数据
                //执行相应地回调
                fn && fn();
            } else {
                //获取数据
                $.post('xxx.json', {
                    id: id
                }, (res) => {
                    //执行相应地回调
                    fn && fn();
                    cache[id] = res.data;
                });
            }
        }
    };

这样就能够避免我们在渲染相同数据时反复调接口破坏性能,降低数据获取成本,为将来某个时刻使用或恢复作准备。

12、