Knockout.js Pure Computed Observables — Efficient Dependency Management
In this tutorial, you will learn about Knockout.js Pure Computed Observables. We cover key concepts, practical examples, and best practices to help you master this topic.
Knockout.js pureComputed is an optimized computed observable that automatically tracks whether it has active observers, releasing or acquiring dependencies as needed for efficient memory management.
What You'll Learn
- Creating pureComputed observables
- How pureComputed differs from regular computed
- When to use pureComputed vs computed
- Pure computed lifecycle and disposal
- Performance characteristics and best practices
Why It Matters
In large applications with many ViewModels, regular computed observables hold subscriptions to their dependencies even when nothing is reading their value. Pure computeds release these subscriptions when unobserved, reducing memory overhead and dependency tracking cost.
Real-World Use
A chat application with hundreds of message ViewModels, each having a pureComputed for formatted timestamp, mentioned users, and link detection. Most messages are off-screen at any time, so pure computeds release their dependencies until the message is visible again.
Pure vs Regular Computed
flowchart TD
A[Computed Type] --> B{Has Observers?}
B -->|Yes| C[Track Dependencies]
B -->|No - Regular| D[Keep Subscriptions Active]
B -->|No - Pure| E[Release All Subscriptions]
C --> F[On Dependency Change: Re-evaluate]
D --> F
E --> G[On New Observer: Re-acquire Dependencies]
G --> C
style C fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px
style E fill:#ff6b6b,stroke:#c0392b,stroke-width:2px
Creating a Pure Computed
var firstName = ko.observable('John');
var lastName = ko.observable('Doe');
// Pure computed
var fullName = ko.pureComputed(function() {
console.log('Re-evaluating fullName');
return firstName() + ' ' + lastName();
});
// Nothing logged yet - pure computed is not evaluated until observed
console.log(fullName());
// Console: Re-evaluating fullName
// Output: John Doe
// No console log - value is cached
console.log(fullName());
// Output: John Doe
firstName('Jane');
// No console log - pure computed is lazy and has observers, so it marks
// itself dirty but does not re-evaluate until next read
console.log(fullName());
// Console: Re-evaluating fullName
// Output: Jane Doe
Expected output: The pure computed does not evaluate until its value is requested. It caches the result and only re-evaluates when a dependency changes and the value is read again.
Observing a Pure Computed
var firstName = ko.observable('John');
var lastName = ko.observable('Doe');
var fullName = ko.pureComputed(function() {
console.log('Evaluating fullName');
return firstName() + ' ' + lastName();
});
// Now we subscribe (become an observer)
var subscription = fullName.subscribe(function(name) {
console.log('fullName changed to: ' + name);
});
// Subscription triggers evaluation
firstName('Bob');
// Console: Evaluating fullName
// Console: fullName changed to: Bob Doe
// Dispose the subscription - no more observers
subscription.dispose();
// Now pure computed releases its dependencies
// This means it no longer tracks firstName/lastName changes
firstName('Charlie');
// No console output (no evaluation, no notification)
Expected output: While the subscription exists, the pure computed tracks dependencies and notifies on change. After disposal, it releases all dependencies and stops tracking.
When to Use Pure Computed
Use pureComputed when:
- The computed is used in a foreach binding (many instances, most off-screen)
- The computed is expensive to evaluate
- The computed is temporary (conditional UI sections)
- Memory usage is a concern in large applications
Use regular computed when:
- The evaluator has side effects (should not, but sometimes needed)
- The computed must always stay up-to-date regardless of observers
- You need to access the computed's dependencies list
Pure Computed in Components
// Product item component - many instances
function ProductItem(product) {
var self = this;
self.name = ko.observable(product.name);
self.price = ko.observable(product.price);
self.quantity = ko.observable(product.quantity);
// Pure computed - releases when product is scrolled off-screen
self.total = ko.pureComputed(function() {
return self.price() * self.quantity();
});
// Pure computed for formatted display
self.formattedTotal = ko.pureComputed(function() {
return '$' + self.total().toFixed(2);
});
}
// In a list of 1000 products, only visible items' computeds stay active
Pure Computed with Read/Write
var firstName = ko.observable('John');
var lastName = ko.observable('Doe');
var fullName = ko.pureComputed({
read: function() {
return firstName() + ' ' + lastName();
},
write: function(value) {
var parts = value.split(' ');
firstName(parts[0]);
lastName(parts.slice(1).join(' '));
}
});
// Write works the same as regular computed
fullName('Jane Smith');
console.log(firstName()); // Output: Jane
console.log(lastName()); // Output: Smith
Evaluation Delay and Lazy Evaluation
var a = ko.observable(1);
var b = ko.observable(2);
var sum = ko.pureComputed(function() {
console.log('Computing sum');
return a() + b();
});
// Change a dependency while no one is observing
a(10);
// The pure computed did not re-evaluate
// because no one was watching
// Now someone reads it
console.log(sum()); // Output: Computing sum \n 12
// The pure computed evaluated lazily on demand
Pure Computed vs Computed: Key Differences
| Feature | Regular Computed | Pure Computed |
|---|---|---|
| Dependency tracking | Always active | Active only when observed |
| Memory usage | Higher (always subscribed) | Lower (releases when unobserved) |
| Evaluation | Eager (evaluates on creation) | Lazy (evaluates on first read) |
| Side effects | Allowed (not recommended) | Should not have side effects |
isActive() |
Always true | False when unobserved |
Converting Between Types
var a = ko.observable(1);
// Regular computed - always active
var regular = ko.computed(function() { return a() * 2; });
// Convert to pure by wrapping
var pure = ko.pureComputed(function() { return a() * 2; });
// You cannot convert a regular computed to pure after creation
// Choose the right type at creation time
Common Mistakes
Using pureComputed with side effects - Pure computeds may be evaluated lazily or not at all. Side effects in the evaluator may execute unpredictably.
Expecting pure computed to evaluate eagerly - Pure computeds do not evaluate until someone reads them or subscribes. Code that relies on immediate evaluation will break.
Not using pureComputed in foreach - A regular computed inside a foreach creates subscriptions for every item, even hidden ones. Pure computeds release subscriptions for off-screen items.
Assuming pure computed is always more efficient - The overhead of tracking observers and releasing/acquiring subscriptions may outweigh benefits for always-visible computeds.
Calling dispose on a pure computed unnecessarily - Pure computeds manage their own lifecycle. Explicit
dispose()is only needed when you want to permanently remove the computed.
Practice Questions
- What triggers a pure computed to start tracking its dependencies?
- What happens to a pure computed's subscriptions when all its observers are removed?
- When would you choose a regular computed over a pure computed?
- Does a pure computed evaluate immediately upon creation?
- Why should pure computed evaluators avoid side effects?
Challenge: Create a product list with 100 items, each having pure computeds for formatted price, discounted price, and stock status. Use the browser's performance tools to compare memory usage between pure computeds and regular computeds. Experiment with scrolling to see how pure computeds release and acquire dependencies.
FAQ
Mini Project
Build a search results page with 500 items, each with pure computeds for matching score, highlighted title, and formatted metadata. Implement pagination and observe how pure computeds release subscriptions for items not on the current page.
What's Next
Control when observables notify subscribers with deferred updates and rate limiting for optimal performance in data-intensive applications.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro