JavaScript设计模式(二):创建型设计模式-简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式

神奇的魔术师-简单的工厂模式

简单工厂模式(Simple Factory)

又叫静态工厂方法,由一个工厂对象决定创建某一种产品对象类的实例。主要用来创建同一类对象.

非工厂模式 (每个提示框都是一个单独的类)

// 警示框
let AlertDialog = function (text) {
    
    
    this.show = function () {
    
    
        alert(text);
    }
}
// 提示框
let PromptDialog = function (text) {
    
    
    this.show = function () {
    
    
        prompt(text);
    }
}
// 确认框
let ConfirmDialog = function (text) {
    
    
    this.show = function () {
    
    
        confirm(text);
    }
}
let alertDialog = new AlertDialog('请检查输入的账号密码!');
let promptDialog = new PromptDialog('登录成功,请输入您的心情:');
let confirmDialog = new ConfirmDialog('确认取消删除操作!');

alertDialog.show();
let value = promptDialog.show();
let isOK = confirmDialog.show();

将以上代码转换为工厂模式

// 警示框
let AlertDialog = function (text) {
    
    
    this.show = function () {
    
    
        alert(text);
    }
}
// 提示框
let PromptDialog = function (text) {
    
    
    this.show = function () {
    
    
        prompt(text);
    }
}
// 确认框
let ConfirmDialog = function (text) {
    
    
    this.show = function () {
    
    
        confirm(text);
    }
}

/**
 * 提示框工厂
 * @param {string} type 提示框类型
 * @param {string} text 提示内容
 * @returns 提示框类的实例
 */
let DialogFactory = function (type, text) {
    
    
    switch (type) {
    
    
        // 警示框差异部分
        case 'alert':
            return new AlertDialog(text);
        // 提示框差异部分
        case 'prompt':
            return new PromptDialog(text);
        // 确认框差异部分
        case 'confirm':
            return new ConfirmDialog(text);
    }
}

// 无所谓使用不使用 new
let alertDialog = DialogFactory('alert', '请检查输入的账号密码!');
let promptDialog = new DialogFactory('prompt', '登录成功,请输入您的心情:');
let confirmDialog = DialogFactory('confirm', '确认取消删除操作!');

alertDialog.show();
let value = promptDialog.show();
let isOK = confirmDialog.show();

简单工厂模式 (抽象出相同的方法)

/**
 * 提示框工厂 - 抽象出相同的代码
 * @param {string} type 提示框类型
 * @param {string} text 提示内容
 */
let DialogFactory = function (type, text) {
    
    
    let dialog = null;
    switch (type) {
    
    
        // 警示框差异部分
        case 'alert':
            dialog = alert.bind(null, text);
            break;
        // 提示框差异部分
        case 'prompt':
            dialog = prompt.bind(null, text);
            break;
        // 确认框差异部分
        case 'confirm':
            dialog = confirm.bind(null, text);
            break;
    }
    this.show = dialog;
}

let alertDialog = new DialogFactory('alert', '请检查输入的账号密码!');
let promptDialog = new DialogFactory('prompt', '登录成功,请输入您的心情:');
let confirmDialog = new DialogFactory('confirm', '确认取消删除操作!');

alertDialog.show();
let value = promptDialog.show();
let isOK = confirmDialog.show();

给我一张名片-工厂方法模式

工厂方法模式(Factory Method)

通过对产品类的抽象使其创建业务主要负责用于创建多类产品的实例

比如解决 现有一个需求,后续可能会在增加一个类似需求,后续可能会在增加一个类似需求······,以此类推;而通过简单工厂类每次都需要添加一个类和修改工厂对象 的问题

需求问题

  • 广告投入问题:(注意每个广告的显示形式是不同的)
    1. 需求1:添加一个 JAVA 宣传广告;
    2. 需求2:添加一个 C# 宣传广告;
    3. 需求3:添加一个 UI 宣传广告;
    4. 需求n:··· ···
  • ⚠ 问题:每次接入新需求都需要去主动添加一个类并修改工厂方法内部判断

