cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Class basics

Learn JavaScript class syntax, constructors, prototype methods, getters, setters, public fields, function-like class values, and the async factory pattern.

By the end, you can
  • 01
    Build instancesWrite a class with fields, a constructor, and methods.
  • 02
    Trace where behavior livesTell own fields from shared prototype methods.
  • 03
    Use modern patternsAdd accessors and a static async factory without making constructors async.

Classes in one picture

A JavaScript class is a compact way to describe objects that share the same shape and behavior. If constructor functions taught you how new links an object to F.prototype, class syntax is the same idea in a cleaner uniform.

Think of a class as a blueprint. Each time you use new, JavaScript builds one house from the blueprint. The constructor is the move-in checklist run once for that house. Prototype methods live in a shared blueprint library, so they are not copied into every house. Public fields are furniture each house receives for itself.

Real-life analogyBlueprints and houses

The blueprint tells builders where rooms go. It does not mean every house has a private copy of the blueprint book. Likewise, a class tells JavaScript how to create instances while shared methods stay on the prototype.

In real life: Blueprint
In JavaScript: The class Counter { ... } declaration
In real life: A new house
In JavaScript: One object made by new Counter()
In real life: Move-in checklist
In JavaScript: The constructor body
In real life: Shared blueprint library
In JavaScript: Counter.prototype methods
In real life: Furniture in each house
In JavaScript: Public fields and this.value data

Where the analogy stops: A real blueprint is paper, but a JavaScript class is a real runtime value: typeof Counter is function, and you can pass it around like other functions.

This lesson covers class syntax, constructors, prototype methods, class accessors, public fields, classes as function values, and the static async factory pattern. Later lessons go deeper into inheritance, static blocks, private fields, and advanced object-oriented design.

Class syntax and the constructor

STEP THROUGH

A class declaration starts with class, a name, and a body. Inside the body, constructor is the special method that runs when you write new ClassName(...). It receives the arguments from new, and this is the fresh instance being built.

Base-class public fields run before the constructor body. The experiment proves it: history exists before the constructor pushes its second note.

Build one Counter, step by step
Step 0 of 9Ready
Your turn: follow the blue line

Classes build objects in a predictable order. Step through a new Counter and watch fields, constructor code, and prototype lookup happen.

Running in
  1. script
Next: line 2
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
class Counter {   constructor(start = 0) {    this.value = start;    this.history.push("constructor ran");  }   increment() {    this.value += 1;    return this.value;  }} const counter = new Counter(5);const result = counter.increment();console.log(counter.history.join(" → "));console.log(result);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Change the constructor argument on line 14

Changing the argument starts a fresh replay. Watch field initialization still happen before the constructor body.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Class syntax is sugar, not a different object model

Under the hood, new Counter(5) still creates an object, links it to Counter.prototype, runs setup with this, and returns the object. The syntax prevents common constructor-function mistakes and keeps methods non-enumerable by default.

Methods live on the prototype

INTERACTIVE

When you write a method such as increment() in a class body, JavaScript puts that function on Counter.prototype. Instances do not receive their own copy. This is exactly the sharing model from F.prototype & constructors, with friendlier syntax.

The next playground compares a class and a constructor function side by side. Both create objects that greet the same way; the differences are the safer rules classes add.

Class vs constructor function
Side by sidePop out in the code editor (opens in a new tab)JavaScript
class UserClass {  constructor(name) {    this.name = name;  }  greet() {    return "Hi " + this.name;  }} function UserFunction(name) {  this.name = name;}UserFunction.prototype.greet = function () {  return "Hi " + this.name;}; const classUser = new UserClass("Ada");const functionUser = new UserFunction("Ada");
Observed factClass instance behavior
Hi Ada

This result comes from real class and constructor values created by the lesson code.

Try it yourself

Class instance behavior → Hi Ada

Classes and constructor functions can create equivalent instances, but classes add stricter, safer default rules.
Equivalent shapePop out in the code editor (opens in a new tab)JavaScript
class UserClass {  constructor(name) {    this.name = name;  }  greet() {    return "Hi " + this.name;  }} function UserFunction(name) {  this.name = name;}UserFunction.prototype.greet = function () {  return "Hi " + this.name;}; const classUser = new UserClass("Ada");const functionUser = new UserFunction("Ada");
Class syntax vs constructor function
QuestionClassConstructor function
Creationnew UserClass("Ada")new UserFunction("Ada")
Shared methodsMethod syntax creates non-enumerable prototype methodsAssignment creates enumerable methods unless you define descriptors
Calling without newThrows TypeErrorMay run like an ordinary function in sloppy scripts
StrictnessClass bodies are always strictOrdinary functions depend on where/how they are written
HoistingClass declarations have a temporal dead zoneFunction declarations are callable before their line
Instance or prototype?
  • clicks = 0
  • this.value = start
  • increment() { ... }
  • get fahrenheit() { ... }
  • static async create()
  • typeof Counter
