2.1.0 • Published 9 years ago

classy-js v2.1.0

Weekly downloads
3
License
ISC
Repository
github
Last release
9 years ago

Classy

A simply demure class library.

All Composition: All Class.

Author: Jordan Timmerman (@skorlir)

Built at the Northwestern University WCAS Multimedia Learning Center

Build Status

What is this and Why

Classy is a crazy experiment in just how far you can go, and just how little you really need, to create useful, versatile, extensible class objects in Javascript.

This is the premise:

You don't need prototypes. You don't need class. You don't need super or extend. With a small set of powerful functions for composing simple data structures, you can build anything.

It's a no-class class system. It's simple. It's Classy.

Features

Classy is a composeable class constructor constructor. Anyplace you might imagine yourself using function and prototypes, you can use ClassyClasses instead.

  • ClassyClasses are objects (using Object.create(null))
  • Everything is defined with composition
  • Every feature is a plugin, defined as a composeable function
  • There's no contextual this (powered by Selfish)
  • The syntax is so simple your cat could sit on your keyboard and use it by accident

      var aClass = Classy(function (self) {
          self.field = 'value'
          self.method = function () {}
          // return is implied
      })

Usage

Show me some code

var TrivialClass = Classy(function () {})

TrivialClass()

Is the simplest possible Classy Class.

This will return an Object ({}) that has absolutely no fields or methods, and no prototype.

Adding Features

Composition is first-class in Classy, and everything that Classy does is an application of composition.

Check it out:

// A couple of features we want to mix in
var IsInstance = require('classy-js/core/is-instance')
  , ToString   = require('classy-js/core/to-string')
  , Compose    = require('classy-js/core/compose')

var Classy = require('classy-js')

// We want all our ClassyClasses to have `isInstance`, so let's create
//  a "class medium" for creating ClassyClasses that have it.
var Medium = Classy().use(IsInstance)

// Now we can `define` classes from that Medium
var Animal = Medium.define(function (animal, type) {
  animal.type = type
})

// Equivalently, we could have done:
var Animal = Classy()
  .use(IsInstance)
  .define(function (animal, type) {
    animal.type = type
  })

// But using a Medium makes things a bit cleaner.

// Let's use that Animal class.
var Dog = Medium
  .use(ToString) // You can add another feature to the medium just for Dog
  .define(function (dog, name) {
    dog.name = name
  })
  .use(Compose(Animal, 'Dog'))

// So now Dog is "composed with" Animal.
// Let's add in some Dog actions.

// Dogs can bark
function Bark (dog) {
  dog.bark = function () {
    return dog.name + ' is barking! Woof! Woof!'
  }
}

// They can wag their tails
function WagTail (dog) {
  dog.wagTail = function () {
    return dog.name + ' is wagging his tail!'
  }
}

// Okay, let's compose those into Dog
Dog = Dog.use([ Bark, WagTail, ToString ])
// Notice that we can also add ToString to Dog here
// We added ToString to the Dog _class_, and now we're adding it to
//   every Dog _instance_.

// Let's play around with our new ClassyClasses!

Dog.toString()
// => Dog { toString: fn, isInstance: fn, constructor: function (dog...) {...} }

var fido = Dog('Fido')
// => { name: 'Fido', type: 'Dog', bark: fn, wagTail: fn, toString: fn }

fido.bark()     // => 'Fido is barking! Woof! Woof!'
fido.wagTail()  // => 'Fido is wagging his tail!'
fido.type       // => 'Dog'
fido.toString()
// => { name: 'Fido', type: 'Dog', bark: fn, wagTail: fn, toString: fn }

// We can check instances, too:
Animal.isInstance(fido) // true
Dog.isInstance(fido)    // true
Dog.isInstance(extend({}, fido, { type: 'Cat' }))    // false
Animal.isInstance(extend({}, fido, { type: 'Cat' })) // true

Bare by Default

Because ClassyClasses use Object.create(null) as their basis, classes are entirely bare by default - this is why every feature is a plugin, and why ClassyClasses have no prototypes or this.

Here's an explicit, albeit contrived, example:

var Truth = Classy(function (self) {
 self.life = 42
})

var theTruth = Truth()
// => { life: 42 }

theTruth.prototype
// => undefined

Object.getPrototypeOf(theTruth)
// => null

for (var fact in theTruth) {
 console.log('The Truth is ', fact, '=', theTruth[fact])
}
// [logged]: The Truth is life = 42

[ 'toString', 'valueOf', 'isPrototypeOf', 'hasOwnProperty' ].map(function (junk) {
 if (theTruth[junk]) return junk
})
// => []
2.1.0

9 years ago

2.0.5

10 years ago

2.0.4

10 years ago

2.0.3

10 years ago

2.0.2

10 years ago

2.0.1

10 years ago

2.0.0

10 years ago

1.0.8

10 years ago

1.0.7

10 years ago

1.0.6

10 years ago

1.0.5

10 years ago

1.0.4

10 years ago

1.0.3

10 years ago

1.0.2

10 years ago

1.0.1

10 years ago

1.0.0

10 years ago