| 面向对象(类型判断、封装、多态、闭包、高阶函数)
| 单例模式 - 命名空间、只被实例化一次
| 工厂模式 - 加工在输厂的过程
| 外观模式 - 将复杂的接口统一,用于低层兼容
| 适配器模式 - 将一个接口转化为另一个接口
| 代理模式 - 一个对象不能直接访问另一个对象时,使用代理对象
| 装饰者模式 - 不改变原有对象的,并对其进行拓展
| 桥接模式 - 提取公用的方法,并通过桥接来进行连接
| 享元模式 -
| 观察者模式 -
| 状态模式 -
| 访问者模式 -
|
| 组合模式
| 策略模式 - 将一组算法封装起来,使其可以相互之间替换
| 中介者模式

类型判断

只能将带有duckSinging()方法的对象加入到 choir 数组中

<script>

    // 方法一
    var duck = {
        duckSinging: function(){
            console.log('嘎嘎嘎');
        }
    }

    // 方法二
    var chicken = {
        chicken: function(){
            console.log('嘎嘎嘎');
        }
    }

    // 存储带有duckSinging()方法的对象
    var choir = [];

    var joinChoir = function(animal){
        if(animal && typeof animal.duckSinging == 'function'){        // 只能加入带有duckSinging()方法的对象
            choir.push(animal);
            console.log('恭喜入合唱团');
            console.log('合唱团已有成员数量', choir.length);
        }
    }

    joinChoir(duck);
    joinChoir(chicken);

</script>

封装

封装: 目的是将信息隐藏, 其它语言通过private、public、protected来处理访问权限

js没有这些关键字通过变量的作用域来实现封装

<script>
    var Book = funtcion(id, bookname, price){
        // 私有属性
        var name = 1;

        // 私有方法
        function checkId(){ }

        // 公有属性
        this.id = id;
        this.bookname = bookname;

        // 公有方法
        this.copy = function(){}

        // 特权方法
        this.getName() = function{ return name }
    }

    // 在原型 (prototype) 来添加属性和方法,有两种方式
    // 1、为原型一一添加属性和方法
    // 2、将一个对象赋给原型

    Book.prototype.display = function(){
        // 展示这本书
    }

    Book.prototype = {            // 这种方法会将prototype下的constructor被覆盖掉
        display: function(){
            // 展示这本书
        }
    }
</script>

<script>
    // 闭包实现 1
    var Book = (function(){
        // 静态私有变量
        var bookNum = 0;

        // 静态私有方法
        function checkBook(name){}

        // 返回构造函数
        return function(newId, newName, newPrice){
            var name, price;
            function checkId(name){}
        }
    });

    Book.prototype = {
        isJSBook: false,
        display: function(){}
    }


    // 闭包实现 2
    var Book = (function(){

        // 私有变量
        var bookNum = 0;

        // 私有方法
        function checkBook(name){ }

        // 创建类
        function _book(newId, newName, newPrice){}
        _book.prototype = {}

        return _book
    })();
</script>

继承

一、类式继承

    将父类的实例赋给子类的原形,类式继承的原因: 类的原型对象作用就是为类的原型添加公有方法,但不能直接访问这些属性和方法,必须通过原型prototype来访问

    <script>

        // 父类
        function Parent(){
            this.name = 'siguang'
        }

        Parent.prototype.say = function(){ 
            console.log(this.name);
        }

        // 子类                
        function Childer(){}

        Childer.prototype = new Parent();   // 类式继承

        // 上一步执行后prototype的constructor会指向Parent,因为constructor指向还是在Paremt类上
        Childer.prototype.constructor = Childer;

        Childer.prototype.getSubValue = function(){
            console.log('getSubValue')
        }

        // 实例
        var oParent = new Parent();
        var oChilder = new Childer();

        oParent.say();                // siguang
        oChilder.say();                // siguang
        console.log(oChilder.name); // siguang
        oChilder.name = 'haha';
        oChilder.say();                // haha
        oChilder.getSubValue();        // getSubValue
    </script>


二、构造函数式继承

    /*
    * 构造函数继承
    * 继承非prototype下的属性和方法
    */
    <script>
        // 父类继承
        function SuperClass(id){
            this.books = ['javascript', 'html', 'css'];
            this.id = id;
            this.showId = function(){
                console.log(this.id);
            }
        }
        SuperClass.prototype.showBooks = function(){
            console.log(this.books);
        }

        // 声明子类
        function SubClass(id){
            // 继承父类所有公用属性和方法(this下的内容),但不继承prototype下的属性和方法
            SuperClass.call(this, id);
        }

        // SubClass.prototype.constructor = SubClass;

        var sub1 = new SubClass(10);
        var sub2 = new SubClass(20);

        sub1.books.push('设计模式');    
        console.log(sub1.books);        // ['javascript', 'html', 'css', '设计模式']
        console.log(sub1.id);            // 10
        // sub1.showBooks();            // 报错,不
        sub1.showId();                    // 10

        console.log(sub2.books);        // ['javascript', 'html', 'css']
        console.log(sub2.id);            // 20
    </script>


