CodingMeUp / AboutFE

知识归纳、内容都在issue里
74 stars 14 forks source link

22、JS设计模式 #23

Open CodingMeUp opened 6 years ago

CodingMeUp commented 6 years ago

抽象工厂模式-设计模式

抽象工厂模式(Abstract Factory)

创建的结果不是一个真实的对象实例,而是一个类簇,它制定了类的结构。

image

场景:

一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式

var VehicleFactory=function(subType,superType){
    //判断抽象工厂中是否存在该抽象类
    if(typeof VehicleFactory[subType] === 'function'){
        //缓存类
        function F();
        //继承父类的属性与方法
        F.prototype=new VehicleFactory[superType]();
        //将子类的constructor指向子类
        subType.constructor=subType;
        //子类的原型继承“父类”
        subType.prototype=new F();
    }
    else {
        throw new Error('未创建该抽象类');
    }
}
//小汽车
VehicleFactory.Car=function(){
    this.type='car';
};
VehicleFactory.Car.prototype={
    getPrice:function(){
        return new Error('抽象方法不能调用');
    },
    getSpeed:function(){
        return new Error('抽象方法不能调用');
    }
};
//公交车
VehicleFactory.Bus=function(){
    this.type='bus';
};
VehicleFactory.Bus.prototype={
    getPrice:function(){
        return new Error('抽象方法不能调用');
    },
    getPassengerNum:function(){
        return new Error('抽象方法不能调用');
    }
}

var BMW= function(price,speed){
    this.price=price;
    this.speed=speed;
}
VehicleFactory(BMW,'Car');
BMW.prototype={
    getPrice:function(){
        return this.price;
    },
    getSpeed:function(){
        return this.speed;
    }
};
var YUTONG= function(price,passenger){
    this.price=price;
    this.passenger=passenger;
}
VehicleFactory(YUTONG,'Bus');
YUTONG.prototype={
    getPrice:function(){
        return this.price;
    },
    getPassengerNum:function(){
        return this.passenger;
    }
};

工厂模式总结

三种工厂模式极为相似,目的都是为了解耦。

相互的演变很容易。

在使用时,只需关心是否达到你解耦的效果

CodingMeUp commented 6 years ago

适配器模式-设计模式

适配器模式 Adapter

将一个对象的接品(方法或属性)转化成另外一个接口,以满足用户需求,使对象之间接口的不兼容问题得于兼容。 两个成熟的类需要通信,但是接口不同,由于开闭原则,我们不能去修改这两个类的接口,所以就需要一个适配器来完成衔接过程。

image

框架适配例子

var A={
  g : function(id){
    return document.getElementById(id);
  }
}
var A={
  g : function(id){
    return $('#'+id).get(0);
  }
}

参数适配

function foo(obj){
  var _adapter={
    age:23,
    color:'black'
  };
  for(var i in _adapter){
    _adapter[i]=obj[i]||_adapter[i];
  }
}

CodingMeUp commented 6 years ago

桥接模式-设计模式

桥接模式 Bridge

桥接模式将抽象部分与它的实现部分分离,是它们都可以独立地变化。 它很好的支持了开闭原则和组合锯和复用原则。 实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这些多角度分离出来让他们独立变化,减少他们之间的耦合.

image

桥接模式主要有4个角色组成:

  1. 抽象类
  2. 扩充抽象类
  3. 实现类接口
  4. 具体实现类

案例一分析

var forEach = function (arr, fn) {
    for (var i = 0; i < arr.length; i++) {
        var val = arr[i];
        if (fn.call(val, i, val, arr)) {
            return false;
        }
    }
}
var arr = [1, 2, 3, 4];
forEach(arr, function (i, v) {
    arr[i] = v * 2;
})

forEach函数并不关心fn里面的具体实现。fn里面的逻辑也不会被forEach函数的改写影响。


案例二分析

与单例的结合

