js设计模式一

74 阅读7分钟

设计模式的定义是:

在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案

通俗一点说,设计模式是在某种场合下对某个问题的一种解决方案。如果再通俗一点说,设 计模式就是给面向对象软件开发中的一些好的设计取个名字。

设计模式不是高深技术,奇技淫巧,只是一种设计思想,针对不同的业务场景,最本质的目的是解耦,为了可扩展性和健壮性。

学习模式的作用

在软件设计中,模式是一些经过了大量实际项目验证的优秀解决方案。熟悉这些模式 的程序员,对某些模式的理解也许形成了条件反射。当合适的场景出现时,他们可以很快地找到 某种模式作为解决方案。

1.0 构造器模式

假设我们要存储一个公司的员工信息

    let people1 = {
        name:'小明',
        age:'18'
    }
    let people2 = {
        name:'小红',
        age:'19'
    }

这才两个人,两个属性,假设有30个属性,300人呢,我们总不能写300个person对象吧 代码太冗余了

我们创建一个构造函数

function Person(name,age){
    this.name = name;
    this.age = age;
}
let person1 = new Person('小明',18)
let person2 = new Person('小红',19)

有多少人我们就 new一下就行了多舒服,当然了我们可以给这些人加一些属性 和方法

比如加一个性别 再加一下自我介绍

function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.selfIntroduce = function(){
        console.log(`我叫${this.name},年龄${this.age},性别${this.sex}`)
    }
}
// 新建一个员工
let person1 = new Person('小明',18,'男')
person1.selfIntroduce() // 我叫小明,年龄18,性别男

这以后再来多少员工我都不怕了

缺点:

  • 构造函数里面的方法需要单独开辟内存空间,当实例比较多的时候会造成内存浪费

所以就有了下面的模式

