Using this utility, and OOP in general, allows you to more easily write reusable, extensible, maintainable, and testable code.
A class may be created in one of two ways (both are equivalent):
var Car = Class;
var Car = Class;
constructor function is optional. If one is not provided, a default constructor that simply calls the superclass's constructor is used instead. The default constructor passes all arguments to the superclass's constructor as well.
The object that you provide to
Class.extend() is the "class definition". Properties specified on this object (with the exception of the special property
constructor and a few others which we will see later) are placed on the constructor function's (i.e. class's) prototype. This is where instance methods, and defaults for fields (instance-level properties) should be placed. Ex:
var Cat = Class;
Classes can be extended (i.e. can inherit from) from other classes. This can be done in one of two ways (which are equivalent).
var Animal = Class;var Cat = Class;var cat = ;cat; // "Hi from Animal"cat; // "Meow from Cat"
extendmethod which is placed on all classes created using
var Animal = Class;var Cat = Animal;var cat = ;cat; // "Hi from Animal"cat; // "Meow from Cat"
Superclass methods can be called easily by using
this._super() takes an array of arguments to pass to the superclass method. It is done this way because most often, you will simply be passing up the
arguments object to the superclass method. Ex:
var BaseClass = Class;var SubClass = BaseClass;var instance = 1 2 3 ;
However, if you want to call the superclass method with specific arguments, simply pass them in an array. Ex:
var BaseClass = Class;var SubClass = BaseClass;var instance = ;instance;
As you can see from the examples above,
this._super() works both in the constructor, and all instance methods.
With the traditional example of animals...
var Animal = Class;var Dog = Animal;var Cat = Animal;var dog1 = "Lassie" ;var dog2 = "Bolt" ;var cat = "Leonardo Di Fishy" ;dog1; // "Woof! My name is: Lassie"dog2; // "Woof! My name is: Bolt"cat; // "Meow! My name is: Leonardo Di Fishy"dog1; // "Lassie is eating"dog2; // "Bolt is eating"cat; // "Leonardo Di Fishy is eating"
Because this implementation does not rely on making
Class the superclass of all classes, Class.js can also be used to extend classes from other frameworks using
Class.extend() (if those classes rely on prototype-chained inheritance behind the scenes, as Class.js does). This allows you to add Class.js features (mixins, inherited static properties, etc) to new subclasses of another hierarchy. Ex:
var MySubClass = Class;
For those of you familiar with Backbone.js, instead of following the usual method of extending a Backbone class (like
Backbone.Model), you could use
Class.extend() to add the features of Class.js to extend it instead. The following example shows how to do so to gain access to
var MyModel = Class;
Class.js allows you to define static methods within the class definition itself (which makes for easier to read / understand code over some other inheritance implementations, which force you to add static methods only after your subclass has been defined).
There are two ways to define static methods/properties:
var Animal = Class;// Concrete classes (which simply use the definition of the Animal class, and inherit the static 'load' method)var Cat = Animal;var Dog = Animal;// ---------------------------------------// Use of the static method that exists only on the Animal class (not subclasses)var myCat = Animal; // instantiate a Cat object using the static factory methodconsole; // true// Use of static method that is inherited by subclassesvar firstCat = Cat; // load the "first cat" from our "server". Instantiates a Cat objectvar firstDog = Dog; // load the "first dog" from our "server". Instantiates a Dog objectconsole; // trueconsole; // true
Although I recommend that you keep multiple inheritance to a minimum (as it increases complexity -- use composition as much as possible instead), there are a few cases where you do want to share some code where that code wouldn't make sense to be a part of your normal inheritance hierarchy as a base class. But also, mixins allows you to implement interfaces as well.
An example of implementing an interface:
// The interfacevar List = Class;// A class implementing the interfacevar CoolList = Class;var myList = ;myList; // succeedsmyList; // ERROR: "remove() must be implemented in subclass"
Our interface could have been implemented using the generalized "abstractMethod" convenience function provided with Class.js as well:
// The interfacevar List = Class;
Here's an example of using a mixin with actual functionality, which adds event-based Observable functionality to the class:
// A mixin that can add very simple events functionality to a class (if anyone wants this for real real, I'll make a github for it)var Observable = Class;// A class that uses the mixinvar Duck = Class;var duck = "Milo" ;// Observe the duck's quackingduck;duck; // will trigger (fire) the event
Duck inherited the methods from the mixin. However, if the class that is being created already defines a method that the mixin also defines,
the class's method overrides it. In this case, you must manually call the mixin's method, if you want it to be called (i.e. you wanted to "extend" the mixin's method, not completely override it with your new class's definition). Following from the example from above:
var Duck = Class;
One last note: if the class includes multiple mixins that all define the same property/method, the mixins defined later in the
mixins array take precedence.
A class may be declared with the special boolean property
abstractClass on its prototype, to prevent direct instantiation of the class. This enforces that a concrete subclass must be created to implement the abstract class's interface.
(Note: Unfortunately, a property name other than the word
abstract had to be used, as
// An abstract class which serves as the base class of Car and Truckvar Vehicle = Class;// Concrete classvar Car = Vehicle;// Concrete classvar Truck = Vehicle;// Attempt to instantiate a Vehicle directlyvar vehicle = 'Chevy' 'P.O.S.' ; // error! Cannot instantiate abstract class// Now concrete vehicle typesvar car = 'Honda' 'Accord' ;; // 130var truck = 'Ford' 'F150' ;; // 80
To declare a method as abstract, set it to the function referred to by
Class.abstractMethod. This allows for "compile time" (i.e. class creation time) checking that concrete classes implement all abstract methods from their abstract base class(es). It also allows for a warning for when a class is defined as having abstract methods, but is not declared with
var Appliance = Class;var Oven = Appliance;
However, if we forget to implement an abstract method, we get an error right off the bat reminding us to do so (as opposed to only getting an error when the abstract method is called):
var Appliance = Class;// Errors because this class forgets to implement the `turnOff` method, and is not declared as abstract itselfvar Oven = Appliance;
This is a special method that may be defined under the
inheritedStatics section, which is executed when the class
is finished being created (i.e. its prototype / inheritance chain has been set up, its mixins have been set up, etc). This can be
used as a static initializer for the class, which you may use to set up the class itself (if there is anything to do at this time).
Although rarely used, it is very useful for setting up static properties for an entire hierarchy of subclasses (when the
onClassCreated method exists under
As a very simple example, we could assign a unique id for each class itself (not instances) in an inheritance heirarchy, including the class that it was originally defined on.
var counter = 0;var MyBaseClass = Class;// empty subclass definition, but onClassCreated() still executes after it is created// because it was defined under the `inheritedStatics` sectionvar MySubClass = MyBaseClass;; // alerts: 1; // alerts: 2
Unfortunately, the "class-js" package name was taken, so I made it "class-js2"
npm install class-js2 --save
var Class = ;var HelloWorld = Class;//...
Class()as a function which internally called
Class.create(). This wouldn't pass JSHint validation when used (JSHint expects the
newoperator for a capitalized var), and
Class.create()is clearer anyway.
onClassCreated(leaving the old one for backward compatability)
Class.isSubclassOfmethod, for statically testing if a class is a subclass of another (without creating instances).
this._super()calling method in constructor functions for Internet Explorer. Apparently a property named
constructoris not enumerable using a for-in loop in IE...