在这里插入图片描述

  • 需求1来了 ······
    // JAVA的广告
    let JAVAAd = function (desc) {
          
          
        this.desc = desc;
        this.show = function () {
          
          
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'green';
            document.getElementById('container').appendChild(div);
        }
    }
    
    /**
     * 广告工厂
     * @param {string} type 广告类型
     * @param {string} desc 广告描述
     */
    let AdFactory = function (type, desc) {
          
          
        switch (type) {
          
          
            case 'JAVA':
                return new JAVAAd(desc);
        }
    }
    
    let javaAd = AdFactory('JAVA', '学了就秃头!!!');
    javaAd.show();
    
  • 需求2来了 ······
    // JAVA的广告
    let JAVAAd = function (desc) {
          
          
        this.desc = desc;
        this.show = function () {
          
          
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'green';
            document.getElementById('container').appendChild(div);
        }
    }
    
    // C#的广告
    let CSharpAd = function (desc) {
          
          
        this.desc = desc;
        this.show = function () {
          
          
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'red';
            document.getElementById('container').appendChild(div);
        }
    }
    
    /**
     * 广告工厂
     * @param {string} type 广告类型
     * @param {string} desc 广告描述
     */
    let AdFactory = function (type, desc) {
          
          
        switch (type) {
          
          
            case 'JAVA':
                return new JAVAAd(desc);
            case 'C#':
                return new CSharpAd(desc);
        }
    }
    
    let javaAd = AdFactory('JAVA', '学了就秃头!!!');
    javaAd.show();
    
    let csharpAd = AdFactory('C#', '这个语言可以开发游戏!!!');
    csharpAd.show();
    
  • 需求3来了 ······
    // JAVA的广告
    let JAVAAd = function (desc) {
          
          
        this.desc = desc;
        this.show = function () {
          
          
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'green';
            document.getElementById('container').appendChild(div);
        }
    }
    
    // C#的广告
    let CSharpAd = function (desc) {
          
          
        this.desc = desc;
        this.show = function () {
          
          
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'red';
            document.getElementById('container').appendChild(div);
        }
    }
    
    // UI的广告
    let UIAd = function (desc) {
          
          
        this.desc = desc;
        this.show = function () {
          
          
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'blue';
            document.getElementById('container').appendChild(div);
        }
    }
    
    /**
    * 广告工厂
    * @param {string} type 广告类型
    * @param {string} desc 广告描述
    */
    let AdFactory = function (type, desc) {
          
          
        switch (type) {
          
          
            case 'JAVA':
                return new JAVAAd(desc);
            case 'C#':
                return new CSharpAd(desc);
            case 'UI':
                return new UIAd(desc);
        }
    }
    
    let javaAd = AdFactory('JAVA', '学了就秃头!!!');
    javaAd.show();
    
    let csharpAd = AdFactory('C#', '这个语言可以开发游戏!!!');
    csharpAd.show();
    
    let uiAd = AdFactory('UI', '这也太酷了吧!!!');
    uiAd.show();
    
  • 需求n来了 ······

解决上述问题 (来了需求只需要添加对应的需求类和调用方法就可以了,无需修改工厂对象)

/**
 * 安全模式创建的广告工厂类 - 安全模式:检测你是否用了new进行实例
 * @param {string} type 广告类型
 * @param {string} desc 广告描述
 */
let AdFactory = function (type, desc) {
    
    
    // 判断你是否用了new
    if (this instanceof AdFactory) {
    
    
        var instance = new this[type](desc);
        return instance;
    } else {
    
    
        return new AdFactory(type, desc);
    }
}

// 广告工厂原型中设置创建所有类型数据对象的基类
AdFactory.prototype = {
    
    
    // JAVA的广告
    JAVAAd: function (desc) {
    
    
        this.desc = desc;
        this.show = function () {
    
    
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'green';
            document.getElementById('container').appendChild(div);
        }
    },
    // C#的广告
    CSharpAd: function (desc) {
    
    
        this.desc = desc;
        this.show = function () {
    
    
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'red';
            document.getElementById('container').appendChild(div);
        }
    },
    // UI的广告
    UIAd: function (desc) {
    
    
        this.desc = desc;
        this.show = function () {
    
    
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'blue';
            document.getElementById('container').appendChild(div);
        }
    }
};

let javaAd = new AdFactory('JAVAAd', '【广告需求一】学了就秃头!!!');
javaAd.show();

let csharpAd = AdFactory('CSharpAd', '【广告需求二】这个语言可以开发游戏!!!');
csharpAd.show();

let uiAd = new AdFactory('UIAd', '【广告需求三】这也太酷了吧!!!');
uiAd.show();

来了一大波需求

在这里插入图片描述

