proteus

A declarative way of creating objects, properties, and classes in ES5 JavaScript

npm install proteus
2 downloads in the last day
9 downloads in the last week
156 downloads in the last month

proteus

A declarative way of creating objects, properties, and classes in ES5 JavaScript

ˈprōtēəs; ˈprōˌt(y)oōs

In Greek Mythology a minor sea god (son of Oceanus and Tethys) who had the power of prophecy but who would assume different shapes to avoid answering questions.

From the Greek protos "first."

Overview

Proteus is a little library of utility functions that I put together to help manage creating objects and implementing classical inheritance in JavaScript flavors 1.8+.

Object Creation and Modification

Proteus.create(proto, props)

Interface to Object.create, however, the props argument is a plain object of properties to copy over to the newly created object. Getters and setters will be preserved.

Proteus.defineProperty(obj, name, val, [spec])

Utility method for creating 'plain' properties on an object. Plain being {enumerable: true, writable: true, configurable: true}, the passed spec can override these defaults.

Proteus.defineProperty(obj, "propName", 42);

Proteus.defineProperty(
    obj,
    "methodName",
    function () { /*...*/ },
    {enumerable: false}
);

Proteus.defineProperties(obj, list)

Define multiple properties.

Proteus.defineProperties(obj, [
    [obj, "propName", 42],
    [obj, "methodName", function () {
        /*...*/
    }, {
        enumerable: false
    }]
]);

Proteus.defineGetter(obj, name, fn, [spec])

Utility method for creating a getter on an object. The property definition will default to {enumerable: true, configurable: true} unless overridden with the spec object.

Proteus.defineSetter(obj, name, fn, [spec])

Utility method for creating a setter on an object. The property definition will default to {enumerable: true, configurable: true} unless overridden with the spec object.

Proteus.defineGetSet(obj, name, getter, [setter], [spec])

Utility method for creating both a getter and a setter on an object. The property definition will default to {enumerable: true, configurable: true} unless overridden with the spec object.

if setter is not given, then the getter function will be used for both getting and setting

Proteus.getPropertyNames(obj)

Get all property names for an object, all the way up the prototype chain.

Proteus.getPropertyDescriptor(obj, name)

Get a property descriptor for an object where ever it may exist in the prototype chain.

Proteus.copyOwnProperties(hidden = false, overwrite = true, supplier, receiver)

Copy properties from supplier to receiver. This method will preserve the property definitions from supplier to receiver (uses Object.getOwnPropertyDescriptor under the hood).

Proteus.copyAllProperties(supplier, receiver)

Copy all properties from supplier to receiver.

Proteus.applyProperties(supplier, receiver)

Copy all enumerable properties from supplier to receiver only if the property does not exist on the receiver object.

Proteus.applyAllProperties(supplier, receiver)

Copy all properties from supplier to receiver, but do not overwrite existing properties on receiver.

Proteus.merge(receiver, arg1, ..., argN)

Merge enumerable properties from all objects passed as arguments onto receiver.

Proteus.mergeAll(receiver, arg1, ..., argN)

Merge all properties from all objects passed as arguments onto reciever.

Proteus.apply(receiver, arg1, ..., argN)

Merge enumerable properties from all objects passed as arguments onto receiver, only if they do not exist on receiver.

Proteus.applyAll(receiver, arg1, ..., argN)

Merge all properties from all objects passed as arguments onto receiver, only if they do not exist on receiver.

Proteus Utility Methods

Proteus.slice(list, offset = 0, end = list.length)

Return a portion of the array-like object.

Proteus.aliasMethod(name, [scope])

Return a function that is bound to call another function on the current object, or the supplied one.

Proteus.delegateMethod(obj, name, [args, ...])

Delegate a function call to another object. Additional arguments will be prepended to the function call.

Proteus.applyProto(self, [name], args = [])

Apply a method from the self object's prototype chain.

The name argument is optional if the function you are invoking from, and the one up the prototype chain you wish to invoke is named. e.g:

