StdClassJS
A dead simple JavaScript inheritance implementation.
A tool, not a framework. Inheritance is done exactly the same way it would be done manually so instanceof will still work and there is no speed reduction. It's all just wrapped up in some helper methods so the repetative boilerplate can be avoided.
It Does Not...
- Do multiple inheritance.
- Can of worms.
- Handle, promote, or propagate any specific means of making methods/properties private/protected.
- Useful constructs, but enforcement is not useful overhead. Agree to a pattern such as "_ prefixed methods are protected", and move on.
- Add _super/_superApply methods.
- It does add a static reference to the parent prototype of a constructor, but it's passive and reasonably non-invasive.
- Wrap any methods.
- "What about the constructor?" nope
- "What about ..." NO
- Any wrapping of anything would create overhead and side effects. Do it yourself if you want to.
- Break if you modify your classes outside of its pervue.
- Feel free to modify prototypes, attach properties willy-nilly, or extend StdClass classes any way you want. This is a tool, not a framework.
- Break instanceof or require a third-party way of checking pedigree.
- Use Object.create or anything that is only in ECMAScript "newer than your environment supports" Edition.
- Juggle.
- This was a tough decision, but in the end I had to conclude it wasn't worth the effort.
You Can...
Use It With Node
Install With NPM
$ npm install stdclass
Unit Testing (optional)
$ npm install --dev stdclass
$ npm test stdclass
Require It
#!/usr/bin/env node
var StdClass = require( 'stdclass' );
Use It In The Browser
<script src="/path/to/stdclass.js"></script>
Use It Inline
There are no dependencies and the source is small, so feel free to copy the source into your own project if adding a seperate file seems cumbersome. Remember to include the license. I recommend just grabbing the source between /* BEGIN CLASS: StdClass */
and /* END CLASS: StdClass */
.
API
StdClass and any derivative constructors it creates, have the following static methods that do all the magic.
extend( [ Function constructor ] )
- Creates a child class that inherits from the constructor that extend is being statically called on.
- Takes an optional constructor function as an argument.
- If no argument is given, then the parent constructor will be inherited. See the examples section if you're not sure what that means.
- Useful when you just want to create a child with overridden methods.
- Copies
extend
,implement
,neo
, andcleanupClassHelpers
static methods to the new child class. - Returns the constructor it's attached to.
implement( [ Object, ... ] )
- Add properties to the class prototype.
- All non-null/non-undefined properties on all objects passed to implement will be added to the class constructor prototype.
- Takes 0 or more objects as arguments.
- Passing no objects is silly, but allowed.
- Returns the constructor it's attached to.
neo( ... )
- A static shortcut for
new
. Class.neo( ... )
is equivalent tonew Class( ... )
.- Prettier when you want to immediately chain methods on a new instance or if you're using instantiation for side effects.
- Does introduce a teeeeensy bit of overhead. I mean really teensy. Statistically insignificant.
- Returns a new instance of the constructor it's attached to.
- A static shortcut for
cleanupClassHelpers()
- Removes
extend
,implement
,neo
, and itself from the class. - This name intentionally left long to stay out of the way and because it's just here for completeness sake and I don't expect many people to use it.
- This does not affect the prototype, so any derivatives that have already been created will also be un-affected.
- Returns the constructor it was attached to.
- Removes
StdClass also has the following static method which it does not pass along to the new constructors that it creates.
StdClass.mixin( [ true, ] Function constructor )
- Takes a required constructor function argument.
- Attaches the
extend
,implement
,neo
, andcleanupClassHelpers
static methods to constructors that were not created by StdClass. - Useful for adding StdClass inheritance to classes that cannot directly inherit from StdClass.
- Also returns the constructor it's attached to, just in case you want to attach it to something else.
All of the above methods are completely portable. You can attach any of them to any function and they will just work.
A reference to the parent class prototype is also statically attached to child constructors for convenience.
parent
Child.parent === Parent.prototype
This allows parent methods to be referenced without referring to the parent by name which helps avoid refactoring if your parent class changes or is renamed. You can of course still use the parent class name if you prefer.
The parent
property also allows for a universally accessible inheritance chain like this.
constructor.parent.constructor.parent.constructor.parent ...
Examples
Parent Class
var MyParent = StdClass.extend( function( args, go, here )
{
// Do parent constructor stuff
})
.implement({
instanceMethod: function( and, here )
{
// Do method stuff
}
});
Child Class
var MyChild = MyParent.extend( function( args, go, here, too )
{
// Call the parent constructor
MyChild.parent.constructor.call( this, args, go, here );
// The above is equivalent the following, but has the advantage of
// not using the parent name. This can save time refactoring should the
// parent class change or be renamed.
//
// MyParent.prototype.constructor.call( this, args, go, here );
// OR
// MyParent.call( this, args, go, here );
// Do child constructor stuff
this.too = too;
})
.implement({
instanceMethod: function( and, here, too )
{
// Call the parent method
MyChild.parent.instanceMethod.call( this, and, here );
// Once again, the above is equivalent to the following except that
// you don't need to use the parent class name.
//
// MyParent.prototype.instanceMethod.call( this, and, here );
// Do child method stuff
this.too = too;
},
newMethod: function()
{
// Do new method stuff
}
});
No explicit constructor means inherit the parent constructor.
Inheriting a constructor means automatically creating a constructor function that does nothing except call the parent constructor, passing through all arguments.
var InheritedConstructor = MyChild.extend();
// Which is the same as...
var Equivalent = MyChild.extend( function()
{
return Equivalent.parent.constructor.apply( this, arguments );
});
Implement can be passed multiple arguments.
InheritedConstructor.implement(
{
foo: 'foo', // Replaced below
bar: 'bar', // Not replaced below
},
{
foo: 'foo2', // Replaces above 'foo' value
bar: null // Does not replace above 'bar' value
}
)
StdClass helper methods can be mixed-in to any class.
var DumbClass = function()
{
// Assume this is somebody elses class that you've included in your
// project. It doesn't have any built in way of extending it! You'll
// have to do it the old fashioned way... or will you?
};
StdClass.mixin( DumbClass );
// Woot!
var NotSoDumbClass = DumbClass.extend();
Turn it back into a dumb class.
NotSoDumbClass.cleanupClassHelpers();
NotSoDumbClass.hasOwnProperty( 'extend' ); // false
NotSoDumbClass.hasOwnProperty( 'implement' ); // false
NotSoDumbClass.hasOwnProperty( 'neo' ); // false
NotSoDumbClass.hasOwnProperty( 'cleanupClassHelpers' ); // false
// Awww, it's dumb again.
License
The MIT License (MIT)
Copyright (c) 2013 Christopher Ackerman
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.