Knockout.js Computed Observables — Derived Values and Auto-Reevaluation
In this tutorial, you will learn about Knockout.js Computed Observables. We cover key concepts, practical examples, and best practices to help you master this topic.
Knockout.js computed observables are read-only functions that automatically derive their value from other observables, Caching results and re-evaluating only when dependencies change.
What You'll Learn
- Creating computed observables with
ko.computed() - How dependency tracking determines reevaluation
- Read-write computeds for two-way derivation
- Pure computed observables for performance
- Using dispose and managing computed lifecycles
Why It Matters
In any application, many values are derived from other values — full names from first+last name, totals from line items, filtered lists from a master list. Computed observables declare these relationships explicitly and keep derived values consistent automatically.
Real-World Use
An invoice calculation where subtotal is computed from line items, tax is computed from subtotal, and total is computed from subtotal+tax. Changing a line item quantity cascades through all dependent values instantly.
Dependency Tracking Flow
flowchart TD
A[firstName observable] --> C[fullName computed]
B[lastName observable] --> C
C --> D{fullName()}
A -->|Change| C
C -->|Re-evaluate| D
D --> E[Update UI]
style C fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px
Creating a Basic Computed Observable
function PersonViewModel() {
var self = this;
self.firstName = ko.observable('John');
self.lastName = ko.observable('Doe');
// Read-only computed
self.fullName = ko.computed(function() {
return self.firstName() + ' ' + self.lastName();
});
}
var vm = new PersonViewModel();
console.log(vm.fullName()); // Output: John Doe
vm.firstName('Jane');
console.log(vm.fullName()); // Output: Jane Doe
Expected output: The computed fullName automatically re-evaluates when firstName or lastName changes. Calling fullName() returns the current derived value without re-evaluating if no dependencies changed.
How Dependency Tracking Works
When a computed's evaluator function runs, Knockout records every observable that is read (by calling it as a function). These become the computed's dependencies. When any dependency changes, the computed marks itself as dirty and re-evaluates on the next read.
var a = ko.observable(1);
var b = ko.observable(2);
var sum = ko.computed(function() {
console.log('Re-evaluating sum');
return a() + b();
});
console.log(sum()); // Console: Re-evaluating sum \n 3
console.log(sum()); // No console output (cached), returns 3
a(5); // Console: Re-evaluating sum
console.log(sum()); // Returns 7 (re-evaluated)
Expected output: The evaluator runs lazily — only when the computed's value is needed. Repeated reads without dependency changes return the cached value.
Read-Write Computed Observables
A computed can be writeable by providing both a read and a write function:
function ViewModel() {
var self = this;
self.firstName = ko.observable('John');
self.lastName = ko.observable('Doe');
self.fullName = ko.computed({
read: function() {
return self.firstName() + ' ' + self.lastName();
},
write: function(value) {
var parts = value.split(' ');
self.firstName(parts[0]);
self.lastName(parts.slice(1).join(' '));
}
});
}
var vm = new ViewModel();
vm.fullName('Jane Smith');
console.log(vm.firstName()); // Output: Jane
console.log(vm.lastName()); // Output: Smith
Expected output: Writing to fullName parses the string and updates the underlying observables, which in turn update any UI bound to firstName and lastName.
Pure Computed Observables
ko.pureComputed is an optimized version that releases subscriptions when nothing is observing it:
var firstName = ko.observable('John');
var lastName = ko.observable('Doe');
// Pure computed — releases dependencies when not being observed
var fullName = ko.pureComputed(function() {
return firstName() + ' ' + lastName();
});
// When a UI binding or another computed depends on fullName,
// it starts tracking. When all observers are gone, it stops.
Expected output: Pure computeds consume less memory in long-lived applications because they do not hold subscriptions when no one is listening. Use pure computeds by default and switch to regular computeds only when you need side effects in the evaluator.
Chaining Computed Observables
Computeds can depend on other computeds:
var price = ko.observable(100);
var quantity = ko.observable(2);
var taxRate = ko.observable(0.08);
var subtotal = ko.pureComputed(function() {
return price() * quantity();
});
var tax = ko.pureComputed(function() {
return subtotal() * taxRate();
});
var total = ko.pureComputed(function() {
return subtotal() + tax();
});
price(150);
// Cascading updates: subtotal -> tax -> total
console.log(total()); // Output: 324 (300 + 24)
Expected output: Changing price triggers subtotal to re-evaluate, which triggers tax, which triggers total. The final value is always consistent.
Managing Computed Lifecycle
Computeds hold references to their dependencies. Dispose them when no longer needed:
var disposable = ko.computed(function() {
// ...
});
// Later, when the computed is no longer needed
disposable.dispose();
// Check if disposed
console.log(disposable.isDisposed()); // Output: true
Common Mistakes
Side effects in computed evaluators - Computed should not modify other observables. Side effects make dependency tracking unpredictable and violate the principle that computeds derive, not mutate.
Creating computeds inside templates - Defining computeds in binding expressions re-creates them on every evaluation. Always define computeds in the ViewModel.
Forgetting to capture
this- Inside the evaluator function,thismay not be the ViewModel. Usevar self = thisor an arrow function.Circular dependencies - Computed A depends on Computed B which depends on Computed A. Knockout detects this and throws an error. Restructure to avoid cycles.
Not disposing long-lived computeds - In large applications, undisposed computeds accumulate and leak memory. Dispose them when the associated component is removed.
Practice Questions
- What triggers a computed observable to re-evaluate?
- What is the difference between
ko.computedandko.pureComputed? - How do you create a writeable computed observable?
- What happens when you read a computed that has not changed since the last read?
- Why should you avoid side effects inside a computed's evaluator function?
Challenge: Build a shopping cart with computed properties for item count, subtotal, tax (8%), shipping (free over $50, otherwise $5.99), and total. Each computed should properly chain dependencies.
FAQ
Mini Project
Build a loan calculator with observables for loan amount, interest rate, and loan term. Create computeds for monthly payment, total interest, and total payment. Display all values and update them in real-time as the user adjusts sliders.
What's Next
Data often comes in lists. Learn how observable arrays track list mutations and enable powerful UI patterns like master-detail and drag-and-drop reordering.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro