IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> web12.JavaScript设计模式 -> 正文阅读

[JavaScript知识库]web12.JavaScript设计模式

灵活的JavaScript

用对象收编变量

var checkobject = {
    checkName: function () {
        // 验证姓名
    }
}

真假对象

var checkobject = function () {
    return {
        checkName: function () {
            // 验证姓名
        }
    }
}
var a = new checkobject();
a.checkEmail();

var checkobject = function () {
    this.checkName = function () {
        // 验证姓名
    }
}
var a = new checkobject();
a.checkEmail();

面向对象编程

包装明星—封装

创建—个类

var Book = function(id, bookname, price){
    this.id = id;
    this.bookname= bookname;
    this.price = price;
}

通过在类的原型(类也是一个对象,所以也有原型prototype)上添加属性和方法,有两种方式,一种是——为原型对象属性赋值,另一种则是将一个对象赋值给类的原型对象

Book.prototype.display = function(){
    // 展示这本书
}//或者
Book.prototype = {
    display:function(){}
}

属性与方法封装

由于JavaScript的函数级作用域,声明在函数内部的变量以及方法在外界是访问不到的,通过此特性 即可创建类的私有变量以及私有方法。

通过this创建的属性可看作是对象共有属性和对象共有方法

通过this创建的方法不但可以访问共有属性共有方法而且还能访问到 类(创建时)或对象自身的私有属性私有方法

特权方法能访问到类的私有属 性方法

在类外面通过点语法定义的属性以及方法被称为 类的静态共有属性类的静态共有方法

类通过 prototype 创建的属性或者方法在类实例的对象中是 可以通过this访问到的,所以我们将prototype 对象中的属性和方法称为共有属性和共有方法


// 私有属性与私有方法,特权方法,对象公有属性和对象共有方法,构造器
var Book = function (id, name, price) {
    //私有属性
    var num = 1;
    //私有方法
    function checkId() {};
    //特权方法
    this.getName = function () {};
    this.getPrice = function () {};
    this.setName = function () {};
    this.setPrice = function () {};
    //对象公有属性
    this.id = id;
    //对象公有方法
    this.copy = function () {};
    //构造器
    this.setName(name);
    this.setPrice(price);
};

类的私有属性以及静态共有属性在新创建的对象中访问不到,而类的共有属性可以通过点语法访问到

闭包实现

// 利用闭包实现
var Book = (function () {
    //静态私有变量
    var bookNum = 0;
    //静态私有方法
    function checkBook(name) {}
    //返回构造函数
    return function (newId, newName, newPrice) {
        //私有变量
        var name, price;
        //私有方法
        function checkID(id) {}
        //特权方法
        this.getName = function () {};
        this.getPrice = function () {};
        this.setName = function () {};
        this.setPrice = function () {};
        //公有属性
        this.id = newId;
        //公有方法
        this.copy = function () {};
        bookNum++
        if (bookNum > 100)
            throw new Error('我们仅出版100本书.');
        //构造器
        this.setName(name);
        this.setPrice(price);
    }
})();
Book.prototype = {
    //静态公有属性
    isJsBook: false,
    //静态公有方法
    display: function () {}
};

创建对象的安全模式

// 图书安全类
var Book = function (title, time, type) {
    // 判断执行过程中this是否是当前这个对象(如果是说明是用new创建的)
    if (this instanceof Book) {
        this.title = title;
        this.time = time;
        this.type = type;
        // 否则重新创建这个对象
    } else {
        return new Book(title, time, type);
    }
}
var book = Book('Javascript', '2014', 'js');

继承

原型链继承

主要思想:重写子类的prototype属性,将其指向父类的实例。

//原型继承
Cat.prototype = new Animal();
//很关键的一句,将Cat的构造函数指向自身
Cat.prototype.constructor = Cat;

优点:

  1. 简单,容易实现
  2. 继承关系纯粹
  3. 可以通过子类直接访问父类原型链属性和函数

缺点

  1. 子类的所有实例将共享父类的属性
  2. 在创建子类实现时,无法向父类的构造函数传递参数
  3. 无法实现多继承
  4. 为子类增加原型对象上的属性和函数时,必须放在new Animal()函数之后

构造继承

