предисловие
В предыдущих двух контрольных списках для самопроверки я поделился с вами базовыми знаниями JavaScript, вы можете просмотреть их вместе~
Эта статья - я в нашей команде""Современный JavaScript-коммандос"«Часть общего контента, второй учебный контент»"Шаблоны проектирования"«Серия знаний, я буду отвечать за свою часть статей, организованных в виде вывода, и каждый надеется узнать что-то новое!
""Современный JavaScript-коммандос""суммировать:
- «Контрольный список для самопроверки интерфейса JavaScript для начинающих и среднего уровня — 1»
- «Контрольный список для самопроверки интерфейса JavaScript для начинающих и среднего уровня — 2»
1. Знакомство с режимом
1. Введение
Такого рода требования часто встречаются в программных комплексах: при изменении состояния объекта некоторые связанные с ним объекты также вносят соответствующие изменения. Это для создания"зависимости между объектами", когда объект изменится, он будет"Автоматически уведомлять другие объекты", другие объекты будут"реагировать соответственно".
Мы называем измененный объект как"цель наблюдения", вызовите уведомленный объект как"наблюдатель","Цель наблюдения может соответствовать нескольким наблюдателям", и между этими наблюдателями нет взаимной связи. Впоследствии наблюдатели могут быть добавлены и удалены по мере необходимости, что упрощает расширение системы. Это фон шаблона наблюдателя.
2. Введение концепции
Шаблон наблюдателя: определение пути между объектами"зависимости «один ко многим»", так что всякий раз, когда состояние объекта изменяется, его зависимые объекты получают уведомление и автоматически обновляются. Паттерн Наблюдатель — это поведенческий паттерн объекта.
3. Жизненные сценарии
Во всех событиях браузера (наведение курсора мыши, нажатие клавиши и т. д.) есть примеры шаблона наблюдателя.
а также:
Если мы подпишемся на общедоступный номер микроканала «курсы самообучения переднего плана» ("цель наблюдения"("наблюдатель") получит эту статью (событие), содержание этой статьи настраивается издателем (настраиваемое событие), и поклонники будут выполнять определенные действия после прочтения (например: лайк, добавление в избранное, подписка и т. д.).
2. Особенности режима
1. Композиция узора
В шаблон наблюдателя обычно включаются следующие роли:
- "Цель: Субъект"
- "Объект наблюдения: ConcreteSubject"
- "Наблюдатель: Наблюдатель"
- "Специфический наблюдатель: бетонобер"
2. Диаграмма классов UML
Источник изображения:«Шаблон наблюдателя шаблонов проектирования TypeScript»
3. Преимущества
- Шаблон наблюдателя может быть реализован"Разделение уровня представления и уровня логики данных",а также"Уменьшите связь между целью наблюдения и наблюдателем";
- Поддержка шаблона наблюдателя"простая широковещательная связь","автоматическое уведомление"все подписанные объекты;
- Шаблон наблюдателя"Соответствовать требованиям «принципа открыт-закрыт».";
- Абстрактное отношение связи между целью наблюдения и наблюдателем может"Расширение и повторное использование по отдельности".
4. Недостатки
- в качестве объекта наблюдения"иметь несколько прямых или косвенных наблюдателей", процесс оповещения всех наблюдателей займет много времени;
- Когда существует связь между объектом наблюдения и наблюдателем"круговая зависимость", наблюдайте, как цели запускают циклические вызовы между ними, возможно"вызвать сбой системы".
- В шаблоне наблюдателя отсутствует соответствующий механизм, позволяющий наблюдателю узнать, как изменился наблюдаемый целевой объект, но знать только, что наблюдаемая цель изменилась.
3. Сценарии использования
Шаблон Observer можно использовать в следующих ситуациях:
- В абстрактной модели поведение объекта"зависит от"Состояние другого объекта. немедленно"цель"Когда состояние изменится, это напрямую повлияет на"наблюдатель"поведение;
- Объекту необходимо уведомить другие объекты о необходимости реагировать, но он не знает, кто эти объекты.
- В системе необходимо создать триггерную цепочку, поведение объекта A повлияет на объект B, поведение объекта B повлияет на объект C..., вы можете использовать шаблон наблюдателя для создания механизма триггера цепочки.
4. Практические примеры
1. Простой пример
- определение"Наблюдайте за целевым интерфейсом"(Тема) и"Интерфейс наблюдателя"(наблюдатель)
// ObserverPattern.ts
// 观察目标接口
interface Subject {
addObserver: (observer: Observer) => void;
deleteObserver: (observer: Observer) => void;
notifyObservers: () => void;
}
// 观察者接口
interface Observer {
notify: () => void;
}
- определение"Конкретный целевой класс наблюдения"(Бетон Тема)
// ObserverPattern.ts
// 具体观察目标类
class ConcreteSubject implements Subject{
private observers: Observer[] = [];
// 添加观察者
public addObserver(observer: Observer): void {
console.log(observer, " is pushed~~");
this.observers.push(observer);
}
// 移除观察者
public deleteObserver(observer: Observer): void {
console.log(observer, " have deleted~~");
const idx: number = this.observers.indexOf(observer);
~idx && this.observers.splice(idx, 1);
}
// 通知观察者
public notifyObservers(): void {
console.log("notify all the observers ", this.observers);
this.observers.forEach(observer => {
// 调用 notify 方法时可以携带指定参数
observer.notify();
});
}
}
- определение"Конкретный класс наблюдателей"(Конкретный Наблюдатель)
// ObserverPattern.ts
// 具体观
class ConcreteObserver implements Observer{
constructor(private name: string) {}
notify(): void {
// 可以处理其他逻辑
console.log(`${this.name} has been notified.`);
}
}
- тестовый код
// ObserverPattern.ts
function useObserver(): void {
const subject: Subject = new ConcreteSubject();
const Leo = new ConcreteObserver("Leo");
const Robin = new ConcreteObserver("Robin");
const Pual = new ConcreteObserver("Pual");
const Lisa = new ConcreteObserver("Lisa");
subject.addObserver(Leo);
subject.addObserver(Robin);
subject.addObserver(Pual);
subject.addObserver(Lisa);
subject.notifyObservers();
subject.deleteObserver(Pual);
subject.deleteObserver(Lisa);
subject.notifyObservers();
}
useObserver();
Полный демонстрационный код выглядит следующим образом:
// ObserverPattern.ts
interface Subject {
addObserver: (observer: Observer) => void;
deleteObserver: (observer: Observer) => void;
notifyObservers: () => void;
}
interface Observer {
notify: () => void;
}
class ConcreteSubject implements Subject{
private observers: Observer[] = [];
public addObserver(observer: Observer): void {
console.log(observer, " is pushed~~");
this.observers.push(observer);
}
public deleteObserver(observer: Observer): void {
console.log(observer, " have deleted~~");
const idx: number = this.observers.indexOf(observer);
~idx && this.observers.splice(idx, 1);
}
public notifyObservers(): void {
console.log("notify all the observers ", this.observers);
this.observers.forEach(observer => {
// 调用 notify 方法时可以携带指定参数
observer.notify();
});
}
}
class ConcreteObserver implements Observer{
constructor(private name: string) {}
notify(): void {
// 可以处理其他逻辑
console.log(`${this.name} has been notified.`);
}
}
function useObserver(): void {
const subject: Subject = new ConcreteSubject();
const Leo = new ConcreteObserver("Leo");
const Robin = new ConcreteObserver("Robin");
const Pual = new ConcreteObserver("Pual");
const Lisa = new ConcreteObserver("Lisa");
subject.addObserver(Leo);
subject.addObserver(Robin);
subject.addObserver(Pual);
subject.addObserver(Lisa);
subject.notifyObservers();
subject.deleteObserver(Pual);
subject.deleteObserver(Lisa);
subject.notifyObservers();
}
useObserver();
2. Принцип реализации двусторонней привязки данных Vue.js
В Vue.js, когда мы изменяем состояние данных, соответственно обновляется и представление.Это двусторонняя привязка данных Vue.js (также известная как реактивный принцип), которая является одной из самых уникальных функций Vue. js. Если вам непонятна двусторонняя привязка данных Vue.js, рекомендуется прочитать официальный документ "Углубленные принципы реагирования"глава.
2.1 Принцип введения
На официальном сайте представлена такая блок-схема, которая представляет весь процесс адаптивной системы Vue.js:
Изображение с официального сайта Vue.js "Углубленные принципы реагирования》
В Vue.js каждый экземпляр компонента соответствует экземпляру наблюдателя, который записывает свойства данных «касание» («касание») как зависимости (процесс «Собрать как зависимость») во время процесса рендеринга компонента. Позже, когда сработает установщик зависимости, наблюдатель будет уведомлен (процесс Notify), так что связанный с ним компонент будет повторно визуализирован (процесс Trigger re-render) — это типичный шаблон наблюдателя.
Этот вопрос интервью проверяет понимание интервьюируемым основных принципов Vue.js, способность реализовать шаблон наблюдателя и ряд важных знаний JS, которые являются всеобъемлющими и репрезентативными.
2.2 Компоненты
В логике реализации двусторонней привязки данных Vue.js есть три ключевые роли:
- наблюдатель (слушатель): Наблюдатель здесь не только подписчик ("Необходимо отслеживать изменения данных"), а также является издателем ("Переслать отслеживаемые данные").
- наблюдатель (подписчик): объект-наблюдатель является реальным подписчиком, и **наблюдатель пересылает данные объекту-наблюдателю. После того, как наблюдатель получает новые данные, он выполняет обновление представления.
- компиляция (компилятор): конкретная роль платформы MVVM, отвечающая за сканирование и анализ каждого элемента узла инструкции, процесс инициализации данных, например инструкции подписчика для создания операций.
Процесс сотрудничества этих трех показан на рисунке:Изображение из: Буклет Nuggets «Основные принципы и практика применения шаблонов проектирования JavaScript»
2.3 Внедрение основного наблюдателя кода
Во-первых, нам нужно реализовать метод, который проходит через объект данных, который необходимо отслеживать, и добавляет к его свойствам настраиваемые атрибуты.getter
а такжеsetter
функция. Таким образом, всякий раз, когда свойство этого объекта изменяется, оно вызываетsetter
функция оповещения подписчиков. этоsetter
функция, которая является нашим слушателем:
// observe方法遍历并包装对象属性
function observe(target) {
// 若target是一个对象,则遍历它
if(target && typeof target === 'object') {
Object.keys(target).forEach((key)=> {
// defineReactive方法会给目标属性装上“监听器”
defineReactive(target, key, target[key])
})
}
}
// 定义defineReactive方法
function defineReactive(target, key, val) {
// 属性值也可能是object类型,这种情况下需要调用observe进行递归遍历
observe(val)
// 为当前属性安装监听器
Object.defineProperty(target, key, {
// 可枚举
enumerable: true,
// 不可配置
configurable: false,
get: function () {
return val;
},
// 监听器函数
set: function (value) {
console.log(`${target}属性的${key}属性从${val}值变成了了${value}`)
val = value
}
});
}
Следующее реализует подписчикаDep
:
// 定义订阅者类Dep
class Dep {
constructor() {
// 初始化订阅队列
this.subs = []
}
// 增加订阅者
addSub(sub) {
this.subs.push(sub)
}
// 通知订阅者(是不是所有的代码都似曾相识?)
notify() {
this.subs.forEach((sub)=>{
sub.update()
})
}
}
Теперь мы можем переписатьdefineReactive
серединаsetter
метод, в прослушивателе, чтобы уведомить подписчика:
function defineReactive(target, key, val) {
const dep = new Dep()
// 监听当前属性
observe(val)
Object.defineProperty(target, key, {
set: (value) => {
// 通知所有订阅者
dep.notify()
}
})
}
V. Резюме
Шаблон наблюдателя также известен как шаблон публикации-подписки, шаблон представления модели, шаблон источника-слушателя или шаблон подчиненного устройства. это"Шаблоны поведения объектов". который определяет"Зависимости «один ко многим» между объектами", при изменении состояния цели наблюдения все объекты-наблюдатели будут уведомлены, чтобы их можно было обновить автоматически.
В реальном бизнесе, если поведение объекта"зависит от"Состояние другого объекта. или когда"цель"Когда состояние изменится, это напрямую повлияет на"наблюдатель"Поведение, постарайтесь принять во внимание использование шаблона наблюдателя для достижения.
6. Расширение
观察者模式和发布-订阅模式两者很像,但其实区别比较大。 Например:
- Разница в степени связи: степень связи в режиме наблюдателя выше, чем в режиме публикации-подписки;
- Проблемы разные: шаблон наблюдателя должен знать о существовании друг друга, в то время как шаблон публикации-подписки должен связываться с издателем/подписчиком через диспетчерский центр.
Увидимся в следующей статье.
Справочная статья
1.«3. Режим наблюдателя» 2.«Шаблон наблюдателя шаблонов проектирования TypeScript» 3.«Основные принципы и практика применения шаблонов проектирования JavaScript»