Base CS from zero
Bundling data and behaviour
In Unit 09 you saw the object: a value made of named fields. A counter object might hold a
single field count. In Unit 08 you saw the function: a named block of behaviour. To
increase the counter, you would write a function increment that takes the object and
adds one to its count field.
Notice that those two things — the count field and the increment function — belong
together. The data is useless without the operation, and the operation is meaningless
without the data. Yet so far they have lived apart: the field inside the object, the
function somewhere else.
This lesson closes that gap. A field of an object does not have to hold a number — it can hold a function. When it does, the function and the data it works on sit inside one named unit. That unit is the basic shape of almost every abstraction you will build, and this lesson traces exactly how a call into it works.
After this lesson you can define a method as a function stored as a field of an object, explain how a method reaches the object’s other fields, trace a method call step by step, and state what the bundle exposes and what it hides.
A method is a function that is stored as a field of an object and operates on the data of that same object.
Recall two things. From Unit 09 lesson 03: an object is a set of named fields, and a field holds a value. From Unit 08: a function is itself a value — it can be stored and called. Put those together: a field can hold a function. When a field holds a function that works on the object’s own other fields, that function is a method.
This gives the object two kinds of field:
- Data fields — hold the object’s state (a number, a string).
- Method fields — hold functions that read or change that state.
A method reaches the object it lives in through the name this. Inside a method,
this refers to the object the method was called on, so this.count means “the count
field of the object I belong to”. Calling a method is written object.methodName() — the
object on the left of the dot becomes this inside the method.
The result is a bundle: one named unit holding data together with the operations on
that data. The bundle’s interface (recall Unit 10 lesson 01) is the set of methods you
can call. The field layout — which data fields exist, what they are named — is the hidden
implementation. A user of the bundle calls counter.increment() and never touches
count directly. This bundling of data with its operations, while hiding the internal
data from outside code, is called encapsulation.
The code below builds a counter object with one data field and two methods, then a caller uses only the methods.
1
const counter = {
2
count: 0, // data field — the state
3
increment() { // method field — an operation
4
this.count = this.count + 1;
5
},
6
value(): number { // method field — another operation
7
return this.count;
8
},
9
};
10
11
counter.increment(); // call a method: counter becomes `this`
12
counter.increment();
13
let n = counter.value(); // n becomes 2
- L2 count is a data field — it holds the object's state, a number
- L3 increment is a method field — its value is a function, not a number
- L4 this refers to counter; this.count reads and writes counter's count field
- L7 value is a method that reads the state and returns it — it changes nothing
- L11 counter.increment(): the object left of the dot becomes this inside the method
- L13 the caller uses the method value() — it never names the field count itself
Step through the three calls at the bottom. Each cell shows the object’s state — the value
of its count field — at that moment.
1
const counter = {
2
count: 0,
3
increment() {
4
this.count = this.count + 1;
5
},
6
value(): number {
7
return this.count;
8
},
9
};
10
11
counter.increment();
12
counter.increment();
13
let n = counter.value();
Why this works
Why bundle them instead of keeping them apart? With the data and its operations in one
unit, there is exactly one place that touches count: the methods. If you decide tomorrow
to rename count, or store it as a string, or split it into two numbers, you change only
the two methods. Every caller still writes counter.increment() and counter.value() —
unchanged, because the interface held. That is the Unit 10 lesson 01 idea — fixed
interface, replaceable implementation — applied to a data structure instead of a single
function.
Common mistake
A common mistake is reading counter.increment (no parentheses) as a method call. It is
not. counter.increment is the method field — it evaluates to the function value itself.
Only counter.increment(), with parentheses, actually calls the function and runs its
body. The same distinction you saw for plain functions in Unit 08 applies here: naming a
function is not calling it.
The counter starts at count = 0. After counter.increment() runs 4 times, what value does counter.value() return?
A method is a function stored as a field of an object. The counter object has fields count, increment, and value. How many of its 3 fields are method fields?
Inside increment, this.count means the count field of the object the method was called on. If counter.increment() is called, this.count refers to the count field of which object? Type 1 if it is the counter object, 0 if it is some other object.
The bundle exposes its methods and hides its data field layout. A caller wants to add 1 to the counter. How many data fields must the caller name directly to do this?
counter.value() is called when count holds 7. value() returns this.count and changes nothing. After the call, what value does count hold?
What is a method, and what does bundling data with methods give you?
A method is a function stored as a field of an object that operates on that object’s
own data. An object can therefore hold two kinds of field: data fields that carry its
state, and method fields that hold the operations on that state. Inside a method,
this names the object the method was called on, so this.count reaches that object’s
own count field; the call object.method() makes the object on the left of the dot
become this. Storing data together with its operations in one named unit is a bundle.
The bundle’s interface is the set of methods you can call; the field layout is the hidden
implementation. A caller uses counter.increment() and counter.value() and never names
the field count itself — so the layout can change while callers stay fixed. Bundling
data with its operations while hiding the internal data is called encapsulation, and
it is the basic shape of nearly every abstraction larger than a single function.