Try it yourself
0 of 6 correct

Sort each class feature by where it lives.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Getters, setters, and public fields

INTERACTIVE

A getter is a method you read like a property. A setter is a method you assign like a property. They are useful when the object should store one source of truth but expose a friendlier view.

Read a getter, write a setter
Temperature classPop out in the code editor (opens in a new tab)JavaScript
class Temperature {  constructor(celsius) {    this.celsius = celsius;  }   get fahrenheit() {    return this.celsius * 9 / 5 + 32;  }   set fahrenheit(value) {    this.celsius = (value - 32) * 5 / 9;  }} const tea = new Temperature(100);console.log(tea.fahrenheit);tea.fahrenheit = 32;console.log(tea.celsius);
Live instanceTemperature
tea.celsius100
tea.fahrenheit212.0
Try it yourself

Getter reads 100°C as 212.0°F. The setter converts Fahrenheit back into celsius and stores only celsius.

The object stores one source of truth (celsius) and computes the other view when asked.

Public fields are different: they create own properties on each instance. A field can hold data, an array, or even an arrow function. That arrow function field is handy when you need a callback that keeps this, but it is not shared through the prototype.

Fields or prototype?
Public fields vs methodsPop out in the code editor (opens in a new tab)JavaScript
class ToolbarButton {  clicks = 0;  handleClick = () => {    this.clicks += 1;    return this.clicks;  };   describe() {    return "Button clicked " + this.clicks + " time(s)";  }} const save = new ToolbarButton();const cancel = new ToolbarButton();console.log(save.describe === cancel.describe);console.log(save.handleClick === cancel.handleClick);
Comparisonsreal booleans
save.describe === cancel.describetrue
save.handleClick === cancel.handleClickfalse
cancel.clicks after save clicked0
Try it yourself
computed on load

A method declaration is shared through the prototype; the arrow function field is created separately for each instance.

Use arrow fields carefully: handy for callbacks, but they cost one function per instance.
Real-life analogyFurniture in each house

Fields are like furniture delivered to each house. Prototype methods are like books in a library all houses can use. If you put the whole library in every house, it works, but it wastes space.

In real life: Every house gets its own sofa
In JavaScript: Every instance gets its own public fields
In real life: The town library is shared
In JavaScript: Prototype methods are shared
In real life: Buying a sofa for every house costs more
In JavaScript: Arrow function fields allocate a function per instance

Where the analogy stops: Furniture is visible; object properties can be non-enumerable, inherited, or hidden behind accessors. Use the analogy only for ownership, not visibility.

Classes are functions, and async setup uses factories

INTERACTIVE

A class declaration creates a value. In fact, typeof Counter is "function". The value has a prototype, can be exported, and can have static methods. But it cannot be called like Counter(); classes must be constructed with new.

Constructors should do synchronous setup. If creating an object needs to wait for data, use a static async factory such as Profile.create(). The factory can await a simulated loading step, then call the normal constructor and return a ready instance.

Static async factory
Factory methodPop out in the code editor (opens in a new tab)JavaScript
class Profile {  constructor(name, avatar) {    this.name = name;    this.avatar = avatar;  }   static async create(name) {    const avatar = await loadAvatar(name);    return new Profile(name, avatar);  }} async function loadAvatar(name) {  return name.slice(0, 1).toUpperCase() + "-avatar";} const profile = await Profile.create("Ada");console.log(profile.avatar);
Factory resulton click

Not run yet

Try it yourself

Not run yet

The wait is simulated with a timer so you can see the pattern now; the Promises and async/await lessons explain the mechanics later.
Point ahead: async/await

The factory pattern uses async and await, which the asynchronous JavaScript stage covers in depth. For now, remember the shape: static async create() waits, then returns new ClassName(...).

Where you’ll use this

Classes are useful when many objects share behavior and each object carries its own state: UI widgets, model objects, data validators, game entities, or service clients. A class makes the shared API easy to find: constructor for required data, methods for actions, getters for computed views, and static factories for named creation paths.

