Learn by reading through in order

Instance Members and static — Values and Methods That Belong to the Class

Learn how static puts values and methods on the class itself: counters with static fields, factory methods that build instances from JSON, shared constants, and when to use instance members instead.

To number parcels 1, 2, 3 in the order you create them, you need to keep track of how many you've created so far. The recipient differs from parcel to parcel, but that running count and the weight limit only need to exist once, shared by every parcel.

This article covers static, which puts values and methods on the class itself instead of on its instances.

Counting Parcels — static Fields

Suppose you want to number parcels in the order they're created. If you write the class field count = 0;, increase it with this.count += 1 in the constructor, and use it as the number, every parcel gets the number 1, because the field starts over from 0 on every new.

A static field (a property declared inside the class with the static keyword; only one copy exists, on the class itself) is read and written through the class name, as in Parcel.count.

In contrast, properties that each instance holds, like this.number, and methods written without static are called instance members.

class Parcel {
  // The number of parcels created so far, stored once on the class
  static count = 0;

  constructor(recipient) {
    Parcel.count += 1;               // Increase the class's count by one
    this.number = Parcel.count;      // Use the count at this moment as the parcel number
    this.recipient = recipient;
  }
}

// Create two parcels
const first = new Parcel("Alice");
const second = new Parcel("Bob");

console.log(first.number, second.number);   // 1 2
console.log(Parcel.count);                  // 2
console.log(first.count);                   // undefined
Where count and Parcel Numbers Live
Parcel (the class itself)
  • static count — 1 after the first new, 2 after the second
  • Read and write it as Parcel.count
Instances created with new
first
  • number — 1 (the count at creation)
  • recipient — "Alice"
  • No countfirst.count is undefined
second
  • number — 2 (the count at creation)
  • recipient — "Bob"
There's only one count, in the class's box, and none in the instances' boxes. Reading it from an instance gives undefined.

Each call to new increases the same count in the class's box by one and copies its current value into the new instance's own number. Even after count goes up to 2, first.number stays at 1, the value it got when it was created.

Number survey answers in the order they arrive. answerTexts is already declared.

① Define SurveyAnswer, which takes the answer text. Each time an answer is created, increase an answer count shared by all answers by one, and use it as that answer's number.

② Turn the three texts into SurveyAnswer instances and put them in answers.

③ Display every answer in the form "A-001 answer text".

④ Display "Answers: N" and "First answer's number: N".

(If it runs correctly, an explanation will appear.)

JavaScript / TypeScript Editor

Run code to see output

Creating Parcels from JSON — static Methods

When you load a saved parcel, you get a JSON string containing the recipient and weight. If every screen that loads parcels has its own JSON.parse and new Parcel(...), a change to the JSON key names means fixing every one of those screens.

But when you create a parcel from JSON, there's no instance yet to call a method on. A static method (a method defined with static that you call through the class name without creating an instance) can be called as Parcel.fromJSON(text). A static method like fromJSON that creates and returns an instance is called a factory method.

class Parcel {
  constructor(recipient, weightKg) {
    this.recipient = recipient;
    this.weightKg = weightKg;
  }

  label() {
    return `${this.recipient} (${this.weightKg} kg)`;
  }

  // Take a JSON string, then create and return a Parcel instance
  static fromJSON(text) {
    const data = JSON.parse(text);
    return new Parcel(data.recipient, data.weightKg);
  }
}

const text = '{"recipient":"Alice","weightKg":2}';
const parcel = Parcel.fromJSON(text);        // Called through the class name
console.log(parcel.label());                 // Alice (2 kg)
From a JSON String to a Parcel
Parcel.fromJSON(text) is calledParse withJSON.parse(text)Create and returnnew Parcel(…)text is justa stringHas propertiesbut no label()An instance thatcan call label()
JSON.parse gives you a plain object with properties but no methods. Only after going through new does it become an instance that can call label().

The last line of fromJSON is new Parcel(...), so the constructor body runs as usual. If you put throw checks in the constructor, parcels loaded back from JSON go through the same checks as parcels entered on screen.

You Can't Call fromJSON on an Instance

Calling it on an instance, as in parcel.fromJSON(text), throws TypeError: parcel.fromJSON is not a function. Unlike label(), a static method can only be called through the class name. How this works is covered in the article on prototypes.

Restore saved cart items from JSON strings. CartItem and savedTexts are already declared.

① Add a method fromJSON that creates and returns a CartItem instance from a JSON string.

② Use ① to turn the two entries in savedTexts into CartItem instances.

③ Display "Name x quantity = subtotal yen" for each item.

④ Display the sum of the subtotals in the form "Total: N yen".

JavaScript / TypeScript Editor

Run code to see output

A Fixed Limit for Every Parcel — static Constants

The 30 kg weight limit is used both in the input form's notice, shown before any parcel exists, and in calculating each parcel's remaining capacity. If you put the limit in an instance field, you'd have to create a parcel with no recipient and no weight just to show the notice on the form.

