【JS】经常使用设计模式

JS经常使用设计模式

大型单页应用里,复杂度上升到必定程度时,没有适当的设计模式进行降耦,后续的开发也难如下手。
而设计模式正是为了降耦而存在。javascript

单例模式

单例模式的核心是确保只有一个实例,而且提供全局访问。java

特色

知足“单一职责原则” : 使用代理模式,不在构造函数中判断是否已经建立过该单例;node

知足惰性原则ajax

应用

弹出登录窗口。算法

实例

var getSingle = function (fn) {
    var res;
    return function() {
        return res || (res = fn.apply(this, arguments));
    }
}

var createPopup() {
    var div = document.createElement('div');
    div.innerHTML = "Login window";
    div.style.display = "none"; 
    document.body.appendChild(div);
    return div;
}

var createLoginPopup = getSingle(createPopup);            
//create popup div here by using a given function, 知足两个原则 

document.getElementById("loginBt").onclick = function() {
    var popup = createLoginPopup();
    pop.style.display = "block";
}

构造函数模式

/**
 * 构造一个动物的函数 
 */
function Animal(name, color){
    this.name = name;
    this.color = color;
    this.getName = function(){
        return this.name;
    }
}
// 实例一个对象
var cat = new Animal('猫', '白色');
console.log( cat.getName() );

原型模式

function Person(){  
}

Person.prototype.name = "bill";
Person.prototype.address = "GuangZhou";
Person.sayName = function (){
    alert(this.name);  
}

var person1 = new Person();
var person2 = new Person();
 
//测试代码
alert(person1.name);   // bill
alert(person2.name);    // bill
person1.sayName();    //bill
person2.sayName();    //bill

person1.name = "666";

alert(person1.name);   // 666
alert(person2.name);    // bill
person1.sayName();    //666
person2.sayName();    //bill

混合模式

/**
 * 混合模式 = 原型模式 + 构造函数模式
 */
function Animal(name, color){
    this.name = name;
    this.color = color;

    console.log( this.name  +  this.color)
}
Animal.prototype.getInfo = function(){
    console.log('名称:'+ this.name);
}

function largeCat(name, color){
    Animal.call(null, name, color);

    this.color = color;
}

largeCat.prototype = create(Animal.prototype);
function create (parentObj){
    function F(){}
    F.prototype = parentObj;
    return new F();
};

largeCat.prototype.getColor = function(){
    return this.color;
}
var cat = new largeCat("Persian", "白色");
console.log( cat )

工厂模式

工厂:函数内部产生b对象并返回。设计模式

1. 
function a(name){
  var b = new object();
    b.name = name;
    b.say = function(){
        alert(this.name);
    }   
       return b    
}
2. 
function Animal(opts){
    var obj = new Object();
    obj.name = opts.name;
    obj.color = opts.color;
    obj.getInfo = function(){
        return '名称:'+obj.name +', 颜色:'+ obj.color;
    }
    return obj;
}
var cat = Animal({name: '波斯猫', color: '白色'});
cat.getInfo();

简单工厂模式

简单工厂模式的理念就是建立对象,对不一样类的实例化;只须要建立一个对象,而后经过对这个对象大量的方法和属性,并在最终将对象返回出来浏览器

//basketball base class  
var Baseketball = function(){  
  this.intro = 'baseketball is hotting at unitedstates';  
}  
Baseketball.prototype = {  
  getMember : function(){\  
    console.log('each team needs five players');  
  },  
  getBallSize : function(){  
    console.log('basketball is big');  
  }  
}  
//football base class   
var Football = function(){  
  this.intro = 'football is popular at all of the world';  
}  
Football = function(){  
  getMember = function(){  
  
  },  
  getBallSize = function(){  
  
  }  
}  
//sport factory  
var SportsFactory = function(name){  
  switch(name){  
    case 'NBA':  
      return new Baseketball();  
    case 'wordCup':  
      return new Football();  
  }  
}  
  
//when you want football   
var football = SportsFactory('wordCup');  
console.log(football);  
console.log(football.intro);  
football.getMember();

迭代器模式

装饰者模式缓存

策略模式

定义一个个能够相互替换的算法,而且把他们封装起来。app

特色

  1. 符合开放-封闭原则 : 要修改使用的算法时不用深刻函数内部进行修改,只需修改策略类;
  2. 将算法的实现与使用分离开,提升算法复用性;
  3. 经过组合、委托和多态避免多重条件选择语句;

应用

动画实现不一样的缓动效果。函数

通常分为两个部分:策略类于环境类。策略类用于封装各类算法,而且负责具体的计算过程; 环境类负责接收用户的请求,而且把请求委托给某一个策略类。由于各个策略类实现的算法和计算的结果不一样,而环境类调用策略类的方法倒是相同的,这就体现了多态性。要想实现不一样的算法,只须要替换环境类中的策略类便可。

在js中,咱们没必要构造策略类,可直接使用函数做为策略对象。

示例