var singleton = function( fn ){  
    var result;  
    return function(){  
        return result || ( result = fn .apply( this, arguments ) );  
    };  
};  
var createMask = singleton( function(){  
    return document.body.appendChild( document.createElement('div') );  
});  

singleton是抽象部分, 而createMask是实现部分。 他们完全可以独自变化互不影响。 如果需要再写一个单例的createScript就一点也不费力。

var createScript = singleton( function(){  
    return document.body.appendChild( document.createElement('script') );  
});  
CodingMeUp commented 6 years ago

建造者模式(Builder)-设计模式

建造者模式(Builder)

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

各执其职,拆解流程

image

场景: 一般产品的建造很复杂,那么请用工厂模式,如果产品的建造更复杂,那么请用建造者模式。

跟工厂模式只是多了一个导演类。


//应聘者信息
//人类,有技能,爱好,姓名,工作---Product
var Human=function(param){
  this.skill=param&&param.skill||'保密';
  this.hobby=param&&param.hobby||'保密';
  this.name={};//partA
  this.work={};//partB
}
Human.prototype={
  getSkill : function(){
    return this.skill;
  },
  getHobby : function(){
    return this.hobby;
  }
}
//姓名类,有firstname,secondname---concreteBuilder
var Name=function(name){
  var that=this;
  (function(name,that){
    //setPartA
    that.name=name;
    that.firstName=name.slice(0,name.indexOf(' '));
    that.secondName=name.slice(name.indexOf(' '));
  })(name,that);
}
// 工作类---concreteBuilder
var Work=function(work){
  var that=this;
  (function(work,that){
    //setPartB
    switch (work) {
      case 'code':
        that.work='码农';
        that.workDescript='每天在Bug从中窜';
        break;
      case 'UE':
      case 'UI':
        that.work='设计师';
        that.workDescript='我天生拥有艺术天赋';
      default:

    }
  })(work,that);
}
//---director
var Person = function(name,work){
  var _person=new Human();
  _person.name=new Name(name);
  _person.work=new Work(work);
  return _person;
}

  1. 整体对象类的拆分会无形中增加了结构的复杂性(对象粒度很小,模块利用率低并且变动不太大,建议还是创建整体的对象)
CodingMeUp commented 6 years ago

命令模式-设计模式

命令模式

用于将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化

代码

var CanvasCommand=(function(){
  var canvas=document.getElementById('canvas'),
  ctx = canvas.getContext('2d');
  //内部命令集对象
  var Action = {
    fillStyle : function(c) {
      ctx.fillStyle = c;
    },
    fillRect :function(x,y,w,h) {
      ctx.fillRect(x,y,w,h);
    },
    moveTo :function(x,y) {
      ctx.moveTo(x,y);
    },
    lineTo :function(x,y) {
      ctx.lineTo(x,y);
    }
  }
  return {
    excute : function(msg){
      if(!msg) {
        if(msg.length){
         for(var i=0,len=msg.length;i<len;i++) 
           arguments.callee(msg[i]);
        }else{
          ms.param = Object.prototype.toString.call(msg.param)==="[object Array]"?msg.param : [msg.param];
          Action[msg.command].apply(Action,msg.param);
        }
      }
    }
  }
})();

CanvasCommand.excute([
{command:'fillStyle',param :'red'},
{command:'fillRect',param :[20, 20, 100, 100]}
]);
CodingMeUp commented 6 years ago

组合模式-设计模式

组合模式 Composite

组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。 alt text 组合模式主要有三个角色:

  1. 抽象组件(Component):抽象类,主要定义了参与组合的对象的公共接口
  2. 子对象(Leaf):组成组合对象的最基本对象
  3. 组合对象(Composite):由子对象组合起来的复杂对象

理解组合模式的关键是要理解组合模式对单个对象和组合对象使用的一致性,我们接下来说说组合模式的实现加深理解。

案例一分析

最简单的组合模式,HTML的DOM结构,addClass 的实现

