Одним из широко используемых паттернов в JavaScript является паттерн Observer. Каждое событие, которое вы используете в своем браузере (щелчок, нажатие клавиши, наведение, пользовательские события и т. д.), является примером шаблона наблюдателя.

Этот шаблон также называется шаблоном публикации-подписки.

Этот шаблон хорошо определен GOF в их книге «Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения» как

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

Проще говоря, считайте, что вы являетесь ПОЛЬЗОВАТЕЛЕМ Netflix и подписываетесь на службу потоковой передачи Netflix, оплачивая ежемесячную арендную плату. Когда вы подписываетесь, Netflix помещает вас в список пользователей, которые подписались на его услуги. Всякий раз, когда выходит новый сериал или фильм, он уведомляет всех подписчиков из списка по электронной почте или SMS. Если вы перестанете платить ежемесячную арендную плату, он автоматически удалит вас из списка, т.е. отпишет вас и больше не уведомит вас.

Здесь Netflix — Субъект, а пользователи — Наблюдатели. Всякий раз, когда у Субъекта появляются новые вещи, он уведомляет об этом Наблюдателей.

Если вы знаете Angular, вы, должно быть, использовали двустороннюю привязку с использованием синтаксиса [()], также называемого Banana Box. Это действительно реализация шаблона наблюдателя.

Другими наблюдаемыми фреймворками/библиотеками являются Knockout, dojo, JQuery, RxJs, React.

Давайте создадим рабочий пример, используя ванильный JavaScript.

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

//A simple HTML doc for modeling observer pattern.
//we have two input fields here which ask user to enter 
//first name and last name.
//when user enter the data, the corresponding observer section
//gets updated(notified)
//file name  - main.html
<!doctype html>
<html>
  <head>
    <title>Observable</title>
  </head>
  <body>
    <div class="firstName">
      <label for ="fName">FirstName</label>
      <input type="text" 
             name = "firstName" 
             id="fName"
             placeholder ="Enter first name">
    </div>
    <div class="lastName">
      <label for="lName">Last Name</label>
      <input type="text" 
             name="lastName" 
             id="lName"
             placeholder="Enter last name">
    </div>
    <div>
      <button type="submit">Submit</button>
    </div>
    <hr>
<!-- the observer section  -->
    <div class="observer-section">
      <h4> you entered below data</h4>
      <p class="data1"> </p>
      <p class="data2"> </p>
    </div>
  </body>
</html>

теперь давайте создадим простой наблюдаемый класс, имеющий в своем прототипе три метода: subscribe(), unsubscribe() и notify().

//the observable function.
//observer.js
function Observable(){
  this.observers = []
}
Observable.prototype.subscribe = function(f){
  this.observers.push(f);
}
Observable.prototype.unsubscribe = function(f){
  this.observers = this.observers.filter(
     subscriber => subscriber !== f
    );
}
Observable.prototype.notify = function(data){
  this.observers.forEach(observer => observer(data));
}

Теперь у нас есть HTML-форма и функция-конструктор Observable. давайте интегрируем их, чтобы увидеть магию шаблона наблюдателя.

//file name  - main.js
const fName = $(".firstName input"),
      lName = $(".lastName input"),
      fNameData = $(".data1"),
      lNameData = $(".data2"),
      updateFname = inputVal => fNameData.text(inputVal),
      updateLname = inputVal => lNameData.text(inputVal),
      fNameObserver = new Observable(),
      lNameObserver = new Observable();
fNameObserver.subscribe(updateFname);
lNameObserver.subscribe(updateLname);
fName.on('keyup', e=>fNameObserver.notify(e.target.value));
lName.on('keyup', e=>lNameObserver.notify(e.target.value));
// *note - I have used JQuery for DOM manupulation.

вы можете попробовать этот код здесь, на codepen или на jsfiddle, чтобы увидеть рабочий пример.

Справочник

книга: шаблоны JavaScript от Стояна Стефанова

вики: шаблон наблюдателя