/**
 * 安全模式创建的广告工厂类
 * @param {string} type 广告类型
 * @param {string} desc 广告描述
 */
let AdFactory = function (type, desc) {
    
    
    if (this instanceof AdFactory) {
    
    
        var instance = new this[type](desc);
        return instance;
    } else {
    
    
        return new AdFactory(type, desc);
    }
}

// 广告工厂原型中设置创建所有类型数据对象的基类
AdFactory.prototype = {
    
    
    // JAVA的广告
    JAVAAd: function (desc) {
    
    
        this.desc = desc;
        this.show = function () {
    
    
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'green';
            document.getElementById('container').appendChild(div);
        }
    },
    // C#的广告
    CSharpAd: function (desc) {
    
    
        this.desc = desc;
        this.show = function () {
    
    
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'red';
            document.getElementById('container').appendChild(div);
        }
    },
    // UI的广告
    UIAd: function (desc) {
    
    
        this.desc = desc;
        this.show = function () {
    
    
            var div = document.createElement('div');
            div.innerHTML = desc;
            div.style.color = 'blue';
            document.getElementById('container').appendChild(div);
        }
    }
};

let ads = [
    {
    
    type: 'JAVAAd', desc: '【广告需求一】学了就秃头!!!'},
    {
    
    type: 'CSharpAd', desc: '【广告需求二】这个语言可以开发游戏!!!'},
    {
    
    type: 'UIAd', desc: '【广告需求三】这也太酷了吧!!!'},
];

ads.forEach(ad => {
    
    
    let adFactory = new AdFactory(ad.type, ad.desc);
    adFactory.show();
})

出现的都是幻觉-抽象工厂模式

抽象工厂模式(Abstract Factory)

通过对类的工厂抽象使其业务用于对产品类簇的创建,而不负责创建某一类产品的实例。

⚠ 注意:抽象方法只负责告诉开发者要实现什么方法,并不负责真正的逻辑处理,可以理解为子类要实现所定义功能的模板

示例:

// 抽象类

/**
 * 抽象工厂方法 - 寄生式继承
 * @param {Class} subType       子类
 * @param {Class} superType     父类
 */
var VehicleFactory = function (subType, superType) {
    
    
    // 判断抽象工厂中是否有该抽象类
    if (typeof VehicleFactory[superType] === 'function') {
    
    
        // 缓存类
        function F() {
    
     };
        // 继承父类属性和方法
        F.prototype = new VehicleFactory[superType]();
        // 将子类constructor指向子类
        subType.constructor = subType;
        // 子类原型继承“父类”
        subType.prototype = new F();
    } else {
    
    
        // 不存在该抽象类抛出错误
        throw new Error('未创建该抽象类');
    }
}

/**
 * 小汽车父抽象类
 *      如果定义的子类继承自小汽车就需要实现 getPrice getSpeed 方法,否则调用报错
 */
VehicleFactory.Car = function () {
    
     this.type = '【父类】小汽车'; };
VehicleFactory.Car.prototype.getPrice = function () {
    
     return new Error('您尚未定义此方法,请定义后在调用!'); };
VehicleFactory.Car.prototype.getSpeed = function () {
    
     return new Error('您尚未定义此方法,请定义后在调用!'); };

/**
 * 公交车父抽象类
 *      如果定义的子类继承自公交车就需要实现 getPrice getPassengerNum 方法,否则调用报错
 */
VehicleFactory.Bus = function () {
    
     this.type = '【父类】公交车'; };
VehicleFactory.Bus.prototype.getPrice = function () {
    
     return new Error('您尚未定义此方法,请定义后在调用!'); };
VehicleFactory.Bus.prototype.getPassengerNum = function () {
    
     return new Error('您尚未定义此方法,请定义后在调用!'); };
// 实例1:

/**
 * 宝马 - 小汽车的子类
 * @param {number} price 价格(单位万)
 * @param {number} speed 速度(单位km/h)
 */
let BMW = function (price, speed) {
    
    
    this.type = '【子类】宝马';
    this.price = price;
    this.speed = speed;
};
VehicleFactory(BMW, 'Car'); // 'Car' ---> VehicleFactory.Car
BMW.prototype.getPrice = function () {
    
     return this.price; };
let bmw = new BMW(30, 120);
console.log(bmw.getPrice()); // 30
console.log(bmw.getSpeed()); // Error: 您尚未定义此方法,请定义后在调用!
// 实例2:

