UNPKG

pixl-webapp

Version:

A client-side JavaScript framework, designed to be a base for web applications.

266 lines (180 loc) 9.62 kB
# Overview The JavaScript language already supports object orientated programming, but the syntax is strange and inheritance is wonky. This library is provided as a means to create classes in a more classical sort of way, including support for static class members, proper constructors, inheritance, and namespaces. # Usage This section describes how to use the framework. ## The oop.js File The framework is provided as a JavaScript library file that you must include in your web page: ```html <script type="text/javascript" src="oop.js"></script> ``` That's it! The library is now available to your page. Make sure you include the library near the top of your file, above all your other code. ## Creating Classes Here is how you create a class using the framework. You'll notice this is dramatically different than the traditional JavaScript class syntax. ```javascript Class.create( 'Animal', { // class member variables nickname: '', color: '', // class constructor __construct: function(new_name, new_color) { this.nickname = new_name; this.color = new_color; }, // methods getInfo: function() { return("Nickname: " + this.nickname + "\nColor: " + this.color); } } ); ``` This defines a class called `Animal`, with two member variables, `nickname` and `color`, a constructor and a `getInfo()` method which returns the nickname and color. You'll notice that to define the constructor method you use the keyword `__construct` which is exactly the same in PHP. Usage of this class is probably what you would expect: ```javascript var dog = new Animal('Spot', 'Green'); console.log( dog.getInfo() ); ``` Of course, you can also access the class member variables, as all members are public. ```javascript dog.nickname = 'Skippy'; dog.color = 'Blue'; console.log( dog.getInfo() ); ``` ## Creating Subclasses To create a subclass that inherits from a base class, use the following syntax: ```javascript Class.subclass( Animal, 'Bear', { // define a new member variable wants: 'Honey', // and a new method roar: function() { console.log("Roar! Give me " + this.wants + "!"); } } ); ``` This defines a `Bear` class which inherits from the base `Animal` class, including the constructor. Notice that we passed the parent class as a reference, not a string. What we did is extend the base class by introducing a new member variable `wants`, and a new method `roar()`. Everything else from the base class will be present in subclass instances: ```javascript var grizzly = new Bear('Fred', 'Brown'); console.log( grizzly.getInfo() ); grizzly.wants = 'blood'; grizzly.roar(); ``` ## Inheritance The framework provides an easy way to override methods defined in a base class. Simply redeclare them in the subclass: ```javascript Class.add( Bear, { // override base class method getInfo: function() { return "Bear has overridden this!"; } } ); ``` This also demonstrates `Class.add()` which allows you to add/replace methods or member variables in a class after it is defined. Note that the class name is passed in as a reference, not a string. Alternatively you could just have defined the method to be overridden in the original subclass definition. ### Calling Superclass methods You can also explicitly invoke the superclass method, in order to extend its functionality: ```javascript Class.add( Bear, { // override base class method getInfo: function() { // first, get info from base class var info = this.__parent.getInfo.call(this); // append bear info and return combined info info += "\nWants: " + this.wants; return info; } } ); ``` So here we are overriding the base class `getInfo()` method, but the first thing we do is call the superclass method of the same name. This is done with the `__parent` keyword, which points to the parent class prototype object. The JavaScript `call()` method allows you to call a function in object context (hence we are passing in the `this` keyword to it). Invoking a superclass constructor is just as easy: ```javascript Class.subclass( Animal, 'Bear', { // define a new member variable wants: 'Honey', // override base class constructor __construct: function(new_name, new_color, new_wants) { // invoke superclass constructor to set name and color this.__parent.__construct.call(this, new_name, new_color); this.wants = new_wants; } // and a new method roar: function() { console.log("Roar! Give me " + this.wants + "!"); } } ); ``` **Note:** You cannot use `Class.add()` to redeclare a class constructor. ## Static Class Members You can define static class members (variables or methods) by using the `__static` keyword. These members do not become part of class instances, but instead live inside the class reference object, and must be accessed that way too. Example class definition: ```javascript Class.create( 'Beer', { // static members __static: { types: ['Lager', 'Ale', 'Stout', 'Barleywine'] }, // class member variables nickname: '', type: '', // class constructor __construct: function(new_name, new_type) { this.nickname = new_name; if (!Beer.types[new_type]) throw new Error("TYPE NOT KNOWN: " + new_type); this.type = new_type; }, // methods getInfo: function() { return("Nickname: " + this.nickname + "\nType: " + this.type); } } ); ``` Here we define a `Beer` class which has a static member defined in the `__static` element. Anything placed there will *not* be propagated to class instances, and must be accessed using the class reference instead. As you can see in the constructor, we are checking the new type against the `types` array which is declared static, so we are getting to the list by using the syntax: `Beer.types` rather than `this.types`. If you were to change `Beer.types` later on, then *all* classes would see the changes instantly. The content is effectively shared. ## Namespaces The OOP framework also offers "namespaces". That is, named containers for your classes to live in, to prevent name collision with built-in classes or 3rd party libraries. You declare namespaces simply by adding periods (.) to your class name as passed to `Class.create()` and `Class.subclass()`: ```javascript Class.create( 'Beverage.Beer', { } ); ``` You can nest namespaces any number of levels deep. The library will automatically create any parent namespaces as needed. Now you can create instances of the class using `Beverage.Beer` instead of just `Beer`: ```javascript var mybeer = new Beverage.Beer(); ``` You can explicity create namespaces (without creating classes) using the `Namespace.create()` function. Example: ```javascript Namespace.create( 'Beverage' ); ``` All this does is create empty objects, ready to receive classes (although creating classes automatically creates the parent namespaces, so this is typically not required). **Note**: Do not use a class name as a namespace, or visa-versa. For example, if you create a class named `Beverage`, do not also use it as a namespace for a subclass, like `Beverage.Beer`. The results are undefined. ## Require Classes If your class requires other classes (possibly those defined in other include files), you can use the `Class.require()` function to make sure they are available. This is sort of like a class "assert" (see below), which will throw an alert if the required classes are not defined. Example: ```javascript Class.require( 'Animal' ); ``` This would throw an alert if the `Animal` class was not defined. You can also specify multiple classes on the same call: ```javascript Class.require( 'Animal', 'Bear' ); ``` ## Assert Macro The framework comes with a very simple `assert()` macro for testing that things you know should be true, are in fact true. You can pass it an optional message to be alerted if the assert fails, so you can tell them apart. Example: ```javascript assert( 1 + 1 == 2, "Math doesn't work" ); ``` If the fact turns out to be false, then the text is displayed in an alert so you can immediately see it and respond. This is a good way to check for objects that should exist in a parent container, by using the double-bang operator: ```javascript assert( !!window.something, "window.something does not exist"); ``` Using the double-bang operator simply converts anything to a Boolean -- a simple way to check for existence of an object. # License The MIT License Copyright (c) 2004 - 2015 Joseph Huckaby 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.