美文网首页JavaScript
JavaScript - 设计模式 - 命名空间

JavaScript - 设计模式 - 命名空间

作者: 西巴撸 | 来源:发表于2017-03-29 18:47 被阅读54次

    本小节主要讲解三种常用的设计模式和命名空间,第一种是工厂模式,第二种是单利模式,第三种是观察者模式


    设计模式概述

    是为了解决在开发中可能遇到的需求(相似),而提出的一套解决方法.

    设计模式要求:

    • 在开发中整个系统需要一套设计模式(架构师)
    • 来源:建筑(建房子)领域
    • 设计模式的四人帮: Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides
    • 设计模式:总共有23种.
    • **设计模式类型:单利(例)模式 |观察者模式 | 代理模式 |工厂模式 |适配器模式 |桥接模式 | .... **
    • 设计模式的书:<设计模式><大话设计模式|大话数据结构><23种常见的设计模式>

    工厂模式

    批量创建大量的同类型的对象

    优点 :

    • 可以通过统一的借口来创建对象,根据传入的参数不同来创建不同的对象,易于扩展和维护,稳定性更好

    核心过程 :

    • 提供一个父构造函数
    • 设置这个父构造函数的原型对象(属性|方法)
    • 在父构造函数身上添加静态工厂方法

    1.需要接收传入的参数(要生产的产品的类型)
    2.判断 是否支持生产
    3.设置子构造函数的原型对象
    4.把新创建的对象返回

    • 定制合作伙伴

    • 直接使用父构造函数的静态工厂方法来创建指定的产品对象

    • 示例代码 :

    <script>
        //1. 提供一个父构造函数
        function PhoneMake(){};
        //2. 设置这个父构造函数的原型对象(属性|方法)
        PhoneMake.prototype.logDes = function(){
            console.log("我们的口号是:" + this.des);
        }
    
        //3. 在父构造函数身上添加静态工厂方法
        PhoneMake.factory = function(typeStr){
            // 3.1 需要接收传入的参数(要生产的产品的类型)
            var productType =  typeStr;
    
            //var Dog = PhoneMake[productType];
            //3.2 判断 是否支持生产
            if (typeof PhoneMake[productType]  != "function")
            {
                //抛出一个异常
                throw "对不起,我们工厂和这个品牌没有商务合作,不能生产!"
            }
    
            //3.3 设置子构造函数的原型对象
             //为了获得构造函数原型对象的方法
            PhoneMake[productType].prototype = new PhoneMake(); 
    
            //3.4 设置子构造函数的原型对象
           var newProduct = new PhoneMake[productType]();
    
            //3.5 把新创建的对象返回
            return newProduct;
        }
    
        //4. 定制合作伙伴
        PhoneMake.iphone = function(){
            this.des = "最安全最稳定的系统,最垃圾的体验"
        }
        PhoneMake.oppo = function(){
            this.des = "充电两小时,通话五分钟"
        }
        PhoneMake.vivo = function(){
            this.des = "照亮你的美,你本来就很美"
        }
        PhoneMake.meizu = function(){
            this.des = "我就是我,不一样的魅族"
        }
    
        //5. 直接使用父构造函数的静态工厂方法来创建指定的产品对象
        var iphone = PhoneMake.factory("iphone");
        var vivo = PhoneMake.factory("vivo");
        var oppo = PhoneMake.factory("oppo");
        var meizu = PhoneMake.factory("meizu");
        var xiaomi = PhoneMake.factory("xiaomi");
    
        iphone.logDes();
        vivo.logDes();
        oppo.logDes();
        meizu.logDes();
    
    </script>
    

    单利模式

    在整个程序的运行过程中,一个类只有一个实例对象

    js中的单利模式

    • js没有类(ES6才有的) ,js实现单利模式(限定讨论的范围)

    1.字面量
    2.内置构造函数(Array Date Function Object)
    3.工厂函数
    4.自定义构造函数(单利模式)

    • js是什么样的语言

    1.弱类型,脚本,轻量级,面向对象,基于原型(对象),解释行语言.函数式.
    2.js到底是不是一门面向对象(类)的语言?
    3.js是一门支持面向对象的语言.(封装|继承|多态)

    单利模式实现之后的表现

    var p1 = new 构造函数()
    var p2 = new 构造函数()
    p1 == p2

    • 示例代码 :
    <script>
        function Person(){
            //首先创建一个空的对象
            //默认把新的对象赋值给this
            //把新对象返回
        }
        var p1 = new Person();
        var p2 = new Person();
        console.log(p1 == p2);
    </script>
    

    单利模式的实现方式01 ----> 全局变量

    全局变量来保存对象实现单利模式

    • 提供一个全局的变量
    • 提供一个构造函数Person
    • 在构造函数内部先判断全局变量是否有值,如果有那么就直接返回
    • 如果没有,那么就把this赋值给全局变量
    • 通过this设置属性和方法

    存在的问题

    • 使用一个全局变量来保存单利对象,该全局变量在整个作用域中都可以被访问或者是修改,可能会轻易的被覆盖或者是修改.

    • 修改之后,创建出来的实例对象就不再是之前的那个单利对象了.

    • 示例代码 :

    <script>
        var instance;
        function Person(){
            if(instance)
            {
                console.log("对象已经被创建,直接把之前创建好的对象返回");
                return instance;
            }
            instance = this;
            this.name = "奥特曼";
            this.age = 1000;
            console.log("第一次创建对象,创建对象之后并返回");
        }
    
        var p1 = new Person();
        var p2 = new Person();
        console.log(p1 == p2);
        var p3 = new Person();
    
        instance = "demo";
        var p4 = new Person();
        console.log(p4);
        console.log(p4 == p1);
    </script>
    

    单利模式的实现方式02 ----> 静态属性

    静态成员:直接添加到构造函数身上的属性或者是方法

    存在的问题

    • 构造函数的静态属性其实也可能被修改,因此这种方法也不安全

    • 示例代码 :

    <script>
        function Person(){
            //判断对象是否已经被创建
            if (Person.instance)
            {
                console.log("之前已经创建过对象,直接返回");
                return Person.instance;
            }
    
            this.name = "大黄蜂"
            Person.instance  = this;
            console.log("第一次创建");
        }
    
        var p1 = new Person();
        var p2 = new Person();
        console.log(p1 == p2);
    
    
        instance = "demo";
        var p3 = new Person();
        console.log(p1 == p3);
    
        Person.instance = "123";
        var p4 = new Person();
        console.log(p4 == p1);
    </script>
    

    单利模式的实现方式03 ----> 惰性函数

    核心过程

    • 提供一个构造函数

    • 在构造函数内部声明一个私有的变量

    • 使用惰性函数定义更新构造函数的实现(直接把instance返回)

    • 设置原型对象[新构造函数的原型对象 = 旧构造函数的原型对象]
      构造函数,prototype == 对象.proto

    • 使用新的构造函数创建实例对象,并且赋值给instance

    • 修正对象的构造函数指向

    • 通过instance设置实例属性和方法

    • 示例代码 :

    <script>
        // 01 提供一个构造函数
        function Person(){
            //this01
            //02 在构造函数内部声明一个私有的变量
            var instance;
            //03 使用惰性函数定义更新构造函数的实现(直接把instance返回)
            Person = function(){
                //内部默认会创建一个空的对象 this02
                return instance;
            }
            //04 设置原型对象[新构造函数的原型对象 = 旧构造函数的原型对象]
            //原型链继承:Man.prototype = new Person();
            //原型式继承:Man.prototype = Person.prototype;
            //Person.prototype = this.__proto__;  //非标准(代码中不要出现)
            Person.prototype = this;
    
            //05 使用新的构造函数创建实例对象,并且赋值给instance
            instance = new Person();
            //instance = this;
            //06 修正对象的构造函数指向
            instance.constructor = Person;
            // 07 通过instance设置实例属性和方法
            instance.name = "我很好听";
            // 08 把instance返回
            return instance;
        }
    
        Person.prototype.des = "描述信息";
        var p1 = new Person();
        Person.prototype.hi = "hi";
    
        var p2 = new Person();
        console.log(p1 == p2);
    
        console.log(p1.constructor == Person);  //true
        console.log(p1.des);
        console.log(p1.hi);
    </script>
    

    单利模式的实现方式04 ----> 全局变量 + 即时函数

    • 示例代码 :
    <script>
        var Person;
        (function(){
            var instance;
    
            Person = function (){
                if(instance)
                {
                    return instance;
                }
                this.name = "momo";
                instance = this;
            }
        })();
    
        var p1 = new Person();
        var p2 = new Person();
        console.log(p1 == p2);
    </script>
    

    观察者模式

    观察者模式举例说明

    • 男生A和男生B同时都喜欢女生C,他们想时时监视女生C的动向,动态,所以他们找了女生C的闺蜜作为观察者帮他们监视实时动态.这样不管女生C有什么最新的动态都会被男生A和男生B监听到,开发中就是男生多了点,动态多了点,核心内容就是这样

    • 要求:

    女神:rose(发布者)
    男生:jack(订阅者)
    男生:tom(订阅者)

    • 过程:

    创建或者是设置一个发布者
    创建订阅者对象
    注册订阅者
    测试(发状态)

    • 示例代码1 : 一个发布者,两个订阅者,关注的是一个状态
    <script>
        //01 创建或者是设置一个发布者
        var rose = {
            user:[],
            addUser:function(fn){
                if (typeof fn != "function")
                {
                    throw "不支持该操作!";
                }
                this.user.push(fn);
            },
            removeUser:function(fn){
                for (var i = 0; i < this.user.length; i++) {
                    if(this.user[i] == fn)
                    {
                        console.log(fn + "取消了订阅");
                        this.user.splice(i,1);
                    }
                }
            },
            eat:function(){
                for (var i = 0; i < this.user.length; i++) {
                   this.user[i]();
                }
            }
        }
    
    
        // 02 创建订阅者对象
        var jack = {
            eat_jack:function(){
                console.log("我陪你去吃拉面吧  ---jack");
            }
        }
        var tom = {
            eat_tom:function(){
                console.log("我陪你去吃寿司吧  ---tom");
            }
        }
    
        // 03 注册订阅者
        rose.addUser(jack.eat_jack);
        rose.addUser(tom.eat_tom);
    
        //04 发布者状态改变
        rose.eat();
        rose.removeUser(jack.eat_jack);
        rose.eat();
    </script>
    
    • 示例代码2 : 多个状态
    <script>
        //01 创建或者是设置一个发布者
        var publisher = {
            addUser:function(fn,type){
                var type = type || "eat";
                if (typeof fn != "function")
                {
                    throw "不支持该操作!";
                }
                this.user[type].push(fn);
            },
            removeUser:function(fn,type){
                var type = type || "eat";
                for (var i = 0; i < this.user[type].length; i++) {
                    if(this.user[type][i] == fn)
                    {
                        console.log(fn + "取消了订阅");
                        this.user[type].splice(i,1);
                    }
                }
            },
            eat:function(){
                for (var i = 0; i < this.user["eat"].length; i++) {
                   this.user["eat"][i]();
                }
            },
            sleep:function(){
                for (var i = 0; i < this.user["sleep"].length; i++) {
                    //console.log(this.user,"++++");
                    this.user["sleep"][i]();
                }
            }
        }
    
        var rose = {};
        //封装一个函数用来快速的让某个指定对象成为发布者
        function makePublisher(o){
            for(var i in publisher)
            {
                if (publisher.hasOwnProperty(i) && typeof publisher[i] == "function" ){
                    o[i] = publisher[i];
                }
            }
            o.user = {
                eat:[],
                sleep:[]
            };
        }
        makePublisher(rose);
    
        // 02 创建订阅者对象
        var jack = {
            eat_jack:function(){
                console.log("我陪你去吃拉面吧  ---jack");
            },
            sleep_jack:function(){
                console.log("晚安 rose  ---jack");
            }
        }
    
        // 03 注册订阅者
        rose.addUser(jack.eat_jack,"eat");        //关注rose肚子饿不饿
        rose.addUser(jack.sleep_jack,"sleep");     //关注rose困不困
    
        rose.eat();
        rose.sleep();
    
    • 示例代码3 : 通用性处理
    <script>
        //01 创建或者是设置一个发布者
        var publisher = {
            addUser:function(fn,type){
                var type = type || "eat";
                if (this.user[type] == undefined)
                {
                    this.user[type] = [];
                }
                if (typeof fn != "function")
                {
                    throw "不支持该操作!";
                }
                this.user[type].push(fn);
            },
            removeUser:function(fn,type){
                this.publish(type,fn);
            },
            publish:function(type,fn){
                var type = type || "eat";
                for (var i = 0; i < this.user[type].length; i++) {
                    //判断当前是要取消订阅还是要发布状态
                    if (typeof fn == "function")
                    {
                        if(this.user[type][i] == fn)
                        {
                            console.log(fn + "取消了订阅");
                            this.user[type].splice(i,1);
                        }
                    }else
                    {
                        this.user[type][i]();
                    }
    
                }
            }
        }
        var rose = {
            eat:function(){
                this.publish("eat");
            },
            sleep:function(){
                this.publish("sleep");
            },
            read:function(){
                this.publish("read");
            }
        };
        function makePublisher(o){
            for(var i in publisher)
            {
                if (publisher.hasOwnProperty(i) && typeof publisher[i] == "function" ){
                    o[i] = publisher[i];
                }
            }
            o.user = {
                eat:[],
                sleep:[]
            };
        }
        makePublisher(rose);
    
        var jack = {
            eat_jack:function(){
                console.log("我陪你去吃拉面吧  ---jack");
            },
            sleep_jack:function(){
                console.log("晚安 rose  ---jack");
            }
        }
        var tom = {
            eat_tom:function(){
                console.log("我买给你吧  ---tom");
            },
            sleep_tom:function(){
                console.log("今晚的太阳很好看 ---tom");
            },
            read_tom:function(){
                console.log("你也在学习js吗?");
            }
        }
    
        rose.addUser(jack.eat_jack,"eat");
        rose.addUser(tom.sleep_tom,"sleep");
        rose.addUser(tom.read_tom,"read");
        rose.eat();
        rose.sleep();
        rose.read();
    
    </script>
    
    • 示例代码4 : 订阅者成为发布者
    <script>
        //01 创建或者是设置一个发布者
        var publisher = {
            addUser:function(fn,type){
                var type = type || "eat";
                if (this.user[type] == undefined)
                {
                    this.user[type] = [];
                }
                if (typeof fn != "function")
                {
                    throw "不支持该操作!";
                }
                this.user[type].push(fn);
            },
            removeUser:function(fn,type){
                this.publish(type,fn);
            },
            publish:function(type,fn){
                var type = type || "eat";
                for (var i = 0; i < this.user[type].length; i++) {
                    //判断当前是要取消订阅还是要发布状态
                    if (typeof fn == "function")
                    {
                        if(this.user[type][i] == fn)
                        {
                            console.log(fn + "取消了订阅");
                            this.user[type].splice(i,1);
                        }
                    }else
                    {
                        this.user[type][i]();
                    }
    
                }
            }
        }
        var rose = {
            eat:function(){
                this.publish("eat");
            },
            sleep:function(){
                this.publish("sleep");
            },
            read:function(){
                this.publish("read");
            },
            lol_rose:function(){
                console.log("你怎么又在打游戏?还是游戏比较重要一些?")
            }
        };
        function makePublisher(o){
            for(var i in publisher)
            {
                if (publisher.hasOwnProperty(i) && typeof publisher[i] == "function" ){
                    o[i] = publisher[i];
                }
            }
            o.user = {
                eat:[],
                sleep:[]
            };
        }
        makePublisher(rose);
    
        var jack = {
            eat_jack:function(){
                console.log("我陪你去吃拉面吧  ---jack");
            },
            sleep_jack:function(){
                console.log("晚安 rose  ---jack");
            },
            statusLol:function(){
                this.publish("lol");
            }
        }
        var tom = {
            eat_tom:function(){
                console.log("我买给你吧  ---tom");
            },
            sleep_tom:function(){
                console.log("今晚的太阳很好看 ---tom");
            },
            read_tom:function(){
                console.log("你也在学习js吗?");
            },
            lol_rose:function(){
                console.log("好兄弟,终于来啦");
            }
        }
    
        rose.addUser(jack.eat_jack,"eat");
    
        rose.addUser(tom.sleep_tom,"sleep");
        rose.addUser(tom.read_tom,"read");
        rose.eat();
        rose.sleep();
        rose.read();
    
        //设置jack成为发布者,状态(lol)
        makePublisher(jack);
        jack.addUser(rose.lol_rose,"lol");
        jack.addUser(tom.lol_rose,"lol");
        jack.statusLol();
    
    </script>
    

    备忘模式(函数结构缓存)

    特定场合:

    • 计算的函数f()
    • 某些参数需要进行大规模反复的计算,可以考虑吧计算的结果保存起来
        f(n) ..... =>1000m
        代码中某些参数可能会反复计算
        f(10) ===>ssddd
        f(10) ===>ssddd
    

    使用一个缓存对象cacheObj{key-value}

    • 思路:

    1.提供一个全局的对象(缓存对象),key-value
    2.当我们传递参数需要进行计算(逻辑)的时候,先检查缓存对象中是否有对应的结果
    3.如果有缓存数据,那么就直接使用(可以节省时间,提高效率)
    4.如果没有缓存数据,那么这个时候再执行计算操作,处理得到结果之后,把这个数据保存起来
    5.函数的参数作为缓存对象的key,把函数计算的结果作为这个key对应的值

    • 示例代码 :
    <script>
        var cache = {};
        function f1(str){
            //.....
            if (cache[str] != undefined)
            {
                console.log("已经存在缓存数据,直接返回");
                return cache[str];
            }
            //....如果缓存中有数据,那么函数体后面的代码就不再执行(节省时间)
    
            //执行耗时操作...
            var result = str + " hello world!";
            cache[str] = result;
            console.log("第一次调用函数传入参数,返回结果");
            return result;
        }
    
        console.log(f1("demo"));   //
        console.log(f1("demo"));   //
        console.log(f1("demo"));
    </script>
    
    <script>
        function f1(str){
            //.....
            if (f1.cache[str] != undefined)
            {
                console.log("已经存在缓存数据,直接返回");
                return f1.cache[str];
            }
            //....如果缓存中有数据,那么函数体后面的代码就不再执行(节省时间)
    
            //执行耗时操作...
            var result = str + " hello world!";
            f1.cache[str] = result;
            console.log("第一次调用函数传入参数,返回结果");
            return result;
        }
    
        f1.cache = {};
        console.log(f1("test"));  //
        console.log(f1("test"));   //
        console.log(f1("test"));
    </script>
    

    命名空间模式:

    写法:就是把所有的东西都写在一个对象里面.

    命名:命名空间的名称一般是项目的名称或者是简写,要求所有的字符都大写

    • 示例代码 :
    <script>
        //01 普通的变量
        var a = "a";
        var b = "b";
    
        //02 对象
        var obj = {
            name:"xxx"
        }
    
        function func (){
            console.log("func");
        }
    
        function Person(){
            this.name = "默认的名称";
        }
        function func (){
            console.log("func");
        }
    </script>
    
    <script>
        var MOMO = {};
        //01 普通的变量
        MOMO.a = "a";
        MOMO.b = "b";
    
        //02 对象
        MOMO.obj = {
            name:"zahngsan"
        }
    
        MOMO.func = function(){
            console.log("func");
        }
    
        MOMO.Person = function(){
            this.name = "默认的名称";
        }
    
        console.log(new  MOMO.Person());
    </script>
    
    <script>
    //    var MOMO ={};
    //    MOMO.obj = {
    //        name:"张三"
    //    };
    //
    //    MOMO.obj = "demodede";
    
        //在使用或者是对属性进行赋值之前,会先做一个检查(检查改属性或者是方法是否存在)
        //var MOMO = {};  不推荐
    
        //02 更安全的处理方式:麻烦
    //    if (MOMO == undefined)
    //    {
    //        var MOMO = {};
    //    }
    
        var MOMO = MOMO || {};      //逻辑或 如果MOMO为真那么返回MOMO,否则就返回{}
        //if (MOMO.name == undefined)
        if ( typeof MOMO.name == 'undefined')
    {
        MOMO.name = "测试的名称";
    }
    
        if ( typeof MOMO.age == 'undefined')
        {
            MOMO.age = 20;
        }
    
        //100属性
    
    </script>
    

    通用的命名空间函数

    在命名空间上面提供一个方法(nameSpace)

    • 得到调用函数传入的字符串
    • 把字符串转换为字符串数组
    • 把MOMO最外层去掉(删除)
    • 遍历
    • 示例代码 :
    <script>
        var MOMO = MOMO || {};
        MOMO.namespace = function(stringParam){
            var str = stringParam;
            var parts = str.split(".");   //根据字符串切割字符串变成一个数组
            var parent = MOMO;
    
            console.log(parts);
    
            if(parts[0] == "MOMO")
            {
                parts = parts.slice(1)    //作用:删除第一个元素返回一个新的数组
            }
    
            for (var i = 0; i < parts.length; i++) {
                //MOMO.name
                //name.des
                //des.abc
                if (parent[parts[i]] == undefined)
                {
                    parent[parts[i]] = {};
                }
                //更新父节点
                parent = parent[parts[i]];
            }
        }
    
        MOMO.namespace("MOMO.name.des.abc");
        console.log(MOMO);
    
        MOMO.namespace("MOMO.a.b.c.d.e.f.d.g.h.j.k.s.d.g.h.j.a.s.d.f.we.r");
        console.log(MOMO);
        MOMO.namespace("abc.des.abc");
        console.log(MOMO);
    </script>
    

    截至今日,面向对象和JavaScript进阶内容已经全部更新完毕! 内容是比较详细的,只要跟着每一篇的博文仔细学习,比你看网上的垃圾视频强多了 ! 理论和实践相结合 更多的是得动手去敲,以后还请大家多多关注更新,日后定会上一些新颖的东西和大家见面 !


    相关文章

      网友评论

        本文标题:JavaScript - 设计模式 - 命名空间

        本文链接:https://www.haomeiwen.com/subject/jvzrgttx.html