/**
 * 兰博基尼 - 小汽车的子类
 * @param {number} price 价格(单位万)
 * @param {number} speed 速度(单位km/h)
 */
let Lamborghini = function (price, speed) {
    
    
    this.type = '【子类】兰博基尼';
    this.price = price;
    this.speed = speed;
};
VehicleFactory(Lamborghini, 'Car'); // 'Car' ---> VehicleFactory.Car
Lamborghini.prototype.getPrice = function () {
    
     return this.price; };
Lamborghini.prototype.getSpeed = function () {
    
     return this.speed; };
let lamborghini = new Lamborghini(130, 260);
console.log(lamborghini.getPrice()); // 130
console.log(lamborghini.getSpeed()); // 260
// 实例3:

/**
 * 宇通客车 - 公交车的子类
 * @param {number} price 价格(单位万)
 * @param {number} count 限乘(单位人)
 */
let Yutong = function (price, count) {
    
    
    this.type = '【子类】宇通客车';
    this.price = price;
    this.count = count;
};
VehicleFactory(Yutong, 'Bus'); // 'Bus' ---> VehicleFactory.Bus
Yutong.prototype.getPrice = function () {
    
     return this.price; };
Yutong.prototype.getPassengerNum = function () {
    
     return this.count; };
Yutong.prototype.getOtherFun = function () {
    
     return '其他方法···'; };
let yutong = new Yutong(15, 24);
console.log(yutong.getPrice());         // 15
console.log(yutong.getPassengerNum());  // 24
console.log(yutong.getOtherFun());      // 其他方法···

分即是合-建造者模式

建造者模式(Builder)

将一个复杂对象的构建层与其表示层相互分离,同样的构建过程可采用不同的表示。

需求:

  • 简历录入:
    • 要求除了可以将他们的兴趣爱好以及一些特长发布在页面里,其他信息,如他们的联系方式,不要发布在网站上;
    • 应聘者想找的工作是可以分类的
      • 比如对于喜欢编程的人来说他们要找的职位就是工程师(engineer)了,当然这里可能还有一些描述。比如‘每天沉醉于编程……’”;
      • 比如创建用户信息如用户姓名等要独立处理,因为他们是要隐藏显示的;
      • 比如这些应聘者也要独立创建,因为他们代表一个整体;
      • 还有这些工作职位也要独立创建,他们是应聘者拥有的一部分,而且种类很多;
    • 不仅仅应聘者需要创建,每位应聘者的信息、应聘职位都要创建
  • ⚠ 注意:我们需要的不仅仅是应聘者的一个实例,还要在创建过程中注意一下这位应聘者都有哪些兴趣爱好、他的姓名等信息,他所期望的职位是什么,等等。那么这些关注点都是需要我们创建的,因此用到了建造者模式
// 人类
let Human = function (param) {
    
    
    // 技能
    this.skill = param && param.skill || '保密';
    // 兴趣爱好
    this.hobby = param && param.hobby || '保密';
}

// 人类原型上的方法
Human.prototype = {
    
    
    // 获取技能
    getSkill: function () {
    
    
        console.log(this.skill);
    },
    // 获取爱好
    getHobby: function () {
    
    
        console.log(this.hobby);
    }
};

// 姓名类
let Named = function (name) {
    
    
    var that = this;
    // 构造器
    // 构造函数解析姓名的姓与名
    (function (name, that) {
    
    
        if (name.indexOf('-') > -1) {
    
    
            that.fullName = name.split('-').join('');   // 全名
            that.lastName = name.split('-')[0];         // 姓
            that.firstName = name.split('-')[1];        // 名
        }
    })(name, that);
};

// 职位类 (对应的职位和职位描述)
let Work = function (workType) {
    
    
    var that = this;
    // 构造器
    // 构造函数中通过传入的职位特征来设置相应职位以及默认的描述
    (function (workType, that) {
    
    
        switch (workType) {
    
    
            case 'code':
                that.workType = '工程师';
                that.workDescript = '每天沉醉于编程';
                break;
            case 'UI':
            case 'UE':
                that.workType = '设计师';
                that.workDescript = '设计更似一种艺术';
                break;
            case 'teach':
                that.workType = '教师';
                that.workDescript = '分享也是一种快乐';
                break;
            default:
                that.workType = workType;
                that.workDescript = '对不起,我们还不清楚您所选择职位的相关描述';
        }
    })(workType, that);
}