主要思想:在子类的构造函数中通过call()函数改变this的指向,调用父类的构造函数, 从而能将父类的实例的属性和函数绑定到子类的this上

//核心通过call()函数实现Animal的实例的属性和函数的继承
Animal.call(this);

优点:

  1. 可解决子类实例共享父类属性的问题
  2. 创建子类的实例时,可以向父类传递参数
  3. 可以实现多继承

缺点

  1. 实例只是子类的实例,并不是父类的实例
  2. 只能继承父类实例的属性和函数,并不能继承原型对象上的属性和函数
  3. 无法复用父类的实例函数

复制继承

主要思想:首先生成父类的实例,然后通过for…in遍历父类实例的属性和函数,并将其 依次设置为子类实例的属性和函数或者原型对象上的属性和函数

优点:

  1. 支持多继承
  2. 能同时继承实例的属性和函数与原型对象上的属性和函数
  3. 可以向父类构造函数中传递值

缺点:

  1. 父类的所有属性都需要复制,消耗内存。
  2. 实例只是子类的实例 ,并不是父类的实例。

组合继承

**主要思想:**组合了构造继承和原型继承两种方法,一方面在子类的构造函数中通过call() 函数调用父类的构造函数,另一方面,通过改变子类 的prototype属性,继承父类的原型对象上的属性和函数

优点:

  1. 既能继承父类实例的属性和函数,又能继承原型对象的属性和函数。
  2. 即是子类的实例,又是父类的实例。
  3. 不存在引用属性共享的问题
  4. 可以向父类的构造函数中传递参数

缺点

  1. 为父类的实例属性会绑定两次。

寄生组合继承

//设置任意函数Super()
var Super = function(){};
//关键语句 Super()函数的原型指向父类Animal的原型,去掉父类的实例属性
Super.prototype = Animal.prototype;

工厂模式

简单工厂模式

// 篮球基类
var Basketball = function () {
    this.intro = '篮球盛行于美国';
}
Basketball.prototype = {
    getMember: function () {
        console.log('每个队伍需要5名队员');
    },
    getBallsize: function () {
        console.log('篮球很大');
    }
}
// 运动工厂
var sportsFactory = function (name) {
    switch (name) {
        case 'NBA':
            return new Basketball();
        case 'wordcup':
            return new Football();
    }
}

一个对象有时也可代替许多类

function createPop(type, text) {
    // 创建—个对象,并对对象拓展属性和方法
    var o = new object();
    o.content = text;
    o.show = function () {
        // 显示方法
    };
    if (type == 'alert') {
        // 警示框差异部分
    }
    if (type == 'prompt') {
        // 提示框差异部分
    }
    if (type == 'confirm') {
        // 确认框差异部分
    }
    // 将对象返回
    return o;
}
// 创建警示框
var userNameAlert = createPop('alert', '用户名只能是26个字母和数字');

工厂方法模式

通过对产品类的抽象使其创建业务主要负责用于创建多类产品的实 例。

安全的工厂方法

// 安全模式创建的工厂类
var Factory = function (type, content) {
    if (this instanceof Factory) {
        var s = new this[type](content);
        return s;
    } else {
        return new Factory(type, content);
    }
}
// 工厂原型中设置创建所有类型数据对象的基类
Factory.prototype = {
    Java: function (content) {
        // ……
    },
    Javascript: function (content) {
        // ……
    },
    UI: function (content) {
        this.content = content;
        (function (content) {
            var div = document.createElement('div');
            div.innerHTML = content;
            div.style.border = '1px solid red';
            document.getElementById('container').appendchild(div);
        })(content);
    },
    php: function (content) {
        // ……
    }
};

抽象工厂模式

带头模范—抽象类

通过对类的工厂抽象使其业务用于对产品类簇的创建,而不 负责创建某一类产品的实例。(手动地抛出错误来模拟抽象类。)

// 汽车抽象类,当使用其实例对象的方法时会抛出错误
var car = function () {};
car.prototype = {
    getPrice: function () {
        return new Error('抽象方法不能调用');
    },
    getspeed: function () {
        return new Error('抽象方法不能调用');
    }
};

幽灵工厂—抽象工厂模式

作为父类来创建一些子类。