A static constant (a static field holding a value that never changes while the program runs) is named in all caps, with words separated by underscores, as in static MAX_WEIGHT_KG = 30;. Whether you're outside the class or inside a method, you read it through the class name: Parcel.MAX_WEIGHT_KG.

class Parcel {
  // The limit shared by all parcels is stored once on the class with static
  static MAX_WEIGHT_KG = 30;

  constructor(weightKg) {
    this.weightKg = weightKg;
  }

  // How many more kg fit before the limit
  remainingKg() {
    return Parcel.MAX_WEIGHT_KG - this.weightKg;
    // Writing this.MAX_WEIGHT_KG - this.weightKg returns NaN
  }
}

// Readable even before any instance exists
console.log(`Up to ${Parcel.MAX_WEIGHT_KG} kg per parcel`);   // Up to 30 kg per parcel
const parcel = new Parcel(12);
console.log(parcel.remainingKg());                            // 18
How You Read the Limit Decides the Result
static MAX_WEIGHT_KG= 30Form notice:Parcel.MAX_WEIGHT_KGIn remainingKg:Parcel.MAX_WEIGHT_KGIn remainingKg:this.MAX_WEIGHT_KGReads 30 withoutan instanceCalculates30 - 12Not on instance,so undefinedShows "Up to30 kg per parcel"Returns 18undefined - 12returns NaN
The left and middle columns read the same 30, with or without an instance. Only the right column, which reads from this, ends up with NaN.

The right column ends up with NaN because this inside a method refers to the instance, and static constants don't live on the instance. undefined - 12 doesn't throw an error, so you only notice when the displayed remaining weight looks wrong.

An All-Caps Name Can Still Be Reassigned

An all-caps name is only a convention that means "don't change this"; if you assign Parcel.MAX_WEIGHT_KG = 50;, the value changes. If you define only a getter, as in static get MAX_WEIGHT_KG() { return 30; }, assigning to it throws a TypeError (getters are covered in the next article).

Check usernames against length rules. registeredNames is already declared.

① Give Account a minimum name length of 3 and a maximum name length of 12, shared by all accounts.

② Add isValidName, which returns whether the name's length is within range.

③ Turn every name into an Account, and display "name: valid" or "name: invalid".

④ Suppose some other part of the code has accidentally set the maximum length to 20. Add that assignment, then display the results again in the same form as ③.

JavaScript / TypeScript Editor

Run code to see output

Deciding Where a Value Lives — static vs. Instance

Once you know about static, it may seem convenient to put a value like the recipient on the class so you can read it from one place, Parcel.recipient. In a quick test that creates only one parcel, the same recipient is displayed whether it's static or on this, so you won't notice the difference.

Decide where a value lives by whether it differs for each item. A value put on this exists separately in each instance created by new, while a static value exists only once on the class, however many parcels you create.

// Recipient kept on static
class SharedParcel {
  static recipient = "";
  constructor(recipient) { SharedParcel.recipient = recipient; }
  label() { return `To: ${SharedParcel.recipient}`; }
}

// Recipient kept on the instance
class Parcel {
  constructor(recipient) { this.recipient = recipient; }
  label() { return `To: ${this.recipient}`; }
}

const sharedFirst = new SharedParcel("Alice");
new SharedParcel("Bob");
console.log(sharedFirst.label());   // To: Bob

const first = new Parcel("Alice");
new Parcel("Bob");
console.log(first.label());         // To: Alice
Does the Second new Change the First Parcel's Recipient?
Recipient goes instatic recipient2nd new assignsBobThe one recipientis overwrittensharedFirst.label()is To: BobRecipient goes inthis.recipient2nd new is aseparate instance1st recipientstays Alicefirst.label()is To: Alice
In the top row, the second parcel's recipient overwrites the single value on the class. Put values that differ for each item on this, so each instance holds its own.

sharedFirst has no recipient of its own, and label() reads SharedParcel.recipient as it is at the time of the call, so it shows "Bob", the last value assigned. The table below summarizes the parcel members from this article: how to write each kind and how to use it.

What it holdsHow to write and use itExamples here
A value per itemPut on this, read from the instancenumber / recipient / weightKg
Method reading per-item valuesNo static, call on the instancelabel() / remainingKg()
One value for all itemsWith static, read via the class namecount / MAX_WEIGHT_KG
Logic needing no instanceWith static, call via the class namefromJSON(text)
QUIZ

Knowledge Check

Answer each question one by one.

Q1Parcel has static count = 0;. What do you get when you read first.count on an instance first?

Q2What happens if you call a static method on the instance parcel, as in parcel.fromJSON(text)?

Q3A class keeps the recipient in a static field. If you create a parcel for "Alice" and then one for "Bob", what recipient does the first parcel's label() show?