// 期望的职位
Work.prototype.changeWorkType = function (workType) {
    
    
    this.hopeWork = new Work(workType);
};

// 对期望职位的描述
Work.prototype.changeDescript = function (desc) {
    
    
    this.hopeWork.workDescript = desc;
};

/**
 * 应聘者建造者
 * @param {*} name      姓名(全名)
 * @param {*} workType  期望职位类别
 * @returns 应聘者对象
 */
let Person = function (param, name, workType) {
    
    
    // 创建应聘者缓存对象
    var _person = new Human(param);
    // 创建应聘者姓名解析对象
    _person.name = new Named(name);
    // 创建应聘者期望职位
    _person.nowWork = new Work(workType);
    // 将创建的应聘者对象返回
    return _person;
};

let person = new Person({
    
     skill: '没有技能', hobby: '爱好女' }, '张-三疯', 'code');
/**
 * Human {
 *      skill: "没有技能",
 *      hobby: "爱好女",
 *      name: Named {fullName: '张三疯', lastName: '张', firstName: '三疯'},
 *      nowWork: Work {workType: '工程师', workDescript: '每天沉醉于编程'}
 * }
 */
person.getSkill();  // 没有技能
person.getHobby();  // 爱好女
console.log(person);
/**
 * Human {
 *      skill: "没有技能",
 *      hobby: "爱好女",
 *      name: Named {fullName: '张三疯', lastName: '张', firstName: '三疯'},
 *      nowWork: Work {
 *          hopeWork: Work {workType: '设计师', workDescript: '设计更似一种艺术'},
 *          workType: '工程师', workDescript: '每天沉醉于编程'
 *      }
 * }
 */
person.nowWork.changeWorkType('UE');
console.log(person);
/**
 * Human {
 *      skill: "没有技能",
 *      hobby: "爱好女",
 *      name: Named {fullName: '张三疯', lastName: '张', firstName: '三疯'},
 *      nowWork: Work {
 *          hopeWork: Work {workType: '设计师', workDescript: '躺着就能拿钱'},
 *          workType: '工程师', workDescript: '每天沉醉于编程'
 *      }
 * }
 */
person.nowWork.changeWorkType('UE');
person.nowWork.changeDescript('躺着就能拿钱');
console.log(person);

与工厂模式的区别

  • 【工厂模式】 - 注重的是生产出来的结果
    • 工厂模式主要是为了创建对象实例或者类簇(抽象工厂),关心的是最终产出(创建)的是什么。不关心你创建的整个过程,仅仅需要知道你最终创建的结果,所以通过工厂模式我们得到的都是对象实例或者类簇;
  • 【建造者模式】 - 注重的是生产过程中的细节
    • 建造者模式在创建对象时要更为复杂一些,虽然其目的也是为了创建对象,但是它更多关心的是创建这个对象的整个过程,甚至于创建对象的每一个细节;
    • 比如创建一个人,我们创建的结果不仅仅要得到人的实例,还要关注创建人的时候,这个人应该穿什么衣服,男的还是女的,兴趣爱好都是什么;

语言之魂-原型模式

原型模式(Prototype)

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

需求:创建一个焦点轮播图

<p>自动切换</p>
<img id="carousel1" class="carousel" src=""/>
<p>监听滚动切换</p>
<img id="carousel2" class="carousel" src=""/>
// 图片轮播类
var Carousel = function (imgList, container) {
    
    
    // 轮播索引
    this.index = 0;
    // 轮播图片数组
    this.imgList = imgList;
    // 轮播图片容器     
    this.container = container;
    // 创建轮播图片
    this.create = function () {
    
    
        let img = document.getElementById(this.container);
        img.src = this.imgList[this.index];
    };
    // 切换下一张图片
    this.change = function () {
    
    
        const that = this;
        setInterval(function () {
    
    
            let img = document.getElementById(that.container);
            that.index++;
            if (that.index > that.imgList.length - 1) {
    
    
                that.index = 0;
            }
            console.log(`%c【${
      
      container}】 ---> ${
      
      that.index}`, 'background-color: pink; color: black;');
            img.src = that.imgList[that.index];
        }, 2000)
    };
};

