Режим публикации-подписки шаблона проектирования Javascript

внешний интерфейс Шаблоны проектирования JavaScript

Введение

Шаблон публикации-подписки, также известный как шаблон наблюдателя, определяет отношение зависимости «один ко многим», то есть при изменении состояния объекта все зависимые объекты будут уведомлены.

запомнить однажды

Как интерфейсный разработчик, события привязки к узлам DOM не могут быть более частыми. Например следующий код

    document.body.addEventListener('click',function () {
        alert(2333);
    },false);
    document.body.click();//模拟点击事件

Здесь мы подписываемся на событие клика document.body.При нажатии на тело он публикует сообщение подписчику и выскакивает 2333.Также мы можем добавлять и удалять подписчиков по желанию.При публикации сообщения все подписчики получат сообщение.

    document.body.addEventListener('click',function () {
        alert(11111);
    },false);
    document.body.addEventListener('click',function () {
        alert(222);
    },false);
    document.body.addEventListener('click',function () {
        alert(333);
    },false);
    document.body.click();//模拟点击事件

Стоит отметить, что для запуска события вручную мы напрямую используем document.body.click(), но лучше использовать fireEvent в IE и dispatchEvent в стандартных браузерах, как показано ниже:

    let fireEvent = function (element,event) {
        if (document.createEventObject) {
            var evt = document.createEventObject();
            return element.fireEvent('on'+event,evt);
        }else{
            var evt = document.createEvent('HTMLEvents');
            evt.initEvent(event,true,true);
            return element.dispatchEvent(evt);
        }
    }
    document.addEventListener('shout',function (event) {
        alert('shout');
    })
    fireEvent(document,'shout');

говорить о сейчас

Повседневная жизнь людей неотделима от разного рода межличностных переговоров.Например, у вас много друзей.В это время вы собираетесь жениться.Вы должны быть издателем,открыть адресную книгу и позвонить каждому абоненту по одному один, чтобы уведомить каждого подписчика, что вы выходите замуж информация. В абстракции реализация шаблона публикации-подписки требует:

  1. Издатель (вы)
  2. Кэшированный список (адресная книга, ваши друзья эквивалентны подписке на все ваши сообщения)
  3. При публикации сообщения пройтись по списку кеша и поочередно вызвать callback-функции хранящихся в нем подписчиков (вызов по одному)
  4. Кроме того, в callback-функцию можно добавить много параметров, и подписчики могут получать эти параметры, например, вы будете сообщать им время свадьбы, место и т. д. После получения сообщения подписчики могут выполнять свою обработку.
let yourMsg = {};
yourMsg.peopleList = [];
yourMsg.listen = function (fn) {
    this.peopleList.push(fn);
}
yourMsg.triger = function () {
    for(var i = 0,fn;fn=this.peopleList[i++];){
        fn.apply(this,arguments);
    }
}

yourMsg.listen(function (name) {
    console.log(`${name}收到了你的消息`);
})
yourMsg.listen(function (name) {
    console.log('哈哈');
})

yourMsg.triger('张三');
yourMsg.triger('李四');

  • Вышеупомянутое является простой реализацией публикации-подписки, но мы обнаружим, что подписчик будет получать каждое сообщение, опубликованное издателем.Если Ли Си темно и не хочет слышать о вашем браке, просто хочет услышать ваши плохие новости, Например, если бы вас уволили, он был бы счастлив. В это время нам нужно добавить ключ, чтобы подписчики могли подписываться только на интересующие их сообщения.
let yourMsg = {};
yourMsg.peopleList ={};
yourMsg.listen = function (key,fn) {
    if (!this.peopleList[key]) { //如果没有订阅过此类消息,创建一个缓存列表
        this.peopleList[key] = [];
    }
    this.peopleList[key].push(fn);
}
yourMsg.triger = function () {
    let key = Array.prototype.shift.call(arguments);
    let fns = this.peopleList[key];
    if (!fns || fns.length == 0) {//没有订阅 则返回
        return false;
    }
    for(var i=0,fn;fn=fns[i++];){
        fn.apply(this,arguments);
    }
}

yourMsg.listen('marrgie',function (name) {
    console.log(`${name}想知道你结婚`);
})
yourMsg.listen('unemployment',function (name) {
    console.log(`${name}想知道你失业`);
})