总结: 类似继承与构造函数式继承的区别

    1、类似继承: ChildreClass.prototype = new ParentClass();

        继承父类的公用属性和方法(this.的内容)和 prototype下的属性和方法, 需要改变constructor的名字

    2、构造函数式继承: ParentClass.call(this);

        只能继承公用属性和就去,不能继承父类prototype下的属性和方法, 不需要改变constructor的名字


三、原型继承

    通过一个继承方法,创建一个过渡对象,将继承的父对象赋给过渡对象的原型,在返回过渡对象的实例

    <script>
        var Parent = function(){
            this.blod = 20;
            this.level = 1;
        }
        Parent.prototype.getBlod = function(){
            console.log(this.blod, this.level);
        }

        Object.create = Object.create || function(obj){
            var F = function(){};        // 声明一个过渡对象
            F.prototype = obj;            // 过渡对象的原型继承父对象
            return new F();                // 返回过渡对象的一个实例,该实例的原型继承了父对象
        }

        var oParent = new Parent();
        var oChilde = Object.create(oParent);

        oChilde.blod = 30;    // 如果clone不设置blod属性,会到prototype的指向中找,最终会在oParent中找到属性
        oChilde.getBlod();    // 30 1

        oParent.getBlod();    // 20 1
    </script>


四、寄生式继承

    就是将原型继承在二次封装,并返回一个对象

    <script>

        // 寄生式继承
        // 声明基对象
        var book = {
            name: 'js book',
            alikeBook: ['css book', 'html book']
        }

        function createBook(obj){
            // 通过原型继承方式创建新对象
            var o = new inhreitObject(obj);
            // 拓展新对象
            o.getName = function(){
                console.log(this.name);
            }

            // 返回拓展新对象
            return o;
        }

        function inhreitObject(obj){
            var F = function(){};
            F.prototype = obj;
            return new F();
        }


        var oCreateBook = createBook(book);
        oCreateBook.getName();            // 'js book'

    </script>


五、多继承

    只适用于对象,不适用构造函数,继承某一个类,和继承多个类

    <script>

        // 单继承 属性复制
        // 这种是浅复制,象jquery等框架实现了深度复制
        var extend = function(target, source){
            // 遍历源对象中的属性
            for(var property in source){
                // 将源对象中的属性复制到目标对象中
                target[property] = source[property];
            }

            // 返回目标对象
            return target;
        }


        var book = {
            name: 'javascript 设计模式',
            alike: ['css', 'html', 'javascript'],
            type: {
                name: '计算机',
                money: 20
            }
        }
        var anotherBook = {
            color: 'blue'
        }

        extend(anotherBook, book);
        console.log(anotherBook.name);
        console.log(anotherBook.alike);
        console.log(book, anotherBook);



        // 多继承 多个对象继承 属性复制
        var mix = function(){
            var i = 1,                    // 从第二个参数起为被继承的对象
                len = arguments.length,    // 获取参数长度
                target = arguments[0],    // 第一个对象为目标对象
                arg;                    // 缓存参数对象

            // 遍历被继承的对象
            for(; i<len; i++){
                // 缓存当前对象
                arg = arguments[i];
                // 遍历被继承对象中的属性
                for(var property in arg){
                    // 将被继承对象中的属性复制到目标对象中
                    target[property] = arg[property];
                }
            }
            return target;
        }


        var book1 = {
            name: 'javascript 设计模式',
            alike: ['css', 'html', 'javascript'],
            type: {
                name: '计算机',
                money: 20
            }
        }

        var book2 = {
            tag: '书'
        }

        var book3 = {
            detail: '产品优势'
        }

        var newBook = mix(book1, book2, book3);

    </script>

多态

多态: 就是调用同一对象,通过参数发出不同指令,执行不同的结果

