js常用设计模式

工厂模式

工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。

因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

var lev=function(){
            return "嘿哈";
        };
        function Parent(){
            var Child=new Object();
            Child.name="李小龙";
            Child.age=20;
            Child.lev=lev;
            return Child;
        };
        var x=Parent();
        alert(x.name);//李小龙
        alert(x.lev());//嘿哈

说明:
1.建议将属性为方法的属性定义到函数之外,这样可以避免重复创建该方法
2.在引用该对象的时候,使用var x=Parent(),不建议使用var x=new object();因为用了new会出现很多问题。
3.在函数的最后返回该对象

.js构造函数模式

var lev=function(){
            return "嘿哈";
        };
        function Parent(){
            this.name ="李小龙";
            this.age =20;
            this.lev=lev;
        };
        var x=Parent();
        alert(x.name);//李小龙
        alert(x.lev());//嘿哈

说明:
1.与工厂方式相比,使用构造函数方式创建对象无需在函数内部创建对象,而使用this指代,并而函数无需明确return
2.同工厂模式一样,虽然属性的值可以为方法,仍建议该方法定义在函数之外

js原型模式

 var lev=function(){
            return "嘿哈";
        };
        function Parent(){
            Parent.prototype.name ="李小龙";
            Parent.prototype.age =20;
            Parent.prototype.lev=lev;
        };
        var x=Parent();
        alert(x.name);//李小龙
        alert(x.lev());//嘿哈

说明:
1.函数中不对属性进行定义
2.利用prototype属性对属性进行定义

构造函数+原型的js混合模式(推荐)

function Parent(){
            this.name ="李小龙";
            this.age =20;
        };
       Parent.prototype.lev= function(){
            return this.name;
        }
        var x = Parent();
        alert(x.name);
         alert(x.lev());

说明:
1.该模式是指混合搭配使用构造函数和原型方式。
2.将所有的属性,不是方法的定义在函数中,将所有属性值为方法的利用prototype在函数之外定义

构造函数+原型的动态原型模式(推荐)

function Parent(){
            this.name ="李小龙";
            this.age =20;
       if(typeof Parent.lev=="undefined"){
       Parent.prototype.lev= function(){
            return this.name;
        }
        Parent.lev=true;
    }
    };
        var x = Parent();
         alert(x.lev());

说明:
1.动态原型方式可以理解为混合构造函数,原型方式的一个特例
2.该模式中,属性为方法的属性直接在函数中进行了定义,但是因为

 if(typeof Parent.lev=="undefined"){
       Parent.prototype.lev= function(){
            return this.name;
        }
        Parent.lev=true;
    }

从而保证创建该对象的实例时,属性的方法不会被重复的创建

订阅/发布模式(subscribe & publish)

text属性变化了,set方法触发了,但是文本节点的内容没有变化。 如何才能让同样绑定到text的文本节点也同步变化呢? 这里又有一个知识点: 订阅发布模式。
  订阅发布模式又称为观察者模式,定义了一种一对多的关系,让多个观察者同时监听某一个主题对象,这个主题对象的状态发生改变时就会通知所有的观察者对象。
发布者发出通知 =>主题对象收到通知并推送给订阅者 => 订阅者执行相应的操作。

 // 一个发布者 publisher,功能就是负责发布消息 - publish
        var pub = {
            publish: function () {
                dep.notify();
            }
        }
        // 多个订阅者 subscribers, 在发布者发布消息之后执行函数
        var sub1 = { 
            update: function () {
                console.log(1);
            }
        }
        var sub2 = { 
            update: function () {
                console.log(2);
            }
        }
        var sub3 = { 
            update: function () {
                console.log(3);
            }
        }
        // 一个主题对象
        function Dep() {
            this.subs = [sub1, sub2, sub3];
        }
        Dep.prototype.notify = function () {
            this.subs.forEach(function (sub) {
                sub.update();
            });
        }

        // 发布者发布消息, 主题对象执行notify方法,进而触发订阅者执行Update方法
        var dep = new Dep();
        pub.publish();

思路: 发布者负责发布消息、 订阅者负责接收接收消息,而最重要的是主题对象,他需要记录所有的订阅这特消息的人,然后负责吧发布的消息通知给哪些订阅了消息的人。

所以,当set方法触发后做的第二件事情就是作为发布者发出通知: “我是属性text,我变了”。 文本节点作为订阅者,在接收到消息之后执行相应的更新动作。

策略模式

策略模式是指将策略(算法)封装起来,策略的目的是将算法和使用分离开。