在这里插入图片描述

  • 默认的轮播
    let imgList = [
        'https://dummyimage.com/500x300/f00/fff&text=00',
        'https://dummyimage.com/500x300/0f0/fff&text=01',
        'https://dummyimage.com/500x300/00f/fff&text=02',
        'https://dummyimage.com/500x300/ff0/fff&text=03',
        'https://dummyimage.com/500x300/0ff/fff&text=04',
    ];
    
    // 实例化轮播图类 - 自动切换类
    let carousel = new Carousel(imgList, 'carousel1');
    carousel.create();
    carousel.change();
    
  • 重写的轮播形式 (监听滚动切换轮播)
    // 实例化轮播图类 - 上下滑动切换类
    let SlideCarousel = function (imgList, container) {
          
          
        const that = this;
        // 构造函数继承图片轮播类
        Carousel.call(that, imgList, container);
        // 重写继承的切换下一张图片方法
        that.change = function () {
          
          
            let img = document.getElementById(that.container);
            // 监听滚动
            img.addEventListener('wheel', function (e) {
          
          
                // 向上滚动
                if (e.wheelDelta > 0) {
          
          
                    that.index++;
                    if (that.index > that.imgList.length - 1) {
          
          
                        that.index = 0;
                    }
                }
                // 向下滚动 
                else {
          
          
                    that.index--;
                    if (that.index < 0) {
          
          
                        that.index = that.imgList.length - 1;
                    }
                }
                console.log(`%c【${
            
            container}】 ---> ${
            
            that.index}`, 'background-color: yellow; color: black;');
                img.src = that.imgList[that.index];
            });
        }
    };
    
    // 实例化轮播图类 - 上下滑动切换类
    let slideCarousel = new SlideCarousel(imgList, 'carousel2');
    slideCarousel.create();
    slideCarousel.change();
    
  • 缺点:
    • 属性方法 都写在基类 Carousel 的构造函数里面,那么每次子类继承都要创建一次父类,假如父类的构造函数中创建时存在很多耗时逻辑,或者每次初始化都做一些重复性工作,这样的性能消耗很大。
      • 比如: this.create = ···this.change = ··· 就属于消耗性能的逻辑

【原型模式】优化上述代码:

  • 优化:
    • 为了提高性能,我们需要有一种 共享机制 ,这样每当创建基类时,对于每次创建的一些 简单而又差异化的属性 我们可以 放在构造函数中 ,而我们将一些 消耗资源比较大的方法 放在基类的原型 中,这样就会 避免很多不必要的消耗 ,这也就是 原型模式 的一个雏形。

在这里插入图片描述

  • 默认的轮播
    // 图片轮播类
    var Carousel = function (imgList, container) {
          
          
        // 轮播索引
        this.index = 0;
        // 轮播图片数组
        this.imgList = imgList;
        // 轮播图片容器     
        this.container = container;
    
    };
    
    Carousel.prototype = {
          
          
        // 创建轮播图片
        create: function () {
          
          
            let img = document.getElementById(this.container);
            img.src = this.imgList[this.index];
        },
        // 切换下一张图片
        change: function () {
          
          
            const that = this;
            setInterval(function () {
          
          
                let img = document.getElementById(that.container);
                that.index++;
                if (that.index > that.imgList.length - 1) {
          
          
                    that.index = 0;
                }
                img.src = that.imgList[that.index];
            }, 2000)
        }
    };
    
    let imgList = [
        'https://dummyimage.com/500x300/f00/fff&text=00',
        'https://dummyimage.com/500x300/0f0/fff&text=01',
        'https://dummyimage.com/500x300/00f/fff&text=02',
        'https://dummyimage.com/500x300/ff0/fff&text=03',
        'https://dummyimage.com/500x300/0ff/fff&text=04',
    ];
    
    // 实例化轮播图类 - 自动切换类
    let carousel = new Carousel(imgList, 'carousel1');
    carousel.create();
    carousel.change();
    
  • 重写的轮播形式 (监听滚动切换轮播)
    // 实例化轮播图类 - 上下滑动切换类
    let SlideCarousel = function (imgList, container) {
          
          
        // 构造函数继承图片轮播类
        Carousel.call(this, imgList, container);
    };
    
    SlideCarousel.prototype = new Carousel();
    
    // 重写继承的切换下一张图片方法
    SlideCarousel.prototype.change = function () {
          
          
        const that = this;
        let img = document.getElementById(that.container);
        // 监听滚动
        img.addEventListener('wheel', function (e) {
          
          
            // 向上滚动
            if (e.wheelDelta > 0) {
          
          
                that.index++;
                if (that.index > that.imgList.length - 1) {
          
          
                    that.index = 0;
                }
            }
            // 向下滚动 
            else {
          
          
                that.index--;
                if (that.index < 0) {
          
          
                    that.index = that.imgList.length - 1;
                }
            }
            img.src = that.imgList[that.index];
        });
    };
    
    // 实例化轮播图类 - 上下滑动切换类
    let slideCarousel = new SlideCarousel(imgList, 'carousel2');
    slideCarousel.create();
    slideCarousel.change();
    