<script>

    var googleMap = {
        show: function(){
            console.log('开始渲染谷歌地图');
        }
    }

    var baiduMap = {
        show: function(){
            console.log('开始渲染百度地图');
        }
    }

    // 渲染地图
    var renderMap =  function(map){

        // 如果对象下有show方法就执行
        if(map.show instanceof Function){        
            map.show();
        }

        // 另一个判断类型模式
        // if(map && typeof map.show == 'function'){
        //     map.show();
        // }
    }

    renderMap(googleMap);
    renderMap(baiduMap);

</script>

闭包、高阶函数

高阶函数至少满足以下条件之一

一、函数可以作为参数被传递

    <script>
        var getUserInfo = function(userId, callback){
            $.ajax('http://xx.com/user?uid='+userId, function(data){
                if(typeof callback == 'function'){
                    callback();
                }
            })
        }
    </script>

二、函数可以作为返回值输出

    <script>

        // 函数作为返回值输出
        var isType = function(type){
            return function(obj){
                return Object.prototype.toString.call(obj) === '[object '+ type +']'
            }
        } 

        var str = "sdfsdf"
        var isString = isType('String');
        console.log(isString(str));        // true
    </script>

全局变量的解决方法

避免全局函数: 1、对象方法   2、函数对象    3、构造函数

<script>

    // 都是全局变量
    function checkName(){
        // 验证姓名
    }

    function checkEmail(){
        // 验证邮箱
    }

    function checkPassword(){
        // 验证密码
    }

    /*
    * 方法一 对象收编全局变量
    */
    var ChceckObject = {
        checkName: function(){
            // 验证姓名
        },
        checkEmail: function(){
            // 验证邮箱
        },
        checkPassword: function(){
            // 验证密码
        }
    }


    /*
    * 方法二 函数对象
    */
    var CheckObject = function(){
        return {
            checkName: function(){
                // 验证姓名
            },
            checkEmail: function(){
                // 验证邮箱
            },
            checkPassword: function(){
                // 验证密码
            }
        }
    }

    // 调用
    var oCheck = CheckObject();
    oCheck.checkName();


    /*
    * 方法三 构造函数写法
    */
    var CheckObject = function(){
        this.checkName = function(){
            // 验证姓名
        }
        this.checkEmail = function(){
            // 验证邮箱
        }
        this.checkPassword = function(){
            // 验证密码
        }
    }

</script>

/* 创建型模式 */

单例模式

单例模式: 又被称为单体模式,只允许实例化一次的对象类

全局变量 var a = {};    属于单例对象, 不属于单例模式

jquery就是一个很大的单例,js载入时被初始化一次

    (function(){
        var jquery = (function(){})();
        window.jQuery = window.$ = jQuery;
    })(window);


一、最简单的单例就是一个对象

    var app = {
        util: {},
        tool: {},
        ajax: {}
    }


二、私有变量的单例模式,减少全局变量方法

    1、使用命名空间

        var MyApp = {};

        MyApp.namespace = function(name){
            var parts = name.split('.');
            var current = MyApp;
            for(var i in parts){
                if(!current[parts[i]]){
                    current[parts[i]] = {}
                }
                current = current[parts[i]];
            }
        }

        MyApp.namespace('event');
        MyApp.namespace('dom.style');


    2、使用闭包封装私有变量

        var user = (function(){
            var _name = 'sven',
                _age = 30;

            return {
                getUserInfo: function(){
                    return _name +'-'+ _age;
                }
            }
        })();


三、惰性单例模式

    <script>

        // 惰性单例模式  只有在需要的时候才会创建
        var createLoginLayer = (function() {
            var div;
            return function() {
                if (!div) {
                    div = document.createElement('div');
                    div.className = 'dialog';
                    div.innerHTML = '我是登录窗口';
                    div.style.display = 'none';
                    document.body.appendChild(div);
                }

                return div
            }
        })();

        var oLoginBtn = document.querySelector('#loginBtn');
        oLoginBtn.onclick = function(){
            var oLayer = createLoginLayer();
            oLayer.style.display = 'block';
        }

    </script>

工厂模式

工厂模式: 将不同的参数传入到工厂方法中,进行加工后,然后在返回新的产品

// 创建一个工厂函数,并将结果返回
function createPerson(name, age, sex){
    var createObject = {};

    // 加工
    createObject = {    
        name: name,
        age: age,
        sex: sex,
        showInfo: function(){
            console.log('姓名:'+ this.name +"  年龄:" + this.age);
        }
    }

    // 返回成品
    return createObject;
}

var person = createPerson('siguang', 30, '男');
person.showInfo();

建造者模式 builder

将一个复杂对象的构建层与表示层相互分离

原型模式 prototype

用原型实例指向创建对象的类,使用于创建新的对象的类共享原型对象的属性及方法