//jq
$(".test").addClass("noTest").remove("test");

模拟一下addClass的实现

var addClass = function (eles, className) {
    if (eles instanceof NodeList) {
        for (var i = 0, length = eles.length; i < length; i++) {
            eles[i].nodeType === 1 && (eles[i].className += (' ' + className + ' '));
        }
    }
    else if (eles instanceof Node) {
        eles.nodeType === 1 && (eles.className += (' ' + className + ' '));
    }
    else {
        throw "eles is not a html node";
    }
}
addClass(document.getElementById("div3"), "test");
addClass(document.querySelectorAll(".div"), "test");

对于NodeList或者是Node来说,客户端调用都是同样的使用了addClass这个接口


案例二分析

产品订单包含多个产品子订单,多个产品子订单组成一个复杂的产品订单。由于Javascript语言的特性,我们将组合模式的三个角色简化成2个角色:

  1. 子对象:在这个例子中,子对象就是产品子订单
  2. 组合对象:这里就是产品的总订单

假设我们开发一个旅游产品网站,其中包含机票和酒店两种子产品,我们定义了子对象如下:

function FlightOrder() { }
FlightOrder.prototyp.create = function () {
    console.log("flight order created");
}
function HotelOrder() { }
HotelOrder.prototype.create = function () {
    console.log("hotel order created");
}

机票订单类和酒店订单类,每个类都有各自的订单创建方法。 接下来我们创建一个总订单类:

function TotalOrders() {
    this.orderList = [];
}
TotalOrders.prototype.addOrder = function (order) {
    this.orderList.push(order);
}
TotalOrders.prototype.create = function (order) {
    for (var i = 0, length = this.orderList.length; i < length; i++) {
        this.orderList[i].create();
    }
}

这个对象主要有3个成员:订单列表,添加订单的方法,创建订单的方法。 在客户端使用的时候如下:

var flight = new FlightOrder();
flight.create();

var orders = new TotalOrders();
orders.addOrder(new FlightOrder());
orders.addOrder(new HotelOrder());
orders.create();

客户端调用展示了两种方式,一种是单一的创建机票订单,一种是创建多张订单,但最终都是通过create方法进行创建,这就是一个很典型的组合模式的应用场景。

CodingMeUp commented 6 years ago

装饰模式-设计模式

装饰模式 Decorator

