Skip to content

Knockout.js Computed Observables — Derived Values and Auto-Reevaluation

DodaTech Updated 2026-06-28 5 min read

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

  1. 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.

  2. Creating computeds inside templates - Defining computeds in binding expressions re-creates them on every evaluation. Always define computeds in the ViewModel.

  3. Forgetting to capture this - Inside the evaluator function, this may not be the ViewModel. Use var self = this or an arrow function.

  4. Circular dependencies - Computed A depends on Computed B which depends on Computed A. Knockout detects this and throws an error. Restructure to avoid cycles.

  5. Not disposing long-lived computeds - In large applications, undisposed computeds accumulate and leak memory. Dispose them when the associated component is removed.

Practice Questions

  1. What triggers a computed observable to re-evaluate?
  2. What is the difference between ko.computed and ko.pureComputed?
  3. How do you create a writeable computed observable?
  4. What happens when you read a computed that has not changed since the last read?
  5. 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

Can a computed observable depend on observables from multiple ViewModels?

Yes, a computed can close over any observables regardless of where they are defined, as long as they are in scope when the computed is created.

How does Knockout detect which observables a computed depends on?

Knockout uses a global tracking context. When the evaluator function runs, any observable that is read (called as a function) registers itself as a dependency.

What happens if a dependency changes while the computed is being read?

Knockout handles this safely. The computed re-evaluates in a controlled manner and any intermediate state is not visible to consumers.

Can a computed observable depend on asynchronous values?

Computeds are synchronous. For async dependencies, use a regular observable and update it manually when the async value resolves.

How many computeds can depend on a single observable?

There is no hard limit. However, each computed adds one subscriber. A single observable with thousands of dependent computeds may cause performance issues on writes.

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