// 抽象工厂方法
var VehicleFactory = function (subType, superType) {
    // 判断抽象工厂中是否有该抽象类
    if (typeof VehicleFactory[superType] === '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('抽象方法不能调用');
    }
};
// 货车抽象类
VehicleFactory.Truck = function () {
    this.type = 'truck';
};
VehicleFactory.Truck.prototype = {
    getPrice: function () {
        return new Error('抽象方法不能调用');
    },
    getTrainload: function () {
        return new Error('抽象方法不能调用');
    }
}

原型模式

用原型实例指向创建对象的类,使用于创建新的对象的类共享原型对象 的属性以及方法。

将可复用的、可共享的、耗时大的从基类中提出来然后放在其原型中,然后子类通过 组合继承或者寄生组合式继承而将方法和属性继承下来,对于子类中那些需要重写的方法进行重写,这 样子类创建的对象既具有子类的属性和方法也共享了基类的原型方法

创建一个焦点图

// 图片轮播类
var LoopImages = function (imgArr, container) {
    this.imagesArray = imgArr; // 轮播图片数组
    this.container = container; // 轮播图片容器
}
LoopImages.prototype = {
    // 创建轮播图片
    createImage: function () {
        console.log('LoopImages createImage function');
    },
    // 切换下—张图片
    changeImage: function () {
        console.log('LoopImages changeImage function');
    }
}
// 上下滑动切换类
var slideLoopImg = function (imgArr, container) {
    // 构造函数继承图片轮播类
    LoopImages.call(this, imgArr, container);
}
slideLoopImg.prototype = new LoopImages();
// 重写继承的切换下—张图片方法
slideLoopImg.prototype.changeImage = function () {
    console.log('slideLoopImg changeImage function');
}
// 渐隐切换类
var FadeLoopImg = function (imgArr, container, arrow) {
    LoopImages.call(this, imgArr, container);
    // 切换箭头私有变量
    this.arrow = arrow;
}
FadeLoopImg.prototype = new LoopImages();
FadeLoopImg.prototype.changeImage = function () {
    console.log('FadeLoopImg changeImage function');
}
// 测试用例
console.log(fadeImg.container); // slide
fadeImg.changeImage(); // FadeLoopImg changeImage function

单体模式

单体模式

提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一变量进行 访问。

优点

  1. 可以用来划分命名空间,减少全局变量的数量。
  2. 使用单体模式可以使代码组织的更为一致,使代码容易阅读和维护.
  3. 可以被实例化,且实例化一次。

单体模式是一个用来划分命名空间并将一批属性和方法组织在一起的对象,如果它可 以被实例化,那么它只能被实例化一次。

// 单体模式
var Singleton = function (name) {
    this.name = name;
    this.instance = null;
};
Singleton.prototype.getName = function () {
    return this.name;
}
// 获取实例对象
function getInstance(name) {
    if (!this.instance) {
        this.instance = new Singleton(name);
    }
    return this.instance;
}
// 测试单体模式的实例
var a = getInstance("aa");
var b = getInstance("bb");
console.log(a === b); // true
console.log(a.getName());// aa
console.log(b.getName());// aa

理解使用代理实现单列模式的好处

具体的业务逻辑分开了,代理只管代理的业务逻辑,在这里代理的作用是实例化对象,并且只实例化一次;

// 单体模式
var CreateDiv = function (html) {
    this.html = html;
    this.init();
}
CreateDiv.prototype.init = function () {
    var div = document.createElement("div");
    div.innerHTML = this.html;
    document.body.appendChild(div);
};
// 代理实现单体模式
var ProxyMode = (function () {
    var instance;
    return function (html) {
        if (!instance) {
            instance = new CreateDiv("我来测试下");
        }
        return instance;
    }
})();
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
console.log(a === b); // true

代理模式

代理是一个对象,它可以用来控制对本体对象的访问,它与本体对象实现了同样的接口,代理对象会把所有的调用方法传递给本体对象的;

使用代理模式 实现单体模式的实例化,其他的事情就交给本体对象去处理;

优点

  1. 代理对象可以代替本体被实例化,并使其可以被远程访问;
  2. 它还可以把本体实例化推迟到真正需要的时候;对于实例化比较费时的本体对象,或者因为尺寸比 较大以至于不用时不适于保存在内存中的本体,我们可以推迟实例化该对象;
