javascript 设计模式 工厂模式 单例模式 构造器模式 桥接模式 外观模式 享元模式 代理模式 观察者模式

前言

原生 js 中没有设计模式,它是模仿 java 语言中的设计模式来实现。

设计模式分为三种类型,共23种。

  • 创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
  • 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
  • 行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)、访问者模式。

工厂模式

工厂模式就是做一个对象创建的封装,并将创建的对象 return 出去。

工厂模式适用于:

  • 当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候。
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

工厂模式普通写法:

function CreateMan(name,age) {
    var obj={};
    obj.name=name;
    obj.age=age;
    obj.sayName=function () {
        return this.name;
    };
    return obj;
}
var p1=CreateMan("zhangsan",20);
var p2=CreateMan("wangwu",30);

工厂模式复杂写法,每一个类都应该是一个单独的文件:

class CarFactory{
	static get CAR(){
		return "Car";
	}
	static get BUS(){
		return "Bus";
	}
	static get TAXI(){
		return "TAXI";
	}
	static createCar(type){
		switch(type){
		 case CarFactory.CAR:
		     return new Car();
		 case CarFactory.BUS:
		     return new Bus();
		 case CarFactory.TAXI:
		     return new Taxi();
		}
	}
}

class Car{
	constructor(){
	}
	getName(){
		console.log("这是一个:"+this.constructor.name)
	}
}

class Bus extends Car{
	constructor(){
		super();
	}
}

class Taxi  extends Car{
	constructor(){
		super();
	}
}

var car1=CarFactory.createCar(CarFactory.BUS);
var car2=CarFactory.createCar(CarFactory.TAXI);
car1.getName();//这是一个:Bus
car2.getName();//这是一个:Taxi

单例模式

单例模式就是运行完成后返回的对象是唯一的,如果已经创建出来就直接返回。

单例模式的普通写法:

var obj;
function getInstance(){
    return obj || (obj={});
}

var s=getInstance();
var o=getInstance();
console.log(s===o);//true

单例模式的复杂写法:

//使用闭包的形式,防止在外部更改 _instance 的值
let ViewModel=(function(){
    var _instance;
    return class ViewModel{
        constructor(){

        }
        static getInstance(){
            if(!_instance) _instance=new ViewModel();
            return _instance;
        }
    }
})();
var a=ViewModel.getInstance();
var b=ViewModel.getInstance();
console.log(a===b);//true

构造器模式

构造器是一个当新建对象的内存被分配后,用来初始化该对象的一个特殊函数。对象构造器是被用来创建特殊类型的对象的,首先它要准备使用的对象,其次在对象初次被创建时,通过接收参数,构造器要用来对成员的属性和方法进行赋值。

function CreateMan(name,age) {
    this.name=name;
    this.age=age;
    this.toString=function () {
        return this.name+"年龄:"+this.age;
    }
}
var p1=new CreateMan("zhangsan",20);
var p2=new CreateMan("wangwu",30);
console.log(p1.toString());
console.log(p2.toString());

桥接模式

桥接模式可以理解为将抽象部分与它的实现部分分离,使它们都可以独立地变化。它的本质就是函数的封装,在实现api的时候,桥接模式非常常用。例如 some() 的实现过程,some函数并不关心 fn 里面的具体实现,fn 里面的逻辑也不会被 some 函数的改写影响。

var list = [1,2,3,4,5];
function some(arr,fn){
    for(var i=0;i<arr.length;i++){
    	//如果数组的元素是空,则跳过
        if(arr[i]===undefined) continue;
        //如果fn()的返回结果是true,则返回true
        if(fn(arr[i],i,arr)) return true;
    }
    //默认返回false
    return false;
}
var s = some(list,function(item,index,arr){
    return item>4
})
console.log(s);//true

外观模式

外观模式是一种无处不在的模式,外观模式提供一个高层接口,这个接口使得客户端或者子系统调用起来更加方法。比如:这样就方便组装,如果一开始就把两个写到一个函数中,那就不能够只单独调用其中一个了。

function getName() {
    return "xiaoming";
}
function getSex() {
    return "man";
}
function getUserInfo() {
    var info=getName()+getSex();
    return info;
}
console.log(getUserInfo());

享元模式

享元模式是一种用于性能优化的模式,它的目标是尽量减少共享对象的数量。通俗的讲,享元模式就是用来减少程序所需的对象个数。

举一个例子,网页中的瀑布流,或者webqq的好友列表中,每次往下拉时,都会创建新的div。那么如果有很多div呢?浏览器岂不是卡死了?所以我们会想到一种办法,就是把已经消失在视线外的div都删除掉,这样页面就可以保持一定数量的节点,但是频繁的删除和添加节点,又会带来很大的性能开销。这个时候就可以用到享元模式了,享元模式可以提供一些共享的对象以便重复利用。比如页面中只能显示10个div,那始终出现在用户视线中的这10个div就可以写成享元。原理其实很简单,把刚隐藏起来的div放到一个数组中,当需要div的时候,先从该数组中取,如果数组中已经没有了,再重新创建一个。这个数组里的div就是享元,它们每一个都可以当作任何用户信息的载体。

代理模式

当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一些额外的事情。