2.0 原型模式

 function Person(name,age,sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    Person.prototype.selfIntroduce = function(){
        console.log(`我叫${this.name},年龄${this.age},性别${this.sex}`)
    }
    // 新建一个员工
    let person1 = new Person('小明',18,'男')
    person1.selfIntroduce() // 我叫小明,年龄18,性别男

这样就不会造成内存浪费了

上面这两种呀我们的es6 完美的用class融合了这两种的写法

class Person{
        constructor(name,age,sex){
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        selfIntroduce(){
            console.log(`我叫${this.name},年龄${this.age},性别${this.sex}`)
        }
    }
    let person1 = new Person('小明',18,'男')
    person1.selfIntroduce() // 我叫小明,年龄18,性别男

demo: 比如我们页面有很多tab选项卡,用原型的模式处理一下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        *{margin:0;padding: 0}
        ul li{list-style: none}
        ul{
            display: flex;
        }
        li{
            display: inline-block;
            width: 100px;
            height:50px;
            line-height: 50px;
            font-size: 20px;
            text-align: center;
            border:1px solid red;
            box-sizing: border-box;
        }
        .header li{
            cursor: pointer;
        }
        .header .active{
            background: palegreen;
        }

        .box li{
            display: none;
        }
        .box .active{
            width: 600px;
            height:300px;
            display: block;
            background: palegoldenrod;
        }
    </style>
</head>
<body>
<div class="content1">
    <ul class="header">
        <li class="active">1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
    </ul>
    <ul class="box">
        <li class="active">1111</li>
        <li>22222</li>
        <li>333333</li>
        <li>44444</li>
        <li>55555</li>
        <li>6666</li>
    </ul>
</div>

<div class="content2">
    <ul class="header">
        <li class="active">1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
    </ul>
    <ul class="box">
        <li class="active">1111</li>
        <li>22222</li>
        <li>333333</li>
        <li>44444</li>
        <li>55555</li>
        <li>6666</li>
    </ul>
</div>

<script>
    function Tabs(elm,type){
        this.elm = document.querySelector(elm)
        this.headerItems = this.elm.querySelectorAll('.header li')
        this.boxItems = this.elm.querySelectorAll('.box li')
        this.type = type
        console.log(this.headerItems,this.boxItems)
        this.change()
    }
    Tabs.prototype.change = function(){
        for(let i=0;i<this.headerItems.length;i++){
            this.headerItems[i].addEventListener(this.type,()=>{
                for(let m=0;m<this.headerItems.length; m++){
                    this.headerItems[m].classList.remove('active')
                    this.boxItems[m].classList.remove('active')
                }
                this.headerItems[i].classList.add('active')
                this.boxItems[i].classList.add('active')
            })
        }
    }
    new Tabs('.content1','click')
    new Tabs('.content2','mouseenter')
</script>

</body>
</html>

3.0 工厂模式

由一个工厂对象决定创建某一种产品对象类的实例,主要用来创建同一类对象

举例说明,比如我们写后台管理的权限功能,不同用户会展示不同的页面

  1. superAdmin 用户 进入有 home-page,user-page,news-page 三个页面
  2. admin 用户 进入有 home-page,user-page 两个页面
  3. editor 用户 进入有 home-page,news-page 两个页面
  function User(role,pages){
    this.role = role
    this.pages = pages
  }
  // 用户工厂
  function UserFactory(role){
    switch(role){
      case 'superAdmin':
        return new User('superAdmin',['home-page','user-page','news-page'])
        break;
      case 'admin':
        return new User('admin',['home-page','user-page'])
        break;
      case 'editor':
        return new User('editor',['home-page','news-page'])
        break;
      default:
         throw Error('你没权限')
    }
  }
  let user = UserFactory('admin')

es6写法

class User{
      constructor(role,pages){
          this.role = role
          this.pages = pages
      }
      // 变成静态方法
    static UserFactory(role){
          switch(role){
              case 'superAdmin':
                  return new User('superAdmin',['home-page','user-page','news-page'])
                  break;
              case 'admin':
                  return new User('admin',['home-page','user-page'])
                  break;
              case 'editor':
                  return new User('editor',['home-page','news-page'])
                  break;
              default:
                  throw Error('你没权限')
          }
      }
  }
  let user = User.UserFactory('admin')

简单的工厂的优点:

只要正确传入参数,就能获取到你需要的对象,具体里面干了啥不需要知道,

缺点:

创建的对象数量少,对象的创建逻辑不复杂时用,如果复杂就难以维护。

4.0单例模式

单例模式的定义是:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器 中的window对象等。在js开发中,单例模式的用途同样非常广泛。试想一下,当我们单击 登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮, 这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。

用闭包产生一个单一的对象

let singleUser = (function(){
    let init;
    function User(name,age){
        this.name = name;
        this.age = age;
    }
    return function(name,age){
        if(!init){
            init = new User(name,age)
        }
        return init
    }
})()
let user1 = singleUser('小明',18) // {name: '小明', age: 18}
let user2 = singleUser('小红',19) // {name: '小明', age: 18}

es6 方法写一下

    class singleUser{
        constructor(name,age) {
            if(!singleUser.init){
                this.name = name;
                this.age = age;
                singleUser.init = this
            }
            return singleUser.init
        }
    }
    let user3 = new singleUser('小小',18 ) // {name: '小小', age: 18}
    let user4 = new singleUser('大大',19 ) // {name: '小小', age: 18}

demo 创建一个频繁关闭和打开的弹框

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .box{
            position: fixed;
            width: 200px;
            height:200px;
            line-height: 200px;
            text-align: center;
            left:50%;
            top:50%;
            transform: translate(-50%,-50%);
            background: palegoldenrod;
        }
    </style>
</head>
<body>

<!--<div class="box">对话框</div>-->

<button id="open">打开</button>
<button id="close">关闭</button>

<script>
    let singleBox = (function(){
        let init = null;
        return function(){
            if(!init){
                init = document.createElement('div')
                init.innerHTML = '对话框'
                init.classList.add('box')
                init.style.display = 'none'
                document.body.appendChild(init)
            }
            return init
        }
    })()
    document.querySelector('#open').onclick = function(){
        // 创建 box框
        let box = singleBox()
        // 显示 box框
        box.style.display = 'block'
    }
    document.querySelector('#close').onclick = function(){
        // 创建 box框
        let box = singleBox()
        // 隐藏 box框
        box.style.display = 'none'
    }
</script>
</body>
</html>

单例模式主要解决一个全局使用的类频繁的被创建和销毁,占用内存或空间。

5.0 装饰器模式

装饰者(decorator)模式能够在不改变对象自身的基础上,动态的给某个对象添加额外的职责,不会影响原有接口的功能。

JavaScript中的装饰器模式

var Plane = {
        fire:function(){
            console.log('发射普通的子弹');
        }
    }
    var oneDecorator = function(){
        console.log('发射导弹!');
    }
    var fire = Plane.fire;
    Plane.fire = function(){
        fire();
        oneDecorator()
    }
    Plane.fire();

使用AOP(面向切面编程)装饰函数

主要是以为在JavaScript中会存在随着函数的调用,this的指向发生变化,导致执行结果发生变化。

封装before函数

在需要执行的函数之前执行某个新添加的功能函数

//是新添加的函数在旧函数之前执行
Function.prototype.before=function (beforefn) {
    var _this= this;                               //保存旧函数的引用
    return function () {                           //返回包含旧函数和新函数的“代理”函数
        beforefn.apply(this,arguments);            //执行新函数,且保证this不被劫持,新函数接受的参数
                                                   // 也会被原封不动的传入旧函数,新函数在旧函数之前执行
        return _this.apply(this,arguments);
    };
};

封装 after 函数

在需要执行的函数之后执行某个新添加的功能函数

//新添加的函数在旧函数之后执行
Function.prototype.after=function (afterfn) {
    var _this=this;
    return function () {
        var ret=_this.apply(this,arguments);
        afterfn.apply(this,arguments);
        return ret;
    };
};

表单验证

Function.prototype.before=function (beforefn) {
    var _this= this;                               //保存旧函数的引用
    return function () {                           //返回包含旧函数和新函数的“代理”函数
        beforefn.apply(this,arguments);            //执行新函数,且保证this不被劫持,新函数接受的参数
        // 也会被原封不动的传入旧函数,新函数在旧函数之前执行
        return _this.apply(this,arguments);
    };
};

var validata=function () {
    if(username.value===''){
        alert('用户名不能为空!')
        return false;
    }
    if(password.value===''){
        alert('密码不能为空!')
        return false;
    }
}

var formSubmit=function () {
    var param={
        username=username.value;
        password=password.value;
    }

    ajax('post','http://www.xxx.com',param);
}

formSubmit= formSubmit.before(validata);


submitBtn.onclick=function () {
    formSubmit();
}

参考链接

复杂一点的

// 装饰者模式 decorate(装饰)
var tree = {};
tree.decorate = function(){
   console.log('别让树倒了')
}
// Decorator(装饰器) 定义方法,用于添加额外的装饰器,
// 装饰器被实现为构造器函数 都继承自tree对象
tree.getDecorator = function(deco){
   tree[deco].prototype = this
   return new tree[deco];
}
// 创建装饰器 redBalls() ,我们将他设为tree的一个属性(已保持
//全局命名空间的纯净) ,redBall 对象也提供了decorate()方法,
// 注意它先调用了父类的decorate()方法

tree.RedBalls = function(){
   this.decorate = function (){
      this.RedBalls.prototype.decorate();
      console.log('放一些红球')
   }
}
tree.BlueBalls = function(){
   this.decorate = function (){
      this.BlueBalls.prototype.decorate();
      console.log('放一些蓝球1')
   }
}
tree.caidai = function(){
   this.decorate = function (){
      this.caidai.prototype.decorate();
      console.log('放一些彩带')
   }
}

// 再把所有的装饰器都添加到基础对象中
tree = tree.getDecorator('BlueBalls');
tree = tree.getDecorator('RedBalls');
tree = tree.getDecorator('caidai');
tree.decorate();
// 返回的顺序是这个哦
/*别让树倒了
 放一些蓝球1
 放一些红球
 放一些彩带*/

永远不要把希望寄托在任何人身上,爱情也好,友情也罢,你没有前途,爱情就没有未来,你不优秀,认识谁都没用,不要做一个讨好别人的人,而要做一个对别人有价值的人

qdysh.png