var Proteus = require("proteus"),
    objA = {
        someMethod: function someMethod () {
            console.log("I'm object A");
        }
    },
    objB = Proteus.create(objA, {
        someMethod: function someMethod () {
            Proteus.applyProto(this, arguments);
            console.log("I'm object B");
        }
    })
;

objB.someMethod();

// => I'm object A
// => I'm object B

Proteus.callProto(self, name, [arg1], [...], [argN])

Call a method name from the self object's prototype chain passing the remaining arguments as arguments.

Note: the name parameter is not optional.

Proteus.Class

Proteus.Class starts off the Proteus inheritance chain (itself is a descendent of Object). From there you can derive new classes from Proteus.Class (or any classes that are derived from Proteus.Class) to develop your class hierarchy.

var MyClass = Proteus.Class.derive({
        // props for MyClass
    }),
    MySubClass = MyClass.derive({
        // props for MySubClass
    })
;

Deriving Inheritance

ProteusClass.derive(props)

As shown above, use the static methods on the core Proteus.Class, or the Constructor functions returned by derive, to derive new subclasses.

In addition, Proteus allows you to define static properties on your subclass' Constructor function. Simply provide a property self in the passed properties for your class, and those will be copied to the Constructor function instead of the Constructor's prototype.

var MyClass = Proteus.Class.derive({
        self: {
            // Static properties for 'MyClass'
            myStaticMethod: function () {
                return true;
            }
        },
        // All other properties are put on the prototype
        instancePropA: "somevalue"
    });

MyClass.myStaticMethod(); // => true
(new MyClass()).instancePropA === "somevalue"; // => true

The Inherited Event

When one class is derived from another, and the superclass possesses a function property named inherited, the function will be called and passed the newly created Constructor function (i.e: the new subclass).

var MyBaseClass = Proteus.Class.derive({
        self: {
            inherited: function (subclass) {
                subclass.superParent = "MyBaseClass";
            }
        }
    }),
    MySubClass = MyBaseClass.derive({/* ... */})
;

MySubClass.superParent === "MyBaseClass"; // => true

ProteusClass.__super__ Property

Every class derived from Proteus.Class has a __super__ property that points to its super class' prototype.

var MyBaseClass = Proteus.Class.derive({
        someMethod: function () {
            // ...
        }
    }),
    MySubClass = MyBaseClass.derive({
        someMethod: function () {
            MySubClass.__super__.someMethod.call(this);
        }
    })
;

Including Instance Functionality

ProteusClass.include(obj1, ..., objN)

You can mix-in functionality from other Constructor function prototype's, or plain objects, into Proteus derived classes with the include method.

If passed a Constructor function, Proteus will copy the function's prototype properties to your Class' prototype. If passed a plain object, it will copy over those properties to the prototype of the class.

// Copy 'OtherClass' prototype properties to 'MyClass' prototype.
MyClass.include(OtherClass);

// Merge the passed object into 'MyClass'
MyClass.include({
    // Additional properties for MyClass.prototype
});

The Included Event

When an object, or Constructor function, is included into a Proteus Class its included function will be called and given the object that it was included into (the Proteus Class Constructor).

var MyModule = Proteus.Class.derive({
        self: {
            decorators: [],
            included: function (includee) {
                includee.decorators = this.decorators.slice();
            }
        }
    }),
    MyClass = Proteus.Class.derive({/* ... */})
;

MyClass.include(MyModule);
MyClass.decorators; // => []

Extending Your Class

ProteusClass.extend(obj)

You can use a ProteusClass's extend method to extend another object with its functionality.

var MyClass = Proteus.class.derive({
        self: {
            someMethod: function () {
                // ...
            }
        }
    });

// elsewhere
MyClass.extend(obj);
typeof MyClass.someMethod; // => "function"

The Extended Event

Instantiation

Report an Issue

License

Copyright (c) 2012 Jerry Hamlet jerry@hamletink.com

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 shall be used for Good, not Evil.

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.

npm loves you