Classes

Making objects better

Review

Objects let us collect multiple properties into a single value.

const ball = {
  x: 10,
  y: 20,
  size: 15,
  color: 'blue'
};

We can pass a whole object to a function

const drawBall = (b, g) => {
  g.drawFilledCircle(b.x, b.y, b.size, b.color);
};

This function assumes the object has a certain structure.

A different kind of object

const paddle = {
  x: 10,
  y: 20,
  width: 10,
  height: 20,
  color: 'blue'
};

This is not just a different object but a different kind of object.

Balls and paddles have different structure, i.e. properties.

A function that works with paddle objects

Obviously we draw a paddle differently than we draw a ball so we need a different function.

const drawPaddle = (g, p) => {
  g.drawFilledRect(p.x, p.y, p.width, p.height, p.color);
}

Remember this function because we’ll come back to it near the end of this deck.

We can classify objects

Imagine you had a bunch of already existing objects:

{ x: 10, y: 20 }

{ x: 30, y: 40 }

{ name: "Fred", age: 14 }

{ name: "Sally", age: 15 }

you could classify them into groups based on what properties they have.

Classification

Points:

{ x: 10, y: 20 }

{ x: 30, y: 40 }

People:

{ name: "Fred", age: 14 }

{ name: "Sally", age: 15 }

Classes provide structure

If we make multiple objects with the same structure, i.e. the same properties, we can use them with the same functions.

Classes give us an easy way to define that structure.

A Ball class

class Ball {

  constructor(x, y, size, color) {
    this.x = x;
    this.y = y;
    this.size = size ?? 15;
    this.color = color ?? 'blue';
  }
}

Note: the ?? operator is kind of like the boolean || operator except it returns either the first value, if it is defined, or the second value.

Making Ball objects

const ball = new Ball(10, 20);
const ball2 = new Ball(30, 40, 25, 'purple');

new Ball() creates a ball object and then runs the constructor from the Ball class to initialize that object.

Within the constructor the variable this refers to the newly created object.

The constructor

constructor(x, y, size, color) {
  this.x = x;
  this.y = y;
  this.size = size ?? 15;
  this.color = color ?? 'blue';
}

The name constructor is required.

Kind of like a function. Takes arguments (x, y, size, and color in this case.)

The constructor’s job is to put the object into a usable state.

Methods

If you recall from when we first looked at strings, objects don’t just have properties.

They also have methods.

String methods

s.slice(1, 3)

s.toLowerCase()

s.toUpperCase()

Recall that methods have access to the value of the object to the the left of the dot, the value of the variable s in this case.

Methods in classes

class Ball {

  // constructor as before

  draw(g) {
    g.drawFilledCircle(
      this.x, this.y, this.size, this.color
    );
  }
}

As in a constructor, this is a special variable whose value is the object on which the method was invoked.

Function vs method

A function takes the ball as an explicit argument:

const drawBall = (b, g) => {
  g.drawFilledCircle(b.x, b.y, b.size, b.color);
};

A method takes the ball as an implicit argument refered to as this:

draw(g) {
  g.drawFilledCircle(this.x, this.y, this.size, this.color);
}

Method powermove: polymorphism

Polymorphism is from Greek “poly” meaning “many” and “morph” meaning “form”, so “many forms”.

When talking about methods it means when we have a single named method that takes on multiple forms.

Remember this function?

const drawPaddle = (g, p) => {
  g.drawFilledRect(p.x, p.y, p.width, p.height, p.color);
}

Let's make it a method

class Paddle {
  // constructor, etc.
  draw(g) {
    g.drawFilledRect(
      this.x, this.y, this.width, this.height, this.color
    );
  }
}

Like the Ball class, this class also has a method named draw.

This one draws a paddle given a graphics object.

Full ball class

class Ball {
  constructor(x, y, size, color) {
    this.x = x;
    this.y = y;
    this.size = size ?? 15;
    this.color = color ?? 'blue';
  }
  draw(g) {
    g.drawFilledCircle(
      this.x, this.y, this.size, this.color
    );
  }
}

Full paddle class

class Paddle {
  constructor(x, y, width, height, color) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.color = color ?? 'blue';
  }
  draw(g) {
    g.drawFilledRect(
      this.x, this.y, this.width, this.height, this.color
    );
  }
}

Polymorphic methods

Polymorphic methods let us treat different kinds of objects as the same in some sense.

Balls and paddles have different internal structure and look different on the screen.

But they are similar in that they both can be drawn.

Drawing with functions and no polymorphism

for (let i = 0; i < balls.length; i++) {
  drawBall(balls[i], g);
}

for (let i = 0; i < paddles.length; i++) {
  drawPaddle(paddles[i], g);
}

Drawing with methods and polymorphism

for (let i = 0; i < things.length; i++) {
  things[i].draw(g);
}

When we call draw on an object, which version of draw is used is automatically determined by the class of the object.

If thing[i] is a ball, it calls the Ball version of draw and if it’s a paddle it calls the Paddle version.