装饰模式动态地给一个对象添加一些额外的职责,就增加功能来说,它比生成子类更灵活。也可以这样说, 装饰模式把复杂类中的核心职责和装饰功能区分开了,这样既简化了复杂类,有去除了相关类中重复的装饰逻辑。 装饰模式没有通过继承原有类来扩展功能,但却达到了一样的目的,而且比继承更加灵活,所以可以说装饰模式是继承关系的一种替代方案。 alt text

  1. 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
  2. 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类
  3. 装饰角色(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口
  4. 具体装饰角色(ConcreteDecorator):负责给构件对象“贴上”附加的责任

Javascript中的对象添加新的属性是一个非常直接了当的过程。 装饰模式解耦了核心和装饰功能,锁业也是强调了松耦合。

案例一分析

// 最简单的装饰
function vehicle( vehicleType ){
    this.vehicleType = vehicleType || "car";
    this.model = "default";
    this.license = "00000-000";

}

var testInstance = new vehicle( "car" );
console.log( testInstance );

// Outputs:
// vehicle: car, model:default, license: 00000-000

var truck = new vehicle( "truck" );

//添加新能力
truck.setModel = function( modelName ){
    this.model = modelName;
};

truck.setColor = function( color ){
    this.color = color;
};

truck.setModel( "CAT" );
truck.setColor( "blue" );

console.log( truck );

// Outputs:
// vehicle:truck, model:CAT, color: blue

// Demonstrate "vehicle" is still unaltered
var secondInstance = new vehicle( "car" );
console.log( secondInstance );

// Outputs:
// vehicle: car, model:default, license: 00000-000

 案例二分析

带有多个装饰器的装饰对象

// The constructor to decorate
function MacBook() {

  this.cost = function () { return 997; };
  this.screenSize = function () { return 11.6; };

}

// Decorator 1
function Memory( macbook ) {

  var v = macbook.cost();
  macbook.cost = function() {
    return v + 75;
  };

}

// Decorator 2
function Engraving( macbook ){

  var v = macbook.cost();
  macbook.cost = function(){
    return  v + 200;
  };

}

// Decorator 3
function Insurance( macbook ){

  var v = macbook.cost();
  macbook.cost = function(){
     return  v + 250;
  };

}

var mb = new MacBook();
Memory( mb );
Engraving( mb );
Insurance( mb );

// Outputs: 1522
console.log( mb.cost() );

// Outputs: 11.6
console.log( mb.screenSize() );
CodingMeUp commented 6 years ago

外观模式(facade)-设计模式


外观模式(facade)

为一组复杂的子系统接口提供一个更为高级的统一接口,使得对接子系统的访问更容易。套餐饭~~~~ alt text

例子

var getEvent=function(event){
  return event || window.event;
}

var A={
  g : function(id){
    return document.getElementById(id);
  },
  css : function(id,key,value){
    document.getElementById(id).style[key]=value;
  }
}
CodingMeUp commented 6 years ago

工厂方法模式(Factory Method)-设计模式

工厂方法模式(Factory Method)

一个创建产品对象的工厂接口,让子类决定实例化哪一个类,将实际创建工作推迟到子类当中 alt text

场景

轻松创建多个类的实例对象,避免了使用者与对象类间的耦合。


学科书本的广告简介

//广告数据

//用工厂方法模式(加上了安全保证)
var Factory=function(type,content){
    if(this instanceof Factory){
        return new this[type](content);
    }
    else{
        return new Factory(type,content);
    }
}
Factory.prototype={
    JavaScript:function(content){
                   (function(content){
                       console.log('javascript');
                       var div=document.createElement('div');
                       div.innerHTML=content;
                       div.style.color='red';
                       document.body.appendChild(div);
                   })(content);
    },
    Php:function(content){
            (function(content){
                console.log('Php');
                var div=document.createElement('div');
                div.innerHTML=content;
                div.style.color='blue';
                document.body.appendChild(div);
            })(content);
    }
}
//调用
var data=[{type:'JavaScript',content:'this is JavaScript'},
          {type:'Php',content:'this is Php'}];
for(var i=0,len=data.length;i<len;i++){
    Factory(data[i].type,data[i].content);
}
CodingMeUp commented 6 years ago

享元模式-设计模式

享元模式 Flyweight

享元模式为运用共享技术有效的支持大量细粒度的对象。因为它可以通过共享大幅度地减少单个实例的数目,避免了大量非常相似类的开销。. 享元模式是一个类别的多个对象共享这个类别的一个对象,而不是各自再实例化各自的对象。这样就达到了节省内存的目的。

image

在JS中,享元模式主要有下面几个角色组成:

  1. 客户端:用来调用享元工厂来获取内在数据的类,通常是应用程序所需的对象,
  2. 享元工厂:用来维护享元数据的类
  3. 享元类:保持内在数据的类

    案例一分析

    <ul class="menu">
    <li class="item">选项1</li>
    <li class="item">选项2</li>
    <li class="item">选项3</li>
    <li class="item">选项4</li>
    <li class="item">选项5</li>
    <li class="item">选项6</li>
    </ul>

    绑定点击事件,一般用下面的方式绑定

    //jq
    $(".item").on("click", function () {
    console.log($(this).text());
    })
    //因为每个项都绑定了事件,都占用了内存。但是这些事件处理程序其实都是很类似的

    根据事件冒泡原理

    $(".menu").on("click", ".item", function () {
    console.log($(this).text());
    })

    案例二分析

    苹果公司批量生产iphone,iphone的大部分数据比如型号,屏幕都是一样,少数部分数据比如内存有分16G,32G等。 未使用享元模式前,我们写代码如下

    function Iphone(model, screen, memory, SN) {
    this. model  = model;//型号
    this.screen = screen;//屏幕
    this.memory = memory;//内存
    this.SN = SN;
    }
    var phones = [];
    for (var i = 0; i < 1000000; i++) {
    var memory = i % 2 == 0 ? 16 : 32;
    phones.push(new Iphone("iphone6s", 5.0, memory, i));
    }

    创建了一百万个iphone,每个iphone都独立申请一个内存. 优化如下:享元类

    function IphoneFlyweight(model, screen, memory) {
    this.model = model;
    this.screen = screen;
    this.memory = memory;
    }

    享元工厂

    var flyweightFactory = (function () {
    var iphones = {};
    return {
        get: function (model, screen, memory) {
            var key = model + screen + memory;
            if (!iphones[key]) {
                iphones[key] = new IphoneFlyweight(model, screen, memory);
            }
            return iphones[key];
        }
    };
    })();
    function Iphone(model, screen, memory, SN) {
    this.flyweight = flyweightFactory.get(model, screen, memory);
    this.SN = SN;
    }
    var phones = [];
    for (var i = 0; i < 1000000; i++) {
    var memory = i % 2 == 0 ? 16 : 32;
    phones.push(new Iphone("iphone6s", 5.0, memory, i));
    }
    console.log(phones);
CodingMeUp commented 6 years ago

迭代器模式-设计模式

迭代器模式

在不暴露对象内部结构的同时,可以顺序的访问聚合对象内部的元素

需了解的内容

  1. for-of
  2. ... 展开符
  3. iterator

for-of和for-in之间的差别

var list = [3, 5, 7];
list.foo = 'bar';

for (var key in list) {
  console.log(key);
}

for (var value of list) {
  console.log(value);
}

...展开运算符

//ES5
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction.apply(null, args);
//ES6
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction(...args);
//选择性参数
function filter(type, ...items) {
    return items.filter(item => typeof item === type);
}
filter('boolean', true, 0, false);        
filter('number', false, 4, 'Welcome', 7);

iterator

function* list(value) {
  for (var item of value) {
    yield item;
  }
}

for (var value of list([1, 2, 3])) {
  console.log(value);
}

var iterator = list([1, 2, 3]);

console.log(typeof iterator.next); // function
console.log(typeof iterator[Symbol.iterator]); // function

console.log(iterator.next().value); // 1

for (var value of iterator) {
  console.log(value); // 2, 3
}

String,Array,TypedArray,Map和Set都是内置迭代器


var string = "hello";

for (var chr of string) {
  console.log(chr);
}

解构赋值

var hello = 'world';
var [a, b, ...c] = [...hello];
console.log(a, b, c);

function co(generator) {
    function next() {
        var part = generator.next("");
        if (!part.done) {
            part.value(next);
        } else {
            console.log("完成");
        }
    }
    next();
}
function a() {
    setTimeout(function () {
        console.log(1);
    }, 1000);
}
function b(next) {
    setTimeout(function () {
        console.log(2);
        next();
        // return "b";
    }, 2000);

}
function c(next) {
    setTimeout(function () {
        console.log(3);
        next();
    }, 3000);
}
function* get() {
    var result1 = yield c;
    var result2 = yield b;
    var result3 = yield a;
}

co(get());
CodingMeUp commented 6 years ago

惰性模式-设计模式

惰性模式

减少每次代码执行时的重复性分支判断

var A={
  on:function(el,type,fn){
    if(el.addEventListener){
      el.addEventListener(type,fn,false);
    }else if(el.attachEvent){
      el.attachEvent('on' + type,fn)
    }else {
      el['on' + type]=fn;
    }
  }
}
var A={
  on:function(el,type,fn){
    if(el.addEventListener){
      el.addEventListener(type,fn,false);
      A.on=el.addEventListener(type,fn,false);      
    }else if(el.attachEvent){
      el.attachEvent('on' + type,fn);
      A.on=el.attachEvent('on' + type,fn)
    }else {
      el['on' + type]=fn;
      A.on=el['on' + type]=fn;
    }
  }
}
CodingMeUp commented 6 years ago

观察者模式-设计模式

观察者模式

第 23 题:介绍下观察者模式和订阅-发布模式的区别,各自适用于什么场景

观察者模式又不同于发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。

使用观察者模式的好处:

  1. 支持简单的广播通信,自动通知所有已经订阅过的对象。
  2. 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。
  3. 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。

代码

var observer = {
    //订阅
    addSubscriber: function (callback) {
        this.subscribers[this.subscribers.length] = callback;
    },
    //退订
    removeSubscriber: function (callback) {
        for (var i = 0; i < this.subscribers.length; i++) {
            if (this.subscribers[i] === callback) {
                delete (this.subscribers[i]);
            }
        }
    },
    //发布
    publish: function (what) {
        for (var i = 0; i < this.subscribers.length; i++) {
            if (typeof this.subscribers[i] === 'function') {
                this.subscribers[i](what);
            }
        }
    },
    // 将对象o具有观察者功能
    make: function (o) { 
        for (var i in this) {
            o[i] = this[i];
            o.subscribers = [];
        }
    }
};

应用场景

当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式

CodingMeUp commented 6 years ago

原型模式-设计模式

语言之魂--原型模式(Prototype)

用原型实例指向创建对象的类,使用于创建新的对象的类共享原型对象的属性以及方法。 场景:创建复杂的对象时,对于那些需求一直在变化而导致对象结构不停地改变时,将那些比较稳定的属性与方法共用而提取的继承实现。

var LoopImages=function(imgArr,container){
  this.images=imgArr;
  this.container=container;
}
LoopImages.prototype={
  createImage : function(){
    console.log('create');
  },
  changeImage : function(){
    console.log('changeImage');
  }
}
var SlideLoopImg=function(imgArr,container){
  LoopImages.call(this,imgArr,container);
  //private
  this.arrow='private';
}
//继承父类的原型
SlideLoopImg.prototype=new LoopImages();
SlideLoopImg.prototype.changeImage=function(){
  //重写
}
//进价
//对象的属性复制,这里只做浅复制,深复制,请见jquery.extends
function prototypeExtend(){
    var F=function(){},
    args=arguments,
    i=0,
    len=args.length;
    for (;i<len;i++) {
       for(var j in args[i]){
           F.prototype[j]=args[i][j];
       }    
    }
    return new F();
}
var fish=prototypeExtend({
    speed:20,
    swim:function(){
        console.log('游泳速度:'+this.speed);
    },{        
        jump:function(){
            console.log('我在跳跃');
    }
});

思考:上面两种实现有什么差别?

CodingMeUp commented 6 years ago

代理模式-设计模式

代理模式 Proxy

一个对象不能直接引用另一个对象,所以需要通过代理对象在这两个对象之间起到中介的作用。

alt text

案例分析

假如ABC要送酸奶小妹玫瑰花,却不知道她的联系方式或者不好意思,想委托C去送这些玫瑰.

// 先声明美女对象
var girl = function (name) {
    this.name = name;
};

// 这是ABC
var abc = function (girl) {
    this.girl = girl;
    this.sendGift = function (gift) {
        console.log("Hi " + girl.name + ", dudu送你一个礼物:" + gift);
    }
};

// 大叔是代理
var proxyTom = function (girl) {
    this.girl = girl;
    this.sendGift = function (gift) {
        (new abc(girl)).sendGift(gift); // 替dudu送花咯
    }
};

调用方式

var proxy = new proxyTom(new girl("酸奶小妹"));
proxy.sendGift("999朵玫瑰");

代理模式与外观模式的区别

代理的客户对象无法直接访问目标对象,代理对象提供对单独目标对象的访问控制,而外观模式的客户对象可以直接访问子系统中的各个对象,但通常由外观对象提供对子系统个元件功能的简化的共同层次的调用接口。

代理模式与适配器的区别

二者都属于一种衔接性质的功能。代理对象和被代理对象的接口是同一个,但是客户没法直接访问被代理者,只能通过代理对象去完成被代理对象的访问。而适配器模式是将多个子系统封装起来,提供一个统一的外部接口,客户只需要使用这个外部接口即可访问对象的子系统了。

外观跟适配器的区别

二者都是对显存系统的封装。外观模式定义了一个新的接口,而适配器则是复用了一个原有的接口;适配器是用来适配对象的,而外观则是用来适配整个子系统的。

CodingMeUp commented 6 years ago

简单工厂模式-设计模式

简单工厂模式(Simple Factory)

由一个工厂对象决定创建出哪一种产品类的实例. 专门定义一个类来负责创建其他类的实例,被创建的实例常常具有共同的父类.

alt text

场景

创建单一的对象



//alert
var LoginAlert=(function(){
    class LoginAlert {  
        constructor(text) {
            this.text = text;
        };
        show(){  
            //do  
            console.log(this.text);
        };        
    };
    return LoginAlert;
})();
var nickNameAlert=new LoginAlert('昵称不合法!');
nickNameAlert.show();

//confirm
var LoginConfirm=(function(){
  class LoginConfirm{
    constructor(text){
      this.text=text;
    };
    show(){
      //do
      console.log(this.text);
    };
  };
  return LoginConfirm;
})();
var nameConfirm=new LoginConfirm('你的用户名不存在!','Confirm:');
nameConfirm.show();

//simple factory
var LoginPopFactory=(function(){
  class LoginPopFactory{
    constructor(name,text){
      switch(name){
        case 'Alert':
           return new LoginAlert(text);
        case 'Confirm':
        default:
           return new LoginConfirm(text);
      }
    };

  };
  return LoginPopFactory;
})();
var nameConfirm=new LoginPopFactory('Confirm','你的用户名不存在!');
nameConfirm.show();

思考: 优缺点各个什么?

CodingMeUp commented 6 years ago

单例模式(Singleton)-设计模式


单例模式(Singleton)

只允许实例化一次的对象类

//全名空间的方式
var Tool={
  Ajax:{
    get:function(){},
    post:function(){}
  },
  Util:{
    method1:function(){}
  }
}
//惰性
var LazySingle=(function(){
  var _instance=null;
  function Single(){
    //公共方法与属性
    return {
      pubMethod:function(){},
      pubProperty:'1'
    }
  }
  //获取单例对象
  return function () {
    if(!_instance){
      _instance=Single();
    }
    return _instance;
  }
})();
CodingMeUp commented 6 years ago

状态模式-设计模式

状态模式

当一个对象的内部状态发生改变时,会导致其行为的改变,这看起来像是改变了对象

最简单的状态对象的实现

当我们发现我们的代码有很多的if时,可以考虑

function showResult(result){
  if(result === 0){
    consol.log(0);
  }else if(result === 1){
    consol.log(1);
  }else if(result === 2){
    consol.log(2);
  }else if(result === 3){
    consol.log(3);
  }
}
showResult(2);

改进后的代码


var ResultState=function(){
  var States ={
    state0:function(){
      console.log(0);
    },
    state1:function(){
          console.log(1);
    },
    state2:function(){
          console.log(2);
    },
    state3:function(){
          console.log(3);
    }
  }
  return {
    show: function(result){
      States['state' + result] && States['state' + result]();
    }
  }
}

超级玛丽

var MarryState=function(){
// 内部状态私有变量
  var _currentState={},
      states={
        jump:function(){
          console.log('jump');
        },
        move:function(){
          console.log('move');
        },
        shoot:function(){
          console.log('shoot');
        },
        jump:function(){
          console.log('jump');
        }
      };
      // 动作控制类
  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('go action');
      // 遍历内部状态保存的动作
      for(var i in _currentState){
        //如果该动作存在则执行
        states[i]&&states[i]();
      }
      return this;
    }
  }
  // 返回接口方法,change、goes
  return {
    change:Action.changeState,
    goes:Action.goes
  }
}