A practical shape

A ShoppingCart might store its own items field, share add() and total() on the prototype, expose get count(), and offer static async fromSavedCart(userId) when it needs to load saved data first.

Classes are not mandatory. Plain objects and factory functions are still great. Choose a class when the new story helps readers understand that many instances share one set of behaviors.

Common misconceptions

“Classes replace prototypes.”

No. Class syntax uses prototypes. Methods and accessors still live on ClassName.prototype.

“The constructor creates methods for each object.”

The constructor initializes instance data. Method declarations are shared.

“Fields and methods are the same.”

Fields are own properties created per instance. Methods are prototype properties unless you intentionally store a function in a field.

“Getters store data.”

A getter runs code when read. It may compute from stored data and does not need to store its own value.

“Constructors can be async.”

A constructor returns the new object immediately. Use a static async factory when setup must wait.

“Classes are hoisted like function declarations.”

Class declarations are in the temporal dead zone until the declaration runs.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upWrite a class

Write the class shown in the starter and predict the two console lines.

Starter codePop out in the code editor (opens in a new tab)JavaScript
class TodoItem {
  constructor(text) {
    this.text = text;
    this.done = false;
  }
  complete() {
    this.done = true;
    return this.text + " done";
  }
}
const task = new TodoItem("Ship lesson");
console.log(task.complete());
console.log(task.done);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticePredict method sharing

    Predict both booleans without running the program.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    class Counter {
      count = 0;
      increment() {
        this.count += 1;
        return this.count;
      }
    }
    const a = new Counter();
    const b = new Counter();
    console.log(a.increment === b.increment);
    console.log(a.count === b.count);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFix calling a class without new

      This code throws. Fix the creation line so it prints the widget name.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      class Widget {
        constructor(name) {
          this.name = name;
        }
      }
      const widget = Widget("Panel");
      console.log(widget.name);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeAdd a getter

        Add a getter that converts Celsius to Fahrenheit.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        class Temperature {
          constructor(celsius) {
            this.celsius = celsius;
          }
          get fahrenheit() {
            return this.celsius * 9 / 5 + 32;
          }
        }
        const room = new Temperature(20);
        console.log(room.fahrenheit);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeUse a static async factory

          Use a static async factory to create a ready profile, then print its avatar.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          class Profile {
            constructor(name, avatar) {
              this.name = name;
              this.avatar = avatar;
            }
            static async create(name) {
              const avatar = name.slice(0, 1).toUpperCase() + "-avatar";
              return new Profile(name, avatar);
            }
          }
          Profile.create("Ada").then(profile => console.log(profile.avatar));

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS
            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What is a JavaScript class?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this method-sharing code print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              class Counter {
                value = 0;
                increment() {
                  this.value += 1;
                  return this.value;
                }
              }
              const a = new Counter();
              const b = new Counter();
              console.log(a.increment === b.increment);

              Choose an answer to see the explanation.

            3. Question 3 of 7Which runs before the constructor body in a base class?

              Choose an answer to see the explanation.

            4. Question 4 of 7What does this getter code print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              class Temperature {
                constructor(celsius) { this.celsius = celsius; }
                get fahrenheit() { return this.celsius * 9 / 5 + 32; }
              }
              console.log(new Temperature(0).fahrenheit);

              Choose an answer to see the explanation.

            5. Question 5 of 7Which statement about classes is true?

              Choose an answer to see the explanation.

            6. Question 6 of 7Why use static async create()?

              Choose an answer to see the explanation.

            7. Question 7 of 7What does this arrow-field comparison print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              class Button {
                click = () => "clicked";
                describe() { return "button"; }
              }
              const one = new Button();
              const two = new Button();
              console.log(one.click === two.click);

              Choose an answer to see the explanation.

            Key takeaways

            • A class is syntax for creating objects with shared prototype behavior.
            • The constructor runs once per new instance; public fields initialize before a base constructor body.
            • Methods and accessors live on the prototype; fields live on each instance.
            • Classes are function values, but they must be called with new and live in the TDZ before declaration.
            • Use static async create() when setup must wait before returning a ready instance.

            Remember the one-liner.
            A class is a blueprint whose constructor sets up each instance while prototype methods are shared by all instances.

            Up next: Inheritance with extends & super.

            CompleteFrontend Clear concepts. Working examples.