// 先申明一个奶茶妹对象
var TeaAndMilkGirl = function (name) {
    this.name = name;
};
// 这是京东ceo先生
var Ceo = function (girl) {
    this.girl = girl;
    // 送结婚礼物 给奶茶妹
    this.sendMarriageRing = function (ring) {
        console.log("Hi " + this.girl.name + ", ceo送你一个礼物:" + ring);
    }
};
// 京东ceo的经纪人是代理,来代替送
var ProxyObj = function (girl) {
    this.girl = girl;
    // 经纪人代理送礼物给奶茶妹
    this.sendGift = function (gift) {
        // 代理模式负责本体对象实例化
        (new Ceo(this.girl)).sendMarriageRing(gift);
    }
};
// 初始化
var proxy = new ProxyObj(new TeaAndMilkGirl("奶茶妹"));
proxy.sendGift("结婚戒"); // Hi 奶茶妹, ceo送你一个礼物:结婚戒 

虚拟代理实现图片的预加载

var myImage = (function () {
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    return {
        setSrc: function (src) {
            imgNode.src = src;
        }
    }
})();
// 代理模式
var ProxyImage = (function () {
    var img = new Image();
    img.onload = function () {
        myImage.setSrc(this.src);
    };
    return {
        setSrc: function (src) {
            myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif");
            img.src = src;
        }
    }
})();
// 调用方式
ProxyImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX130-200.png");

优点

  1. 用户可以放心地请求代理,他们只关心是否能得到想要的结果。假如我门不需要代理对象的话,直 接可以换成本体对象调用该方法即可。
  2. 在任何使用本体对象的地方都可以替换成使用代理。

虚拟代理合并http请求

通过一个代理函数收集一段时间内(比如说2-3秒)的所有 id,一次性发Ajax请求给服务器,相对来说网络请求降低了, 服务器压力减少了

// 本体函数
var mainFunc = function (ids) {
    console.log(ids); // 即可打印被选中的所有的id
    // 再把所有的id一次性发ajax请求给服务器端
};
// 代理函数 通过代理函数获取所有的id 传给本体函数去执行
var proxyFunc = (function () {
    var cache = [], // 保存一段时间内的id
        timer = null; // 定时器
    return function (checkboxs) {
        // 判断如果定时器有的话,不进行覆盖操作
        if (timer) {
            return;
        }
        timer = setTimeout(function () {
            // 在2秒内获取所有被选中的id,通过属性isflag判断是否被选中
            for (var i = 0, ilen = checkboxs.length; i < ilen; i++) {
                if (checkboxs[i].hasAttribute("isflag")) {
                    var id = checkboxs[i].getAttribute("data-id");
                    cache[cache.length] = id;
                }
            }
            mainFunc(cache.join(',')); // 2秒后需要给本体函数传递所有的id
            // 清空定时器
            clearTimeout(timer);
            timer = null;
            cache = [];
        }, 2000);
    }
})();
var checkboxs = document.getElementsByClassName("j-input");
for (var i = 0, ilen = checkboxs.length; i < ilen; i += 1) {
    (function (i) {
        checkboxs[i].onclick = function () {
            if (this.checked) {
                // 给当前增加一个属性
                this.setAttribute("isflag", 1);
            } else {
                this.removeAttribute('isflag');
            }
            // 调用代理函数
            proxyFunc(checkboxs);
        }
    })(i);
}

缓存代理

第一次运行时候进行缓存,当再一次运行相同的时候,直接从缓存里面取, 这样做的好处是避免重复一次运算功能,如果运算非常复杂的话,对性能很耗费,那么使用缓存对象可 以提高性能

// 计算乘法
var mult = function () {
    var a = 1;
    for (var i = 0, ilen = arguments.length; i < ilen; i += 1) {
        a = a * arguments[i];
    }
    return a;
};
// 计算加法
var plus = function () {
    var a = 0;
    for (var i = 0, ilen = arguments.length; i < ilen; i += 1) {
        a += arguments[i];
    }
    return a;
}
// 代理函数
var proxyFunc = function (fn) {
    var cache = {}; // 缓存对象
    return function () {
        var args = Array.prototype.join.call(arguments, ',');
        if (args in cache) {
            return cache[args]; // 使用缓存代理
        }
        return cache[args] = fn.apply(this, arguments);
    }
};
var proxyMult = proxyFunc(mult);
console.log(proxyMult(1, 2, 3, 4)); // 24
console.log(proxyMult(1, 2, 3, 4)); // 缓存取 24
var proxyPlus = proxyFunc(plus);
console.log(proxyPlus(1, 2, 3, 4)); // 10
console.log(proxyPlus(1, 2, 3, 4)); // 缓存取 10