我们假设有这样一个应用:

年终绩效评定,有S,A,B,C四种绩效,对应年终奖为4,3,2,1个月工资,设计函数,计算绩效。函数如下:

function calBonus(grade, salary) {
  if (grade === 'S') {
    return salary*4;
  } else if (grade === 'A') {
    return salary*3;
  } else if (grade === 'B') {
    return salary*2;
  } else {
    return salary;
  }
}

函数里各种判断条件,如果算法实现比较复杂,这个函数会异常庞大。我们用策略模式改造函数可如下:

//计算绩效为S的年终
function S(salary) {
  return salary * 4;
}

//策略使用
function calBonus(fn, salary) {
  return  fn(salary);
}

//计算绩效为S,工资为20000的年终
calBonus(S, 20000);

这种方法改写代码后,代码的扩展性就好了一些,实际的算法由函数封装起来,代码的扩展性强了,也方便复用。

这就是策略模式的应用之一,在代码中,将具体的算法或策略封装起来,和使用的场景分离,可以提高代码扩展性和复用率。

代理模式

代理模式很好理解,我们不能直接使用目标函数,而是通过调用代理函数来实现对目标函数的使用。

对于网络代理这个词语,每个同学应该都了解,就是无法直接上网,把上网的请求都发送到代理服务器,由代理服务器请求数据,然后转发给相应人员。

现在有一个场景,某个网页有很多请求,有部分请求是不被允许的,我们使用代理模式实现这一功能。代码如下:

//允许的请求地址
let urlArr = ['/aaa', '/bbb'];

//正常的ajax请求函数,url请求地址,method请求方法, data请求数据,callback回调函数
function commonAjax(url, method, data, callback) {
  //具体实现略过,这个大家应该都知道
}

//代理请求函数
function proxyAjax(url, method, data, callback) {
  if (urlArr.indexOf(url) > -1) {
    commonAjax(url, method, data, callback);
  } else {
    let data = {
      status: false,
      message: '该请求不被允许',
      code: 403
    }
    callback(data);
  }
}

代码解析:urlArr是我们允许的请求地址,其它请求不允许发送,commonAjax是正常的请求函数,proxyAjax是代理请求函数。

注意:代理接口和实际函数接口要保持一致。

后续需求发生变化,不需要限制任何请求,直接调用请求函数即可。现在大部分公司联网都是有限制的,过滤一部分网站,只有公司白名单里的网站才能访问,其功能和上述代码逻辑是一致的。

单例模式

单例模式就是一个实例在整个网页的生命周期里只创建一次,后续再调用实例创建函数的时候,返回的仍是之前创建的实例。在实际开发中应用十分广泛,例如页面中的登录框,显示消息的提示窗,都只需要一个实例即可。
我们以消息提示框为例,展示如何实现单例模式。代码如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>singleTon</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    /* 提示框动画和样式 */
    @keyframes appear{
      from {
        left: 100%;
      }
      to {
        left: 50%;
      }
    }
    .message-tip{
      animation: appear .5s linear;
      position: fixed;
      width: 200px;
      background: red;
      color: #fff;
      line-height: 1.5;
      padding-left: 5px;
      top: 20px;
      left: 50%;
      border: 1px solid #d3d3d3;
      transform: translateX(-50%);
    }
  </style>
</head>
<body>
  <button id="tipsBtn" onclick="showMsg()">提示消息</button>
  <script>
    //创建提示框单例
    let createTips = (function(){
      let tipDom = null;
      function createDom() {
        let div = document.createElement('div');
        div.setAttribute('class', 'message-tip');
        return div;
      }

      return function(msg) {
        if (!tipDom) {
          tipDom = createDom();
        }
        tipDom.innerHTML = msg;
        return tipDom;
      }
    })();

   //验证是够是单例,打印true,则是单例模式
  console.log(createTips('1') === createTips('2'));

   function showMsg() {
     let tips = createTips('这是消息提示');
     document.body.appendChild(tips);
      setTimeout(() => {
        document.body.removeChild(tips);
      }, 5000);
   }
  </script>
</body>
</html>

代码讲解:使用createTips函数创建的div时,如果原先已经创建了div,则直接调用原来的div,不再重新创建。除了提示框,网页中一些登录框等等也可以用单例模式实现,网页的整个生命周期里,只有一个dom被创建。或者js的代码中的某个实例如果共享的,只需要一次,就使用单例模式创建。window对象也是一个单例,整个生命周期里只有一个window对象。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值