<script>

    // 定义一个基本的图片轮播对象
    var LoopImages = function(imgArr, container){
        this.imgArr = imgArr;
        this.container = container;
    }
    LoopImages.prototype = {
        // 创建
        createImage: function(){
            console.log('loopImage createImage function');
        },
        // 切换方法
        changeImage: function(){
            console.log('LoopImage changeImage function');
        }
    }

    // 定义一个上下滑动的效果
    var SliderLoopImage = function(imgArr, container){
        LoopImages.call(this, imgArr, container);
    }
    SliderLoopImage.prototype = new LoopImages();            // 就是类似继承,并将要改变的方法重写

    // 重写切换方法
    SliderLoopImage.prototype.changeImage = function(){
        console.log('SlideLoopImg changeImage function');
    }

    var oSlider = new SliderLoopImage(['1.jpg', '2.jpg'], '#box');
    oSlider.createImage();
    oSlider.changeImage();

</script>

/* 结构型模式 */

外观模式

外观模式: 为一组复杂的子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统接口的访问更容易

// 外观模式实现
function addEvent(dom, type, fn){
    if(dom.addEventListener){
        dom.addEventListener(type, fn, false);
    }
    else if(dom.attachEvent){
        dom.attachEvent('on'+type, fn);
    }
    else{
        dom['on'+type] = fn;
    }
}

var myInpurt = document.querySelector('#myinput');

addEvent(myInpurt, 'click', function(){
    console.log('绑定第一个事件')
})

适配器模式

适配器模式: 将一个类的接口转化成另外一个接口,使类之间的接口不兼容问题通过适配器得以解决

一、如果有jQuery框架,现在A框架与jQuery框架基本相同可以将两种框架适配

    // 定义A框架
    var A = jQuery || {};

    A.g = function(id){
        return document.getElementById(id);
    }

    A.on = function(id, type, fn){
        var dom = typeofi id === 'String' ? A.g(id) : id;
        if(dom.addEventListener){
            dom.addEventListener(type, fn, false);
        }
        else if(dom.attachEvent){
            dom.attachEvent('on'+type, fn);
        }
        else{
            dom['on'+type] = fn;
        }
    }

二、jquery适配器

    window.A = A = jQuery;

三、参数适配

    function doSomeThing(obj){
        var adapter = {
            name: '雨夜',
            titel: '设计',
            age: 30,
            color: 'pink',
            size: 100,
            prize: 50
        }

        for(var key in adapter){
            adapter[key] = obj[key] || adapter[key];        // 处理如果obj没有的参数
        }
        return adapter;            // 也可以使用extend();
    }

代理模式

代理模式: 由于一个对象不能直引用另一个对象,所以需要一个代理对象来为两个对象之间起到中介作用

<script>
    var Flower = function(){};

    var xiaoming = {
        sendFlower: function(target){
            var flower = new Flower();
            target.receiveFlower(flower);
        }
    }

    // B代理小明去给A送花
    var B = {
        receiveFlower: function(flower){
            A.listenGoodModd(function(){        // 侦听A的心情
                A.receiveFlower(flower);
            })
        }
    }

    // A接收小明的花
    var A = {
        receiveFlower: function(flower){
            console.log('收到花'+ flower);
        },
        listenGoodModd: function(fn){
            setTimeout(function(){
                fn()
            }, 10000);
        }
    }

    xiaoming.sendFlower(B);
</script>

装饰者模式

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

<p>姓名: <input type="text" name="" id="username" /><span id="usernameError">Error username xxxx</span><br></p>
<p>密码: <input type="text" name="" id="password" /><span id="passwordError">Error password xxxx</span></p>

<script>

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

    // 点击文本框,显示提示信息,并保留原input上的事件
    var username = document.querySelector('#username');
    var password = document.querySelector('#password');

    // 基础需求,点击文本框后会加入文本内容
    username.onclick = function(){
        this.value = '初始化username';
    }.bind(username);

    password.onclick = function(){
        this.value = '初始化password';
    }.bind(password);


    // 新增的需求,点击后需要有提示消息,这里不破坏原有的功能
    // 装饰者方法
    var decorator = function(input, fn){
        var input = document.querySelector(input);

        // 判断事件是否已经绑定事件
        if(typeof input.onclick == 'function'){
            // 缓存input本身的处理的事件
            var oldClickFn = input.onclick;

            // 为事件定义新的事件, 执行将新好的方法全执行
            input.onclick = function(){
                oldClickFn();
                fn();
            }
        }
        else{
            input.onclick = fn;
        }
    }

    // 调用
    decorator('#username', function(){
        document.querySelector("#usernameError").style.display = 'block';
    })
    decorator('#password', function(){
        document.querySelector("#passwordError").style.display = 'block';
    })