yourMsg.triger('marrgie','张三');
yourMsg.triger('unemployment','李四');

  • Публиковать сообщения нужно.Так же у всех людей есть круг друзей и тоже нужно публиковать сообщения.Поэтому нам необходимо извлечь функцию публикации-подписки и вынести ее в отдельный объект.Кому нужно динамически устанавливать и функция публикации-подписки (функция installEvent реализует функцию публикации-подписки динамической установки).
var event = {
    peopleList:[],
    listen:function (key,fn) {
        if (!this.peopleList[key]) { //如果没有订阅过此类消息,创建一个缓存列表
        this.peopleList[key] = [];
        }
        this.peopleList[key].push(fn)
    },
    trigger:function () {
         let key = Array.prototype.shift.call(arguments);
        let fns = this.peopleList[key];
        if (!fns || fns.length == 0) {//没有订阅 则返回
            return false;
        }
        for(var i=0,fn;fn=fns[i++];){
            fn.apply(this,arguments);
        }
    }
}

var installEvent  = function (obj) {
    for(var i in event){
        obj[i] = event[i];
    }
}

let yourMsg = {};
installEvent(yourMsg);
yourMsg.listen('marrgie',function (name) {
    console.log(`${name}想知道你结婚`);
})
yourMsg.listen('unemployment',function (name) {
    console.log(`${name}想知道你失业`);
})

yourMsg.trigger('marrgie','张三');
yourMsg.trigger('unemployment','李四');
  • Есть события, от которых нужно время от времени отписываться.Например Ли Си ваш хороший друг, но из-за одного вы расстались.Вы удалили его из своей адресной книги.Вот мы добавляем событие к событию , удалить метод;
remove:function (key,fn) {
      var fns = this.clientList[key];
      if(!fns){
          return false;
      }  
      if(!fn){
          fns && (fns.length=0)
      }else{
          for (let index = 0; index < fns.length; index++) {
              const _fn = fns[index];
              if(_fn === fn){
                  fns.splice(index,1);
              }
          }
      }
    }

Обсуждение заказа на публикацию-подписку

Обычно мы видим подписку, а затем публикацию, но нужно ли соблюдать этот порядок? Ответ не обязательно. Если издатель публикует сообщение первым, но в это время на него не подписаны подписчики, мы можем предотвратить исчезновение сообщения во вселенной. Как и автономные сообщения QQ, автономные сообщения хранятся на сервере, и получатель получит это сообщение только после входа в систему в следующий раз. Точно так же мы можем построить стек, который хранит офлайн-события.Когда событие публикуется, если в это время нет подписчика, чтобы подписаться на событие, мы временно оборачиваем действие публикации события в функцию, и эти функции-оболочки будут храниться в стеке, когда есть объект для подписки на событие, мы будем проходить по стеку и выполнять эти функции-обертки по очереди, то есть повторно отправлять событие внутрь, но жизненный цикл офлайн-события только один раз , точно так же, как непрочитанное сообщение QQ предложит вам только один раз Same.

Удобство JavaScript-реализации модели публикации-подписки

Поскольку JavaScript имеет преимущество функций обратного вызова, нам проще писать подписки на разработку. Традиционная публикация-подписка, такая как Java, обычно передает самого подписчика в объект издателя в качестве ссылки, и объект подписчика также должен предоставлять метод, называемый обновлением, чтобы объект издателя вызывал его при необходимости. Следующий код использует js для имитации традиционной реализации.

function Dep() {
    this.subs = [];
}
Dep.prototype.addSub = function (sub) {
    this.subs.push(sub);
}
Dep.prototype.notify = function () {
    this.subs.forEach(sub=>sub.update());
}
function Watcher(fn) {
    this.fn = fn;
}
Watcher.prototype.update = function () {
     this.fn();
}

var dep = new Dep();
dep.addSub(new Watcher(function () {
    console.log('okokok');
}))
dep.notify();

резюме

  • Преимущества публикации-подписки очевидны. Он обеспечивает развязку во времени и развязку между объектами. С архитектурной точки зрения MVC и MVVM незаменимы для участия публикации-подписки. Наш часто используемый Vue также основан на публикации-подписке. , Недавно я найду время, чтобы записать исходный код реализации Vue, EventEmitter в том же узле также опубликован и подписан, и его реализация была написана вручную раньше.
  • Публикация-подписка имеет и минусы, создание подписчика отнимает определенное количество времени и памяти, а при подписке на сообщение сообщение может и не произойти в итоге, но подписчик всегда будет существовать в памяти. Если программа использует много публикаций-подписок, это также затруднит отслеживание ошибок в программе.