var marry=new MarryState();
marry.change('jump','shoot')
     .goes()
     .goes()
     .change('shoot')
     .goes();

与strategy的区别

  1. 一个是实现把对象的内在状态的变化封装起来,用外部行为来表现出来;
  2. 一个是封装一系列平行且复杂多变的实现方式,
CodingMeUp commented 6 years ago

策划模式-设计模式

策划模式

将定义的一组算法封装起来,使其相互之间可以替换。封装的算法具有一定的独立性,不会随客户端变化而变化。

代码

折扣

var PriceStrategy=function(){
  var Strategy ={
    return30:function(price){
      return +price + parseInt(price / 100)*30;
    },
    return50:function(price){
      return +price + parseInt(price / 100)*50;
    },
    return80:function(price){
      return +price + parseInt(price / 100)*80;
    },
    return90:function(price){
      return +price + parseInt(price / 100)*90;
    },
  }
  return function(algorithm,price){
    return stragtegy[algorithm] && stragtegy[algorithm] (price)
  }
}();

表单验证

var InputStrategy=function(){
  var Strategy ={
    notNull:function(value){
      return /\s+/.test(value);
    },
    number:function(value){
      return /^[0-9]+(\.[0-9]+)?$/.test(value);
    }
  }
  return {
    addStrategy:function(){
     ///  
    },
    check:function(){
     value = value.replace(/^\s+|\s+$/g,'');
     return stragtegy[type] && stragtegy[type] (value) 
    }
  }

}();
CodingMeUp commented 6 years ago