职责链模式

由多个不同的对象组成的,发送者是发送请求的对象,而接收者则是链中那些接收这种请求并 且对其进行处理或传递的对象。请求本身有时候也可以是一个对象,它封装了和操作有关的所有数据,

优点:消除请求的发送者与接收者之间的耦合。

基本实现流程:

  1. 发送者知道链中的第一个接收者,它向这个接收者发送该请求。
  2. 每一个接收者都对请求进行分析,然后要么处理它,要么它往下传递。
  3. 每一个接收者知道其他的对象只有一个,即它在链中的下家(successor)。
  4. 如果没有任何接收者处理请求,那么请求会从链中离开。

比如对象A给对象B发请求,如果B对象不处理,它就会把请求交给C,如果C对象不处理的话,它就会把请求交给D,当然没有任何对象处理该请求的话,那么请求就会从链中离开。

同步责任链:让每个节点函数同步返回一个特定的值”nextSuccessor”,来表示是否把请 求传递给下一个节点

function order500(orderType, isPay, count) {
    if (orderType == 1 && isPay == true) {
        console.log("亲爱的用户,您中奖了100元红包了");
    } else {
        // 自己不处理,传递给下一个对象order200去处理
        order200(orderType, isPay, count);
    }
};

function order200(orderType, isPay, count) {
    if (orderType == 2 && isPay == true) {
        console.log("亲爱的用户,您中奖了20元红包了");
    } else {
        // 自己不处理,传递给下一个对象普通用户去处理
        orderNormal(orderType, isPay, count);
    }
};

function orderNormal(orderType, isPay, count) {
    // 普通用户来处理中奖信息
    if (count > 0) {
        console.log("亲爱的用户,您已抽到10元优惠卷");
    } else {
        console.log("亲爱的用户,请再接再厉哦");
    }
}

该代码违反了面向对象中的 开放-封闭原则

异步责任链:给Chain类再增加一个原型方法Chain.prototype.next,表示手动传递请求给职责链中的一 下个节点

function Fn1() {
    console.log(1);
    return "nextSuccessor";
}

function Fn2() {
    console.log(2);
    var self = this;
    setTimeout(function () {
        self.next();
    }, 1000);
}

function Fn3() {
    console.log(3);
}
// 下面需要编写职责链模式的封装构造函数方法
var Chain = function (fn) {
    this.fn = fn;
    this.successor = null;
};
Chain.prototype.setNextSuccessor = function (successor) {
    return this.successor = successor;
}
// 把请求往下传递
Chain.prototype.passRequest = function () {
    var ret = this.fn.apply(this, arguments);
    if (ret === 'nextSuccessor') {
        return this.successor &&
            this.successor.passRequest.apply(this.successor, arguments);
    }
    return ret;
}
Chain.prototype.next = function () {
    return this.successor &&
        this.successor.passRequest.apply(this.successor, arguments);
}
//现在我们把3个函数分别包装成职责链节点:
var chainFn1 = new Chain(Fn1);
var chainFn2 = new Chain(Fn2);
var chainFn3 = new Chain(Fn3);
// 然后指定节点在职责链中的顺序
chainFn1.setNextSuccessor(chainFn2);
chainFn2.setNextSuccessor(chainFn3);
chainFn1.passRequest(); // 打印出1,2 过1秒后 会打印出3

优点

  1. 解耦了请求发送者和N个接收者之间的复杂关系,不需要知道链中那个节点能处理你的请求,所以 你 只需要把请求传递到第一个节点即可。
  2. 链中的节点对象可以灵活地拆分重组,增加或删除一个节点,或者改变节点的位置都是很简单的事 情。
  3. 我们还可以手动指定节点的起始位置,并不是说非得要从其实节点开始传递的。