var strategies = {
    "s1": function() {
        //algo 1
    },
    "s2": function() {
        //algo 2
    }, 
    "s3": function() {
        //algo 3
    }
 }
 
 var someContext =  new SomeConext();
 someContext.start("s1");  //using s1 to calculate
 //someContext.add("s1");  or add s1 as a rule for validation

外观模式

也可译为门面模式。它为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。引入外观角色以后,使用者只须要直接与外观角色交互,使用者与子系统之间的复杂关系由外观角色来实现,从而下降了系统的耦合度。
好比在家要看电影,须要打开音响,再打开投影仪,再打开播放器等等,引入外观角色以后,只须要调用“打开电影设备”方法就能够。外观角色封装了打开投影仪等操做,给使用者提供更容易使用的方法。

做用

  1. 简化复杂接口
  2. 解耦和,屏蔽使用者对子系统的直接访问

实例

在形式上,外观模式在javascript中就像这样:

function a(x){
   // do something
}
function b(y){
   // do something
}
function ab( x, y ){
    a(x);
    b(y);
}

下面的一个例子,把阻止冒泡和阻止默认事件放到了外观角色中:

var N = window.N || {};

N.tools = {
    stopPropagation : function( e ){
        if( e.stopPropagation ){
            e.stopPropagation();
        }else{
            e.cancelBubble = true;
        }
    },

    preventDefault : function( e ){
        if( e.preventDefault ){
            e.preventDefault();
        }else{
            e.returnValue = false;
        }
    },
    
    stopEvent : function( e ){
        N.tools.stopPropagation( e );
        N.tools.preventDefault( e );
    }

外观模式在javascript的应用主要能够分为两类,某块代码反复出现,好比函数a的调用基本都出如今函数b的调用以前,那么能够考虑考虑将这块代码使用外观角色包装一下来优化结构。还有一种就是对于一些浏览器不兼容的API,放置在外观内部进行判断,处理这些问题最好的方式即是将跨浏览器差别所有集中放置到一个外观模式实例中来提供一个对外接口。

代理模式

代理模式的定义:为其余对象提供一种代理以控制对这个对象的访问。在某些状况下,一个对象不适合或者不能直接引用另外一个对象,而代理对象能够在客户端和目标对象之间起到中介的做用。

虚拟代理

虚拟代理是把一些开销很大的对象,延迟到真正须要它的时候才去建立执行

图片懒加载

//图片加载
let imageEle = (function(){
    let node = document.createElement('img');
    document.body.appendChild(node);
    return {
        setSrc:function(src){
            node.src = src;
        }
    }
})();

//代理对象
let proxy = (function(){
    let img = new Image();
    img.onload = function(){
        imageEle.setSrc(this.src);
    };
    return {
        setSrc:function(src){
            img.src = src;
            imageEle.setSrc('loading.gif');
        }
    }
})();

proxy.setSrc('example.png');

合并http请求

若是有一个功能须要频繁进行请求操做,这样开销比较大,能够经过一个代理函数收集一段时间内请求数据,一次性发出

//上传请求
let upload = function(ids){
    $.ajax({
        data: {
            id:ids
        }
    })
}

//代理合并请求
let proxy = (function(){
    let cache = [],
        timer = null;
    return function(id){
        cache[cache.length] = id;
        if(timer) return false;
        timer = setTimeout(function(){
            upload(cache.join(','));
            clearTimeout(timer);
            timer = null;
            cache = [];
        },2000);
    }    
})();
// 绑定点击事件
let checkbox = document.getElementsByTagName( "input" );
for(var i= 0, c; c = checkbox[i++];){
    c.onclick = function(){
        if(this.checked === true){
            proxy(this.id);
        }
    }
}

缓存代理

缓存代理能够做为一些开销大的运算结果提供暂时的存储,下次运算时,若是传递进来的参数跟以前一致,则能够直接返回前面存储的运算结果

//计算乘积
let mult = function(){
    let result = 1;
    for(let i = 0,len = arguments.length;i < len;i++){
        result*= arguments[i];
    }
    return result;
}

//缓存代理
let proxy = (function(){
    let cache = {};
    reutrn function(){
        let args = Array.prototype.join.call(arguments,',');
        if(args in cache){
            return cache[args];
        }
        return cache[args] = mult.apply(this,arguments);
    }
})();

优缺点

1.优势:代理模式能将代理对象与被调用对象分离,下降了系统的耦合度。代理模式在客户端和目标对象之间起到一个中介做用,这样能够起到保护目标对象的做用。代理对象也能够对目标对象调用以前进行其余操做。
2.缺点:增长了系统的复杂度

观察者模式

模块模式

/**
 * 模块模式 = 封装大部分代码,只暴露必需接口
 */
var Car = (function(){
    var name = '法拉利';
    function sayName(){
        console.log( name );
    }
    function getColor(name){
        console.log( name );
    }
    return {
        name: sayName,
        color: getColor
    }
})();
Car.name();
Car.color('红色');
相关文章
相关标签/搜索