代理模式主要有三种:

  • 保护代理,指拒绝访问主体;
  • 虚拟代理,在控制对主体进行访问时,加入了一些额外的操作;
  • 缓存代理,为一些数据大的运算结果提供暂时的缓存,提升效率(类似于闭包中的局部变量)。
// 主体,发送消息
function sendMsg(msg) {
    console.log(msg);
}

// 代理,对消息进行过滤
function proxySendMsg(msg) {
    // 无消息则直接返回,这是保护代理
    if (typeof msg === "undefined") {
        return;
    }    
    // 有消息则进行过滤,这是虚拟代理
    msg = ("" + msg).replace(/小朋友/g, '');
    sendMsg(msg);
}

sendMsg('你好小朋友'); //你好小朋友
proxySendMsg('你好小朋友'); //你好
proxySendMsg(); 

观察者模式

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态时,所有依赖于它的对象都会得到通知并被自动更新。通俗点理解,就是面试官是被观察者,而等待通知的人是观察者。

观察者模式的三个步骤:添加至观察列表,从观察列表中移除,进行观察。

javascript中平时接触的dom事件,其实就是一种观察者模式的体现:

div.onclick=function click(){ console.log("click" ) }

观察者模式实现弹幕效果:

html结构代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .view{
            width: 536px;
            height: 480px;
            border: 1px solid #000000;
            overflow: hidden;
            position: relative;
        }
        input{
            width: 450px;
            border:1px solid #CCCCCC;
            height: 30px;
        }
        button
        {
            width:86px;
            height: 35px;
        }
    </style>
</head>
<body>
    <div class="view">
        <video src="./video/test3.mp4" controls></video>
    </div>
    <input type="text"><button>发送</button>
    <script type="module">
        import Bubble from "./js/observer/Bubble.js";
        var txt;
        init();
        function init(){
            var bn=document.querySelector("button");
            txt=document.querySelector("input");
            bn.addEventListener("click",clickHandler);
            window.addEventListener("keyup",clickHandler);
        }
        function clickHandler(e){
            if(e.type==="keyup" && e.keyCode!==13) return;
            if(txt.value.trim().length===0) return;
            //新建弹幕
            var p=new Bubble(txt.value);
            p.appendTo(".view");
            txt.value="";
        }
    </script>
</body>
</html>

Bubble.js文件:

import TimeManager from "./TimeManager.js";
import IMove from "./IMove.js";

export default class Bubble extends IMove{
    left=0;//弹幕定位的left值
    speed=3;//弹幕移动的速度
    w=0;//弹幕父容器的宽度
    constructor(msg){
    	//执行超类的构造函数
        super();
        //创建弹幕
        this.elem=this.createElem(msg);
    }
    createElem(msg){
    	//单例模式
        if(this.elem) return this.elem;
        //创建弹幕
        var div=document.createElement("div");
        div.textContent=msg;
        Object.assign(div.style,{
            position:"absolute",
            whiteSpace:"nowrap"
        });
        return div;
    }
    appendTo(parent){
    	//添加到父容器中
        if(HTMLElement.isPrototypeOf(parent.constructor)){
            parent.appendChild(this.elem);
        }else if(parent.constructor===String){
            parent=document.querySelector(parent);
            if(!parent) return;
            parent.appendChild(this.elem);
        }
        //获取父容器的一些值
        var rect=parent.getBoundingClientRect();
        this.left=rect.width+10
        Object.assign(this.elem.style,{
            left:this.left+"px",
            top:Math.random()*rect.height/2+"px"
        })
        this.w=this.elem.offsetWidth;
        //添加到观察列表
        TimeManager.add(this);
    }
    update(){
		//执行移动函数
        this.left-=this.speed;
        this.elem.style.left=this.left+"px";
        if(this.left<-this.w){
        	//删除元素并从观察列表移除
            this.elem.remove();
            TimeManager.remove(this);
        }
    }
}

TimeManager.js代码:

import IMove from "./IMove.js";

export default class TimeManager{
    static list=[];
    static ids;
    constructor(){

    }
    //添加到观察列表
    static add(elem){
    	//如果已经添加,则直接跳出
        if(TimeManager.list.indexOf(elem)>-1) return;
        TimeManager.list.push(elem);
        //如果观察列表中有内容并且TimeManager.ids为false时,设置动画
        if(TimeManager.list.length>0 && !TimeManager.ids){
            TimeManager.ids=setInterval(TimeManager.animation,16);
        }
    }
	//从观察列表中移除
    static remove(elem){
        var index=TimeManager.list.indexOf(elem);
        if(index<0) return;
        //先将元素设为null,再移除
        TimeManager.list[index]=null;
        TimeManager.list.splice(index,1);
        //如果观察列表中没有内容时,则清除定时器,并将TimeManager.ids设为0
        if(TimeManager.list.length===0){
            clearInterval(TimeManager.ids);
            TimeManager.ids=0;
        }
    }
	//执行动画
    static animation(){
        for(var i=0;i<TimeManager.list.length;i++){
        	//IMove在这里类似于是一个接口,用来判断当前对象中是不是有update方法
            if(IMove.isPrototypeOf(TimeManager.list[i].constructor)) TimeManager.list[i].update();
        }
    }
}

IMove.js文件,在这里相当是一个接口,用来确定当前new Bubble() 当中是不是有update方法。

export default class IMove{
    constructor(){

    }
    update(){
        
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值