缺点

? 职责链模式中**多了一点节点对象,**可能在某一次请求过程中,大部分节点没有起到实质性作用, 他们的作用只是让请求传递下去,从性能方面考虑,避免过长的职责链提高性能。

命令模式

命令指的是一个执行某些特定事情的指令。

使用命令模式消除发送者与接 收者的代码耦合关系。

理解宏命令

宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。 其实类似把页面的所有函数方法放在一个数组里面去,然后遍历这个数组,依次 执行该方法的。

观察者模式和发布订阅模式

观察者模式

观察者模式定义对象间一对多的依赖关系,使得每当一个对象改变状态,则依赖它的对象都会得到通知并自动更新

观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。

例子

Subject:面试公司,Observers:面试者,Observer1,2,3,

Subject用来维护Observers,为某些event(职务空缺)来通知(notify())观察者

image-20210909172522066

发布-订阅模式

订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。

在观察者模式中,Subject就像一个发布者(Publisher),观察者(Observers)完全和订阅者(Subscriber)关联,subject通知观察者就像一个发布者通知他的订阅者,只不过发布者需要通过信息中介这个第三方组件来将发布者和订阅者串联起来,因为发布者和订阅者都不知道对方的存在。

发布订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。

image-20210909174306955

如何实现发布订阅模式?

  1. 建立一个对象
  2. 在该对象上建立一个缓存列表(调度中心)
  3. on方法用来把函数fn都加到缓存列表中(订阅者注册事件到调度中心)
  4. emit方法取到arguments里第一个当作event,根据event值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)
  5. off方法能够根据event值取消订阅(取消订阅)
  6. once方法只监听一次,调用完毕后删除缓存函数(订阅一次)
//建立发布者对象
let eventEmitter={};
// 建立缓存列表
eventEmitter.list={};

//订阅,用on方法把函数fn加到缓存列表中去
eventEmitter.on =function (event,fn) {  
    let _this=this;
    //若对象中没有对应的event值,也就是说明没有订阅过,就给event建立个缓存列表
    //若对象中有相应的event值,把fn添加到对应event的缓存列表里
    (_this.list[event]||(_this.list[event]=[])).push(fn);
    return _this;
}

//发布
eventEmitter.emit=function () {  
    let _this =this;
    //第一个参数是对应的event值,直接用数组的shift方法取出
    let event =[].shift.call(arguments);
        fns=_this.list[event];
    //若是缓存列表里没有fn就会返回false
    if(!fns ||fns.length ===0){
        return false;
    }
    //遍历event值对应的缓存列表,依次执行fn
    fns.forEach(fn=>{
        fn.apply(_this,arguments);
    });
    return _this;
};
function user1(content) {  
    console.log('用户1订阅了:',content);
}
function user2(content) {  
    console.log('用户2订阅了:',content);
}
// 订阅
eventEmitter.on('article', user1);
eventEmitter.on('article', user2);

//发布
eventEmitter.emit('article','JavaScript发布-订阅模式');

观察者模式和发布订阅模式的区别

  1. 观察者模式中,观察者知道Subject,subject一直保持对Observers进行记录,发布订阅模式中,发布者和订阅者不知道对方的存在,只能通过信息中介进行通信
  2. 发布订阅模式中,组件是松散耦合的,正好和观察者模式相反
  3. 观察者模式大多数是同步的,好比事件触发,subject就会去调用观察者的方法,而发布-订阅模式大多数是异步的(使用消息队列)
  4. 观察者必须在单个应用程序地址空间实现,而发布订阅更像交叉应用模式

中介者模式

所有的相关 对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要 通知中介者对象即可

核心

使网状的多对多关系变成了相对简单的一对多关系(复杂的调度处理都交给中介者)

实现

多个对象,指的不一定得是实例化的对象,也可以将其理解成互为独立的多个项。当这些项在处理 时,需要知晓并通过其他项的数据来处理。

如果每个项都直接处理,程序会非常复杂,修改某个地方就得在多个项内部修改我们将这个处理过 程抽离出来,封装成中介者来处理,各项需要处理时,通知中介者即可。

常用算法

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-14 13:14:30  更:2021-09-14 13:16:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 22:40:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码