节流模式与去抖-设计模式

节流模式与去抖

对重复的业务逻辑进行节流控制,执行最后一次操作并取消其他操作,以提高性能。

throttler

  1. 鼠标移动,mousemove 事件
  2. DOM 元素动态定位,window对象的resize和scroll 事件

    debounce

  3. 文本输入keydown 事件,keyup 事件,例如做autocomplete

throttler&& debounce

/*
* 频率控制 返回函数连续调用时,fn 执行频率限定为每多少时间执行一次
* @param fn {function}  需要调用的函数
* @param delay  {number}    延迟时间,单位毫秒
* @param immediate  {bool} 给 immediate参数传递false 绑定的函数先执行,而不是delay后后执行。
* @return {function}实际调用函数
*/
var throttle = function (fn,delay, immediate, debounce) {
   var curr = +new Date(),//当前事件
       last_call = 0,
       last_exec = 0,
       timer = null,
       diff, //时间差
       context,//上下文
       args,
       exec = function () {
           last_exec = curr;
           fn.apply(context, args);
       };
   return function () {
       curr= +new Date();
       context = this,
       args = arguments,
       diff = curr - (debounce ? last_call : last_exec) - delay;
       clearTimeout(timer);
       if (debounce) {
           if (immediate) {
               timer = setTimeout(exec, delay);
           } else if (diff >= 0) {
               exec();
           }
       } else {
           if (diff >= 0) {
               exec();
           } else if (immediate) {
               timer = setTimeout(exec, -diff);
           }
       }
       last_call = curr;
   }
};

/*
* 空闲控制 返回函数连续调用时,空闲时间必须大于或等于 delay,fn 才会执行
* @param fn {function}  要调用的函数
* @param delay   {number}    空闲时间
* @param immediate  {bool} 给 immediate参数传递false 绑定的函数先执行,而不是delay后后执行。
* @return {function}实际调用函数
*/

var debounce = function (fn, delay, immediate) {
   return throttle(fn, delay, immediate, true);
};