Spritzr
About spritzr
Spritzr is an inheritance/traits/talents library for node.js and the browser.
It aims to provide three things:
- Extension: to do classical single inheritance
- Traits: mixins at a class level and with state (a bit like multiple inheritance)
- Talents: like traits, but applied to a single instance (and they can also be removed at will)
Spritzr was inspired by a great traits/talents library CocktailJS https://github.com/CocktailJS/cocktail.
Compatibility
Spritzr is automatically unit tested against the following browsers:
- Internet Explorer 6, 7, 8, 9, 10 and 11
- Firefox latest and 3.6 versions
- Chrome latest version
- Safari desktop latest version
- Opera desktop latest version
- Android 2.3 and 4.2
- iOS 7
It is also tested on node.js version 0.10 and PhantomJS (headless webkit).
Special thanks to BrowserStack for providing a free account to run our automated tests on all these different browsers and operating systems!
API
Inheritance
Spritzr has an extend
function which implements a basic single inheritance model.
The prototype chain is maintained so that instanceof
works as expected.
{ thistype = type;};Animalprototypetype = null; Animalprototype { return "Hi, I'm a " + thistype + " called " + thisname;}; // We need to explicitly call the super constructor using a handy $super property. { this; thisname = name;};Personprototypename = null; Spritzr; var steve = "Steve"; ;;; // Spritzr also provides an isa() function which acts like instanceof; // We can also override methods and properties of the super class// but still access them using $supervar { this;};Spritzr; Tonyprototype { var normalGreeting = this$super; return normalGreeting + ", y'all!";}; var tony = ;;
Gotchas
Super constructor not explicitly called
When extending a class, the super class constructor will not be implicitly called when the sub class is instantiated. Therefor you should call the $super()
constructor from within the subclass constructor; for example:
var { // Set up animaly stuff}; var { this; // Call the Animal() constructor // Set up mammalian stuff like live babies and stuff.}; Spritzr;
Scope fudging when calling a super class method
When you call a super class method using this.$super.method()
then the scope gets lost somewhere along the way, which means that this
within the super method is not pointing to the correct object. There are two ways around this:
- Ensure you always call
this.$super()
from within the constructor. This has access to the real object and will cache it for later within the $super object. You can then just callthis.$super.method()
to call the super class method. - Pass in othe correct scope when you call the method by using
this.$super.method.call(this, arg1, arg2 ... )
.
Traits
Classes can be extended with multiple traits, which are a bit like interfaces in Java but can also contain method implementations and properties.
var { }; var { };Spritzr; // Mammal is an extension of Animal var { };Spritzr; // Amphibian is an extension of Animal var { };Spritzr; // Bird is an extension of Animal // We create a LaysEggs trait, which can be a class or a plain old objectvar { };LaysEggsprototype { return ;}; // And we can apply the trait to specific classesSpritzr; // All Amphibians can now lay eggsSpritzr; // All Birds can now lay eggs // Then we can use isa() to work out if an object has a specific traitvar cat = ;; var frog = ;; var parrot = ;; // Traits are also inherited through class extensionvar { };Spritzr; // Emu extends Bird var emu = ;; // or from other traitsvar { };Spritzr; var { };Spritzr; // A platypus is a mammalSpritzr; // But also a monotreme var ducky = ;;
Talents
Talents are a bit like traits, but they are applied to instances rather than classes. They can also be removed at any point.
// We have a class describing peoplevar { thisfirstName = firstName; thislastName = lastName;};PersonprototypefirstName = null;PersonprototypelastName = null;Personprototype { return thisfirstName + ' ' + thislastName;}; var sharon = "Sharon" "Ackerman";var tony = "Tony" "Jones"; // We can define a talent to describe friendshipvar { thisnickname = nickname;};Friendprototypenickname = null;Friendprototype { return thisfirstName + ' "' + thisnickname + '" ' + thislastName;}; // And add a new talent to our friendsSpritzr; // We can then check for existence of the talent to find out if a person is our friend;; // And the methods should be overridden appropriately;; // We can also remove talents from an instance using the amazing titled unspritz functionSpritzr;; // We don't need to instantiate the talent first either - the constructor will automatically be calledvar { thisusername = thisfirstName + thislastName;};HasAccountprototypeusername = null; Spritzr; // This adds the methods/properties and calls the constructor ;;
Caveats
The library is reasonably well tested, but there are some flows which haven't been thought about much so far:
- The effect of spritzing the same trait or talent twice into a class of object is untested and undefined.
- New properties added to a trait after it has been spritzed into a class won't be reflected in the class (so if you add trait A into class B, then add a method to A it won't be reflected in B). You should set up your class hierarchy at the start of the application and avoid mutating it later.
- Equally, methods added to a talent after it's been spritzed into an instance also won't be reflected in the instance.
- Probably a load of other stuff I haven't thought of!
The intention is to overcome these caveats in the future - it should be possible.