DEV Community

Discussion on: 3 Ways to Create Objects in Object-Oriented JavaScript

Collapse
 
koresar profile image
Vasyl Boroviak • Edited

4. Using mixins for prototypes and for regular properties. More info here: stampit.js.org/

const stampit = require("stampit");

const Point = stampit({
  props: { // default property values
    x: 0,
    y: 0
  },
  init({ x, y }) { // kinda constructor
    if (x != null) this.x = x;
    if (y != null) this.y = y;
  },
  methods: { // that goes to the prototype
    distance(point) {
      return Math.sqrt(Math.abs(this.x - point.x)**2 + Math.abs(this.y - point.y)**2);
    }
  }
});

const point = Point({ x: 12, y: 42 });
point.distance({ x: 13, y: 42 }) === 1.0;

// kinda inheritance, but not really, see https://stampit.js.org/
const Circle = stampit(Point, {
  props: {
    radius: 1
  },
  init({ radius }) {
    if (radius != null) this.radius = radius;
  },
  methods: { // that goes to the prototype
    distance(point) {
      return Point(point).distance(this) - this.radius;
    }
  }
});

// Two different initializers will be executed here.
const circle = Circle({ x: 12, y: 42, radius: 1.5 });
circle.distance({ x: 12, y: 42 }) === 0.5;

const Tagged = stampit({
  props: {
    tag: ""
  },
  init({ tag }) {
    this.tag = tag || this.tag;
  }
});

const Colored = stampit({
  props: {
    color: "#000000"
  },
  init({ color }) {
    this.color = color || this.color;
  }
});

const GraphPoint = stampit(Circle, Tagged, Colored, {
  methods: {
    draw(ctx) {
      ctx.setColor(this.color);
      ctx.drawFilledCircle(this.x, this.y, this.radius);
      if (this.tag) 
        ctx.drawText(this.x + this.radius, this.y + this.radius, this.tag);
    }
  }
});

// FOUR different initializers will be executed here.
const graphPoint = GraphPoint({ 
  x: 12, 
  y: 42, 
  radius: 1.5, 
  color: "red", 
  tag: "start" 
});
graphPoint.draw(someDrawingContext);

const Line = stampit({
  props: {
    point1: Point({ x: 0, y: 0 }),
    point2: Point({ x: 0, y: 0 })
  },
  init({ point1, point2 }) {
    this.point1 = Point(point1);
    this.point2 = Point(point2);
  },
  methods: {
    middlePoint() {
      return Point({ 
        x: (this.point1.x + this.point2.x) / 2, 
        y: (this.point1.y + this.point2.y) / 2
      });
    }
  }
});

const line = Line({ point1: { x: 12, y: 42 }, point2: { x: 34, y: 40 } });

const GraphLine = stampit(Line, Colored, Tagged, {
  methods: {
    draw(ctx) {
      ctx.setColor(this.color);
      ctx.drawLine(this.point1.x, this.point1.y, this.point2.x, this.point2.y);
      if (this.tag) 
        ctx.drawText(this.middlePoint().x, this.middlePoint().y, this.tag);
    }
  }
});

const graphLine = GraphLine({ 
  point1: { x: 12, y: 42 }, 
  point2: { x: 34, y: 40 }, 
  color: "red", 
  tag: "March" 
});
graphLine.draw(someDrawingContext);

The idea is - separate concerns. The above has:

  • Point concern and all related functionality.
  • Circle concern and the related functionality. Circle is an extension of the Point.
  • Tag concern.
  • Color concern.
  • GraphPoint concern is Circle, Tag and Color concerns.
  • Line concern - it has two Points.
  • GraphLine - is the Line but with additional Tag and Color functionalities.

More info and examples are here: stampit.js.org/