***原型模式的特点: ***

原型模式对继承他的子类都可以访问其上的属性和方法,包括杜父类拓展的方法

// 比如:

// ···Code

// 父类上拓展的方法
Carousel.prototype.getImgListCount = function () {
    
    
    console.log(this.imgList.length);
};

// 子类上拓展的方法
SlideCarousel.prototype.getContainer = function () {
    
    
    console.log(this.container);
};

// 子类访问父类上拓展的方法
slideCarousel.getImgListCount();    // 5
// 子类访问自己类上拓展的方法
slideCarousel.getContainer();       // carousel2

原型继承

比如现在没有人的基类,只有人的一些特性类,比如游泳、跑步、唱歌、跳舞等等,那么就可以通过以下方法创造出一个人的实例

function prototypeExtend(...args) {
    
    
    // 缓存类,为实例化返回对象临时创建
    const F = function () {
    
     };
    for (let i = 0; i < args.length; i++) {
    
    
        // 遍历每个模板对象中的属性
        for (let key in args[i]) {
    
    
            // 将这些属性复制到缓存类原型中
            F.prototype[key] = args[i][key];
        }
    }
    // 返回缓存类的一个实例
    return new F();
}

let Swim = function (speed) {
    
    
    this.speed = speed;
    this.swim = function () {
    
    
        console.log('游泳速度 ' + this.speed);
    };
};

let Run = function () {
    
    
    this.run = function (speed) {
    
    
        console.log('跑步速度 ' + speed);
    };
};

let Jump = function () {
    
    
    this.jump = function () {
    
    
        console.log('跳跃动作 ');
    };
};

let person = prototypeExtend(new Swim(20), new Run(), new Jump());
person.run(10);     // 跑步速度 10
person.swim();      // 跑步速度 20
/**
 * F {
 *      [[Prototype]]: {
 *          jump: ƒ (),
 *          run: ƒ (speed),
 *          speed: 20,
 *          swim: ƒ (),
 *          constructor: ƒ ()
 *          [[Prototype]]: Object
 *      }
 * }
 */
console.log(person);

一个人的寂寞-单例模式

单例模式(Singleton)

又被称为单体模式,是只允许实例化一次的对象类。有时我们也用一个对象来规划一个命名空间,井井有条地管理对象上的属性与方法。

const A = {
    
    
    ajax(){
    
    },
    name: 'Lee',
    other: {
    
    
        getName(){
    
    },
    },
};

A.ajax();
A.name;
A.other.getName();

单例模式中的私有变量 - 无法修改的静态变量

  • ⚠ 注意: 静态变量一般大写
const A = (function(){
    
    
    // 私有变量
    const [A, ...B] = [1, 2, 3];
    return {
    
    
        getA(){
    
    
            console.log(A);
        },
        getB(){
    
    
            console.log(B);
        }
    }
})();

A.getA(); // 1
A.getB(); // [2, 3]

惰性单例

// 惰性载入单例
var LazySingle = (function () {
    
    
    // 单例实例引用
    var _instance = null;
    // 单例
    function Single() {
    
    
        /*这里定义私有属性和方法*/
        return {
    
    
            publicMethod: function () {
    
     
                this.publicProperty = '2.0';
            },
            publicProperty: '1.0'
        }
    }
    // 获取单例对象接口
    return function () {
    
    
        // 如果为创建单例将创建单例
        if (!_instance) {
    
    
            _instance = Single();
        }
        // 返回单例
        return _instance;
    }
})();

let instance1 = new LazySingle();
let instance2 = new LazySingle();

instance1.publicMethod();
console.log(instance1.publicProperty);  // 2.0
console.log(instance2.publicProperty);  // 2.0

猜你喜欢

转载自blog.csdn.net/weixin_43526371/article/details/127357136
今日推荐