Knockout.js строится на принципах реактивного программирования, где изменения данных автоматически отражаются в интерфейсе. Основой этой реактивности являются observable и computed observable. Они реализуют паттерн Observer, позволяя объектам подписываться на изменения и получать уведомления при их наступлении.
ko.observable — это функция, создающая реактивное
свойство. Она хранит значение и обеспечивает механизм уведомления
подписчиков:
var firstName = ko.observable('Иван');
console.log(firstName()); // Получение значения
firstName('Пётр'); // Установка нового значения
Ключевые моменты:
subscribe:firstName.subscribe(function(newValue) {
console.log('Имя изменилось на: ' + newValue);
});
ko.computed позволяет создавать вычисляемые
свойства, которые зависят от других observables:
var lastName = ko.observable('Смирнов');
var fullName = ko.computed(function() {
return firstName() + ' ' + lastName();
});
console.log(fullName()); // Иван Смирнов
Особенности:
Knockout.js реализует Publisher-Subscriber (Pub/Sub) паттерн, где observable выступает в роли Publisher, а подписчики (subscribe-функции) — Subscribers.
Механизм работы:
Пример с динамическим обновлением интерфейса:
var cartItems = ko.observableArray([]);
var totalPrice = ko.computed(function() {
return cartItems().reduce(function(sum, item) {
return sum + item.price;
}, 0);
});
cartItems.subscribe(function(changes) {
console.log('Корзина обновлена:', changes);
});
Здесь cartItems — Publisher, а totalPrice и
функция в subscribe — подписчики, реагирующие на изменения
списка.
ko.observableArray расширяет возможности обычных
observables, предоставляя методы работы с массивами:
push, pop, remove,
removeAll, replace, sort,
reversecartItems.push({ name: 'Товар 1', price: 100 });
cartItems.push({ name: 'Товар 2', price: 200 });
console.log(totalPrice()); // 300
Это демонстрирует, как Publisher-Subscriber паттерн упрощает реактивное обновление интерфейса при работе с коллекциями данных.
Knockout позволяет подписываться не только на все изменения, но и
фильтровать их с помощью параметров event и
deferEvaluation:
firstName.subscribe(function(newValue) {
console.log('Изменилось только имя');
}, null, 'change'); // подписка только на события изменения
В современном коде Knockout observables часто используются внутри компонентов:
function ProductViewModel(name, price) {
this.name = ko.observable(name);
this.price = ko.observable(price);
}
function CartViewModel() {
this.items = ko.observableArray([]);
this.total = ko.computed(() => {
return this.items().reduce((sum, item) => sum + item.price(), 0);
});
}
var cart = new CartViewModel();
cart.items.push(new ProductViewModel('Товар A', 150));
Таким образом, любая часть интерфейса автоматически реагирует на изменения данных без необходимости вручную перерисовывать DOM.
Knockout поддерживает chain subscriptions и dependent observables, позволяя строить сложные реакции:
var a = ko.observable(1);
var b = ko.observable(2);
var sum = ko.computed(() => a() + b());
sum.subscribe((value) => {
console.log('Сумма изменилась:', value);
});
a(5); // вызовет пересчет sum и уведомление подписчиков
Такая архитектура упрощает построение приложений с динамическими данными и поддерживает чистое разделение между данными и представлением.
Knockout.js сочетает в себе простоту observables с мощью Publisher-Subscriber паттерна, делая интерфейсы реактивными, а код — более структурированным и поддерживаемым.