</script>

桥接模式

桥接模式: 将一个功能有共用的部分抽取出来,将实现与抽出来共用方法,通过桥接方法链接在一起,这就是桥接模式

<div class="box">
    <span>最新回答</span>
    <span>热门回答</span>
    <span>等待回答</span>
</div>

<script>
    var box = document.querySelector('.box');
    var spans = box.getElementsByTagName('span');

    // 抽出的公用
    function changeColor(dom, color, bg){
        dom.style.color = color;
        dom.style.backgroundColor = bg;
    }

    // 桥接模式
    function bindSpan(){
        var i = 0;
        var len = spans.length;

        for(i; i<len; i++){
            spans[i].onmouseover = function(){
                changeColor(this, '#ad2102', '#ffbb96');
            }
            spans[i].onmouseout = function(){
                changeColor(this, '#ad2102', '#fff2e8');
            }                
        }
    }

    bindSpan();
</script>

享元模式

享元模式: 运用共享技术有效地支持大量细粒的对象,避免对象间有相同内容造成多余的开销

/* 行为型设计模式 */

观察者模式

观察者模式 - 又称发布订阅模式或消息机制

状态模式

状态模式 - 当一个对象内部状态发生改变时,会导致行为改变

状态模式是解决程序中臃肿的分支判断语句问题,将每个分支转化一种状态独立出来

<script>

    // 创建一个玛丽类
    var MarryState = function(){

        // 存储内部状态
        var _currentState = {};

        // 动作与状态方法映射
        var states = {
            jump: function(){
                console.log('jump');
            },
            move: function(){
                console.log('move');
            },
            shoot: function(){
                console.log('shoot');
            },
            squat: function(){
                console.log('squat');
            }
        };

        // 动作控制类
        var Action = {

            // 改变状态方法
            changeState: function(){
                var arg = arguments;
                _currentState = {};
                if(arg.length){
                    for(var i=0, len=arg.length; i<len; i++){
                        _currentState[arg[i]] = true;
                    }
                }
                return this;
            },

            // 执行动作
            goes: function(){
                console.log('触发一次动作');
                for(var i in _currentState){
                    states[i] && states[i]();       // 如果动作存在并执行
                }
                return this;
            }
        }

        return {
            change: Action.changeState,
            goes: Action.goes
        }
    }


    MarryState()
        .change('jump', 'shoot')
        .goes()

</script>

策略模式

策略模式: 定义一系列算法,把他们封装起来,并使它们可以相互替换

策略模式由两部分组成:

    1、策略类,策略类封装了具体的算法,并负责具体的计算过程

    2、环境类,接受客户的请求,把请求委托给一个策略类

Example:

    <script>
        // 需求根据KPI等级返回奖金金额

        // 定义策略类
        var strategies = {
            'S': function(salary){
                return salary * 4;
            },
            'A': function(salary){
                return salary * 3;
            },
            'B': function(salary){
                return salary * 2;
            }
        }

        // 环境类
        var calculateBonus = function(level, salary){
            return strategies[level] && strategies[level](salary);
        }

        console.log(calculateBonus('B', 2000));        // 4000
        console.log(calculateBonus('S', 5000));        // 20000

    </script>

职责链模式

职责链模式 - 解决请求的发送者与请求的接受者之间的耦合

命令模式

命令模式 - 将请求与实现解耦并封装成独立对象,从而使不同的请求对客户端的实现参数

迭代器模式

迭代器模式: 提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部

<script>
    var isType = function(type){
        var callType =  Object.prototype.toString.call(type);

        switch(callType){
            case '[object Object]':
                return 'object';
                break;

            case '[object Array]':
                return 'array';
                break;

            default: 
                return false;
                break;
        }
    }

    var each = function(arr, callback){
        if(isType(arr) == 'object'){
            for(var key in arr){
                callback.call(arr[key], key, arr[key]);
            }
        }
        else if (isType(arr) == 'array'){
            for(var i=0; i<arr.length; i++){
                callback.call(arr[i], i, arr[i]);
            }
        }
    }

    each([1, 2, 3], function(index, val){
        console.log(index, val);
    })

    each({name: 'siguang', age: 32, sex: '男'}, function(key, val){
        console.log(key, val);
    })
</script>

发布订阅模式模式

发布订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变,所有依赖于它的对象都将得到通知.

js中事件模型来替代传统的发布订阅模式

| http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html