JS中常用的设计模式

1.工厂模式:

定义:不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,这个函数被视为一个工厂

分类:简单工厂,工厂方法,抽象工厂

区别:简单工厂是将创建对象的步骤放在父类进行,工厂方法是延迟到子类中进行,它们两者都可以总结为:“根据传入的字符串来选择对应的类”,而抽象工厂则是:“用第一个字符串选择父类,再用第二个字符串选择子类”

  1. 实现简单工厂 (ES5):
var UserFactory = function (role) {
    
    
    function Admin() {
    
    
        this.name = "管理员",
        this.viewPage = ['首页', '查询', '权限管理']
    }
    function User() {
    
    
        this.name = '普通用户',
        this.viewPage = ['首页', '查询']
    }
    switch (role) {
    
    
        case 'admin':
            return new Admin();
            break;
        case 'user':
            return new User();
            break;
        default:
            throw new Error('参数错误, 可选参数: admin、user');
    }
}
var admin = UserFactory('admin');
var user = UserFactory('user');
  1. 实现工厂方法 (ES5):
//安全模式创建的工厂方法函数
var UserFactory = function (role) {
    
    
    if (this instanceof UserFactory) {
    
    
        var s = new this[role]();
        return s;
    } else {
    
    
        return new UserFactory(role);
    }
}
//工厂方法函数的原型中设置所有对象的构造函数
UserFactory.prototype = {
    
    
    Admin: function () {
    
    
        this.name = "管理员",
        this.viewPage = ['首页', '查询', '权限管理']
    },
    User: function () {
    
    
        this.name = '用户',
        this.viewPage = ['首页', '查询']
    }
   }
//调用
var admin = UserFactory('Admin');
var user = UserFactory('User');

2.单例模式:

定义:保证一个类仅有一个实例,并且提供一个可以访问它的访问点

实现:用一个变量来标识实例是否已经存在,如果存在,则直接返回已经创建好的实例,反之就创建一个对象

场景:模态框、浏览器window对象,弹窗,无论点击多少次,弹窗只应该被创建一次

简单实现 (ES6):

class Singleton {
    
    
    //构造函数
    constructor(name) {
    
    
        this.name = name;
    }
    //实例方法
    getName() {
    
    
        console.log(this.name);
    }
    //静态方法获取实例对象
    static getInstance(name) {
    
    
        if (!this.instance) {
    
    
            this.instance = new Singleton(name);
        }
        return this.instance;
    }
}
//创建静态属性作为唯一标识
Singleton.instance = null;
//验证
var a = Singleton.getInstance('a');
var b = Singleton.getInstance('b');
a.getName(); //a
b.getName(); //a
console.log(a===b);//true

3.观察者模式(发布-订阅):

定义:当一个对象的状态发生变化时,所有依赖于他的对象都将得到通知

实现:指定一个发布者,给发布者添加一个缓存列表,列表用于存放回调函数以便通知订阅者, 发布者发布消息的时候,会遍历这个缓存列表,触发里面存放的订阅者回调函数

场景:

//订阅消息
myDiv.addEventListener('click', function(){
    
    
    console.log("myDiv被点击了")
});
//发布消息
myDiv.click();

简单实现 (ES6):

//发布者(售楼处)
class SalesOffices{
    
    
    constructor(){
    
    
        //缓存列表(售楼花名册)
        this.clientList = [];
    }
    //订阅函数(订阅售楼消息)
    listen(fn){
    
    
        this.clientList.push(fn);
    }
    //发布函数(发布售楼消息)
    trigger(){
    
    
        //遍历花名册,给他们发消息
        for(var i=0;i<this.clientList.length;i++){
    
    
            var fn = this.clientList[i];
            fn.apply(this,arguments);
        }
    }
}
//实例化
var salesOffices = new SalesOffices();
//小明订阅售楼消息
salesOffices.listen(function(price){
    
    
    console.log("小明" + price);
});
//小红订阅售楼消息
salesOffices.listen(function(price){
    
    
    console.log("小红" + price);
});
//售楼处发布售楼消息
salesOffices.trigger("你好,今天的房价为2万一平!");
//打印结果:
小明你好,今天的房价为2万一平!
小红你好,今天的房价为2万一平!

4.构造函数模式:

function Person(name , age){
    
    
  this.name = name;
  this.age = age;
  this.sayName = function(){
    
    
    console.log(this.name)
  }
}

var person1 = new Person('ccc', 18)
var person2 = new Person('www', 18)
person1.sayName()      // --> 'ccc'

**person1 和person2 分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性指向Person。这正是构造函数模式胜过工厂模式的地方。**如下:

console.log(person1 instanceof Person)      // --> true
console.log(person1 instanceof Object)      // --> true
console.log(person2 instanceof Person)      // --> true
console.log(person2 instanceof Object)      // --> true

构造函数模式与工厂模式的区别:

  1. 没有显式的创建对象
  2. 直接将属性和方法赋给了this对象
  3. 没有return 语句

要创建Person的新实例,必须使用new操作符。以这种方式调用构造函数实际上会经历一下4个步骤:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
  3. 执行构造函数中的代码(为这个新对象添加属性)
  4. 返回新对象

5.代理模式:

代理模式的定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。

常用的虚拟代理形式:某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去创建(例:使用虚拟代理实现图片懒加载)

图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。

var imgFunc = (function() {
    
    

  var imgNode = document.createElement('img');

  document.body.appendChild(imgNode);  //是真正挂在到页面上的图片,但平没有设置src

  return {
    
    

    setSrc: function(src) {
    
    

      imgNode.src = src;

    }

  }

})();

var proxyImage = (function() {
    
    

  var img = new Image();  // 此img 对象用于加载真实图片

  img.onload = function() {
    
     // 加载好后将图片链接赋值到挂载图片的src

    imgFunc.setSrc(this.src);

  }

  return {
    
    

    setSrc: function(src) {
    
    

      imgFunc.setSrc('./loading,gif'); // 先让挂载图片显示loading

      img.src = src; // 让img去加载真实的图片

    }

  }

})();

proxyImage.setSrc('./pic.png');

猜你喜欢

转载自blog.csdn.net/weixin_46071217/article/details/108567019