stampit API
  • Introduction
  • Essentials
    • What is a Stamp
    • Installation
    • Specification
      • Merging algorithm
      • Object creation internals
    • FAQ
  • API
    • Quick start
    • Basics
    • Methods
    • Properties
    • Deep properties
    • Initializers
    • Static properties
    • Static deep properties
    • Configuration
    • Deep configuration
    • Composers
    • Property descriptors
    • Static property descriptors
    • Name
  • Ecosystem
    • Ecosystem Overview
    • @stamp/collision
    • @stamp/required
    • @stamp/privatize
    • @stamp/named (DEPRECATED)
    • @stamp/instanceof
Powered by GitBook
On this page
  • What is stampit
  • Differences from classes
  • History
  • Using Stamps
  • Ecosystem
  • Quick code example

Introduction

NextWhat is a Stamp

Last updated 4 years ago

What is stampit

stampit is a JavaScript module which implements the . Stamps are composable factory functions.

Think of stamps as classic classes but without any limits, boundaries, or rules.

The stamps brag large amount of features and things you can do with your objects and/or stamps.

Head straight to the page for code examples.

Differences from classes

  • Inheritance

    • Classes

      • Classes do child+parent+parent+parent... inheritance chains. See picture above.

      • When you extend a class you link class prototypes.

    • Stamps

      • Stamp are single object + single prototype. See picture above.

  • Object creation

    • Classes

      • In most programming languages you execute only one constructor per class.

      • To pass data to the parent constructor you have to manually call parent constructor this.super(data).

      • To create an object you need to use the new keyword: const object = new MyClass().

    • Stamps

      • Stamp executes every initializer (aka constructor) it has.

      • All initializers receive exactly the same set of arguments, no need to manually pass data.

      • The initializer execution sequence is the same as the stamp composition sequence.

History

Using Stamps

Ecosystem

Quick code example

The example below introduces GraphPoint and GraphLine stamps (aka blueprints, aka factory functions, aka behaviours).

Let's start by declaring the Point stamp.

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);
    }
  }
});

That's how you create instance of that stamp.

// Creating instance of the Point.
const point = Point({ x: 12, y: 42 });
// Calling a method.
point.distance({ x: 13, y: 42 }) === 1.0;

Now let's "inherit" the Point to create a Circle. We will add radius property to the mix.

// kinda inheritance, but not really
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;
    }
  }
});

When creating instance of the Circle you will actually call TWO different initialisers (aka constructors). Stamps have multiple initialisers.

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

Now, declaring couple of additional stamps. We'll use them to enrich JavaScript drawable objects.

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;
  }
});

The Drawable behaviour. We have to make sure that developers implement the draw() method when using this stamp.

const Required = require("@stamp/required");

const Drawable = stampit(
  Tagged,
  Colored,
  Required.required({ methods: { draw: Required } })
);

Now let's implement the GraphPoint stamp.

const GraphPoint = stampit(Drawable, Circle, {
  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);
    }
  }
});

Please, note we are executing FOUR intializers here.

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

Line primitive.

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 } });

Our final destination - a drawable graph line.

const GraphLine = stampit(Drawable, Line, {
  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);
    }
  }
});

And the usage.

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. We split our logic into several reusable highly decoupled primitive behaviours (Point, Colored, Tagged, Circle, Line).

The above has:

  • Point concern and all related functionality in a separate stamp.

  • Circle concern and the related functionality separated. Circle is an extension of the Point.

  • Tagged concern in a separate stamp.

  • Colored concern in a separate stamp.

  • GraphPoint concern is Circle, Tagged and Colored concerns combined.

  • Line concern - it has two Points.

  • GraphLine - is the Line but with additional Tagged and Colored functionalities.

You can't do anything like that with classes. And the resulting code is more readable than classes.

When you "extend" you separately merge , separately merge , separately merge , separately merge (aka constructors), etc etc etc.

That's why it is not just inheritance, but special type of object composition. The stamp composition algorithm is .

You can influence composition result with your code at runtime using the feature.

To create an object you as a function: const object = MyStamp().

The original idea of Stamps belongs to . See his from 11 Feb 2013 in the stampit repository. Since then the idea evolved to the and a whole of mini modules.

Head straight to the for code examples.

Stampit have a lot of helper modules. You can always find the list of official NPM packages here:

See more information on the page.

methods
properties
static properties
initializers
standardized
composers
call stamp
Eric Elliott
first commit
specification
ecosystem
Quick start
https://www.npmjs.com/~stamp
Ecosystem
stamp specification
Quick Start
Object instances created from classes vs stamps.