• Stars
    star
    169
  • Rank 224,453 (Top 5 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 12 years ago
  • Updated about 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A set of object-oriented tools for JavaScript

dejavu Build Status

NOTE: This project is deprecated and is no longer mantained.

Have you ever had the feeling that you're seeing something you've already seen before? That's the feeling you get when using dejavu.

If you are a developer coming from a language like PHP, Java, ActionScript 3.0, and others, it's likely that you are already familiar with Object Oriented Programming. However, JavaScript uses prototypal inheritance which, although powerful and flexible, can be difficult to understand, and specially to maintain in large projects.

dejavu is a library that delivers classical inheritance on top of JavaScript prototypal inheritance, making it a breeze to move into JavaScript.

Why another?

There are some libraries out there able to shim classical inheritance, however none offers all the functionality that many programmers require.

Also, even though being one of the most feature rich OOP libraries out there, it has an outstanding performance, rivaling with vanilla in production.

Features

  • Classes (concrete, abstract and final)
  • Interfaces
  • Mixins (so you can get some sort of multiple inheritance)
  • Private and protected members
  • Static members
  • Constants
  • Context binding for functions
  • Method signature checks
  • Possible to extend or borrow from vanilla classes
  • Custom instanceOf with support for Interfaces
  • Two builds, regular and AMD based
    • AMD optimized for speeding up developer workflow, allowing testing without the need to re-compile everything into a single file
    • regular if you are not using AMD in your projects
  • Two modes for each build, strict and loose
    • strict best in development, enforcing a lot of checks, making sure you don't make many typical mistakes
    • loose best for production, without checks, improving performance

Users are encouraged to declare 'use strict' while using the dejavu in strict mode, otherwise some code might fail silently. This can happen because dejavu uses Object.freeze and Object.seal to lock classes and instances, guaranteeing that no one changes the behaviour of your classes by replacing methods, etc, and possibly breaking your code, making it really hard to pin point what's wrong. Although this is the default behaviour, it can be changed.

You will read more on it later in this document.

Do not confuse 'use strict' with the dejavu strict mode.

Getting started

The quickest way to start using dejavu in your project, is by simply including dist/regular/strict/dejavu.js (note that this is in strict mode).

If you're developing a client-side app, simply put the file in some folder, and include it in the HTML:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type"
              content="text/html; charset=UTF-8">
        <script type="text/javascript" src="dejavu.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            'use strict';

            // declare the "Person" class
            var Person = dejavu.Class.declare({
                _name: null,

                initialize: function(name) {
                    this.setName(name);
                },

                setName: function(name) {
                    this._name = name;

                    return this;
                },

                getName: function() {
                    return this._name;
                }
            });

            // create a new instance of person
            var indigo = new Person('Marco');
            console.log('A new indigo was born,', indigo.getName());
        </script>
    </body>
</html>

This will make a dejavu global available for you. If you're developing in Node.js, install it with npm install dejavu and use it like so:

var dejavu = require('dejavu');

// declare the "Person" class
var Person = dejavu.Class.declare({
    _name: null,

    initialize: function(name) {
        this.setName(name);
    },

    setName: function(name) {
        this._name = name;

        return this;
    },

    getName: function() {
        return this._name;
    }
});

// create a new instance of person
var indigo = new Person("Marco");
console.log("A new indigo was born,", indigo.getName());

The installation will create a .dejavurc in your package, where you can enable/disable the strict mode as well as change other dejavu options. By default the strict mode is used. Still, you want to leverage your package with the loose mode. Because dejavu reads.dejavurc from the process.cwd(), packages that require your package will be running the loose mode unless they also define a .dejavurc (which will only happen if they also depend on dejavu directly).

Performance

Since all those nice features and common rules of classic OOP degrade performance, dejavu has two separates modes, for different stages in the development.

The strict mode is suitable for development, and will do all sorts of checks, throwing an error when you try to do something considered illegal.

Note that if your project works in strict mode, it will work in loose mode.

As for the loose mode, there is no overhead associated with checks, thus making it suitable for production, since it will be more efficient and have a lower memory footprint and filesize.

Finally, in order to achieve that extra edge, that puts dejavu next to vanilla JS in terms of performance, you should run the optimizer that is bundled with the library. Note that this step is completely optional, and dejavu will still perform faster than most libraries, even if you don't run the optimizer. The optimizer will analyse your code and make some improvements, boosting it a bit further.

You can check the benchmarks in jsperf comparing dejavu with other OOP libraries. Note that the loose mode is used in this test, simulating a production environment, and both the normal and optimized versions are tested. It is also important to mention that many libraries like JSFace does not chain prototypes. This gives JSFace an extra edge in performance in some browsers, like Firefox, but renders the instanceof operator useless.

Syntax

Overview

Here's an overview of what most developers look for in an OOP library. You can find complete examples further down.

var Person = Class.declare({
    // although not mandatory, it's really useful to identify
    // the class name, which simplifies debugging
    $name: 'Person',

    // this is a protected property, which is identified by
    // the single underscore. two underscores denotes a
    // private property, and no underscore stands for public
    _name: null,
    __pinCode: null,

    // class constructor
    initialize: function (name, pinCode) {
        this._name = name;
        this.__pinCode = pinCode;

        // note that we're binding to the current instance with `$bind`.
        // this will be explained with great detail later on
        setTimeout(this._logName.$bind(this), 1000);
    },

    // public method (follows the same visibility logic, in this case with no underscore)
    getName: function () {
        return this._name;
    }

    _logName: function () {
        console.log(this._name);
    }
});

Complete example

For those looking for something more, here's a more complete usage of dejavu.

This example illustrates the usage of:

  • $name meta attribute
  • this.$self vs this.$static
  • $super() for accessing overridden methods
  • instanceOf
  • member visibility
  • statics, abstracts, abstract statics, finals, final statics and constants
  • $extends vs $borrows

In this case, and keep in mind that this is just for illustration purposes, we'll create three interfaces, that are implemented by an abstract class, that is then extended by a concrete class.

var dejavu = require('dejavu');

// ------------ AN INTERFACE ------------
// this interface is useless, is only here to illustrate
// that interfaces can extend other interfaces
var UselessInterface = dejavu.Interface.declare({
    $name: 'UselessInterface'
});

var PersonInterface = dejavu.Interface.declare({
    $name: 'PersonInterface',
    // if you need to extend multiple interfaces,
    // just provide an array
    $extends: UselessInterface,

    // interface methods can specify argument list, and any class
    // that implements that interface will be automatically checked,
    // to make sure it obeys the method signature. If you want to
    // specify an optional argument, you should prepend it by a dollar
    // sign, like so: someMethod(arg1, arg2, $thisArgIsOptional)
    getName: function () {},
    setName: function (name) {}
});

// ------------ ANOTHER INTERFACE ------------
var EngineerInterface = dejavu.Interface.declare({
    $name: 'EngineerInterface',

    think: function(subject) {}
});

// ------------ AN ABSTRACT CLASS ------------
var AbstractIndigo = dejavu.AbstractClass.declare({
    $name: 'AbstractIndigo',
    // implements multiple interfaces
    $implements: [PersonInterface, EngineerInterface],

    $constants: {
        INDIGO_WEBSITE: 'http://www.indigounited.com/',
        INDIGO_EMAIL:   '[email protected]'
    },

    $statics: {
        logIndigoInfo: function () {
            // by using this.$static, we're making sure that dejavu
            // uses late binding to resolve the member. If you're
            // looking for early binding, you can use this.$self
            // instead
            console.log(
                this.$static.INDIGO_WEBSITE,
                this.$static.INDIGO_EMAIL
            );
        }
    },

    // method/attribute visibility is controlled by
    // the number of underscores that the identifier
    // has:
    // public:    no underscores
    // protected: 1 underscore
    // private:   2 underscores
    //
    // the attribute below is protected
    _name: null,

    getName: function () {
        return this._name;
    },

    setName: function (name) {
        this._name = name;

        return this;
    },

    // note that we're not implementing the method `think()` of the
    // EngineerInterface. This will be automatically turned into an
    // abstract method, since we're in an abstract class
    $abstracts: {
        beAwesome: function () {}

        // you can also put "$statics {}" here
        // to create an abstract static method
    },

    // finals are not overridable
    $finals: {
        // you can also put "$statics {}" here
        // to create a final static method

        thisIsFinal: function () {
            console.log('Can\'t change this!');
        }
    }
});

// ------------ A CONCRETE CLASS ------------
// also, if you need this concrete class to be final,
// you can just use dejavu.FinalClass.declare instead
var Indigo = dejavu.Class.declare({
    $name: 'Indigo',
    // class extends another one.
    //
    // in case you need to extend from several classes,
    // you can instead use $borrows, and specify an
    // array of identifiers. Still, note that borrowing
    // will not allow you to perform dejavu.instanceOf
    // tests, as the class is not technically extending
    // the other, just borrowing its behaviour.
    $extends: AbstractIndigo,

    _subject: 'nothing',

    initialize: function (name) {
        // call the parent method, in this case the parent constructor,
        // but can be applied to any method when you need to call the
        // overridden method
        this.$super();

        this.setName(name);

        this._logThought();
    },

    beAwesome: function () {
        console.log(this._name, 'is being awesome!');
        this.$self.logIndigoInfo();
        this.think('the next big thing');
    },

    think: function (subject) {
        this._subject = subject;
    },

    _logThought: function () {
        console.log(this._name, 'is thinking about', this._subject);
    }
});

var indigo = new Indigo('André');
indigo.beAwesome();

// check the type of an object
console.log(
    dejavu.instanceOf(indigo, EngineerInterface) ?
    'we have an engineer!'
    : 'say what now?'
);
console.log(dejavu.instanceOf(indigo, Indigo) ?
    'we have an indigo!'
    : 'say what now?'
);
// native instanceof also works for classes, but not for interfaces
console.log((indigo instanceof Indigo) ?
    'we have an indigo!'
    : 'say what now?'
);

Taking it to another level

Front-end devs are encouraged to program using the AMD paradigm because of its obvious benefits. Since dejavu is built on it, it will integrate seamlessly with your AMD loader.

{
    // your loader config
    paths: {
           'mout': '../vendor/mout/src'
    },
    packages: [
        {
            name: 'dejavu',
            // You can switch to the loose mode anytime
            location: '/path/to/dejavu/dist/strict'
        }
    ]
}

With this setup, you can require the dejavu object or specific parts of it:

// Load dejavu completely
define(['dejavu'], function (dejavu) {

    // the dejavu variable is an object that contains:
    // Class
    // FinalClass
    // AbstractClass
    // Interface
    // instanceOf
    // options

    // example usage
    var Person = dejavu.Class.declare({
        initialize: function () {
            // ...
        }
    });

    return Person;
});

// In this case, only the `Class` module of `dejavu` is included,
// which means all the other modules are not loaded.
define(['dejavu/Class'], function (Class) {

    // Example usage
    var Person = Class.declare({
        initialize: function () {
            // ...
        }
    });

    return MyClass;
});

Additional details

Binding and anonymous members

You will eventually run into a situation where you want to declare a callback that accesses class members. On traditional JavaScript, you would just var that = this or .bind(this), and everything would be ok, because there is no restriction on visibility. Since dejavu enforces this, you will need to mark that callback as a member of the class, using something like the following:

// ...
var that = this;
setTimeout(function () {
    that._someProperty = 'protected properties on callbacks';
}.$member(), 1000);
// ...
setTimeout(function () {
    this._someProperty = 'protected properties on callbacks';
}.$member().bind(this), 1000);

If the $member().bind(this) is too verbose for you, you can just .$bind(this), which is equivalent.

Finally, when defining a method directly on the class declaration that you know will always be used using the class context, you can bind it right there like so:

var MyClass = dejavu.Class.declare({
    $name: 'MyClass',

    doSomething: function () {
        // notice that _someMethod is $bound() below,
        // which is more efficient than $bind()ing on
        // every execution of doSomething()
        setTimeout(this._someMethod, 1000);
    },

    _someMethod: function () {
        console.log('method efficiently bound');
    }.$bound()
});

Classes/instances are locked

By default, constructors and instances are locked. This means that no one can monkey patch your code.

This behaviour can be changed in two ways:

With the $locked flag:

var UnlockedIndigo = Class.declare({
    $name: 'UnlockedIndigo',
    $locked: false

    initialize: function () {
        // Altough the foo property is not declared,
        // it will not throw an error

        this.name = 'Filipe';
    },

    talk: function () {
        console.log('An indigo is talking!');
    }
});

Members can be added, replaced and deleted from the prototype:

UnlockedIndigo.prototype.age = 20;
UnlockedIndigo.prototype.talk = function () {
    console.log('... now is running');
};

Members can be added, replaced and deleted from the instance:

var filipe     = new UnlockedIndigo();
filipe.friends = ['Marco','Andre'];
filipe.talk    = function () {
    console.log('I am talking about dejavu!');
};

By setting the global option:

This will change the default behaviour, but classes can still override it with the $locked flag.

dejavu.options.locked = false;

Note that once a class is unlocked, its subclasses cannot be locked. Also, although undeclared members are allowed, they will not have their access controlled (they are interpreted as public).

Vanilla classes

dejavu allows you to extend or borrow vanilla classes. In this case, constructors and instances are forcibly UNLOCKED.

function Person(name) {
    this.name = name;
};

var Engineer = dejavu.Class.declare({
    $extends: Person
});

var filipe = new Engineer('Filipe');
// Engineer class and filipe instance are unlocked

Debugging

In strict mode, logging instances with console.log will print some strange stuff (getters, setters, etc). This happens because dejavu manages accesses to private/protected members as well as make other stuff work. To get around this issue, dejavu provides a console.inspect method that works just like console.log but prints a friendly object representation of the instance.

Works on

  • IE (6+)
  • Chrome (4+)
  • Safari (3+)
  • Firefox (3.6+)
  • Opera (9+)
  • Node.js and Rhino

Dependencies

dejavu depends on mout. If you use the regular build, you don't need to worry because all functions used from mout are bundled for you. If you use the AMD build, learn how to setup your loader. If you use dejavu on node, npm will take care of fetching everything for you.

Building dejavu

Simply run npm install to install all the tools needed. Then just run npm run-script build.

Testing dejavu

dejavu has more than 250 tests. Simply run npm install to install all the tools needed. Then just run npm test to execute them.

License

Released under the MIT License.

More Repositories

1

node-promise-retry

Retries a function that returns a promise, leveraging the power of the retry module.
JavaScript
290
star
2

automaton

Task automation tool built in JavaScript
JavaScript
215
star
3

node-request-progress

Tracks the download progress of a request made with mikeal/request
JavaScript
212
star
4

node-cross-spawn-async

A cross platform solution to node's spawn
JavaScript
66
star
5

spoonjs

Improve your cooking and become a true web app chef
JavaScript
26
star
6

node-planify

Plan a series of steps and display the output in a beautiful way
JavaScript
23
star
7

node-request-replay

Replays a request when a network error occurs using the retry module
JavaScript
22
star
8

jquery.simplemarquee

A jQuery plugin that aims to provide a scrolling marquee similar to the good old winamp player
JavaScript
21
star
9

js-deep-sort-object

Simple module to sort objects recursively by its keys
JavaScript
20
star
10

js-err-code

Create error instances with a code
JavaScript
19
star
11

js-deep-filter

Recursively filters collections (arrays and objects)
JavaScript
17
star
12

node-diff-json-structure

Get the structural diff of two JSON objects
JavaScript
15
star
13

node-1

Distributed pub/sub based in ØMQ
JavaScript
13
star
14

node-p-throttler

A promise based throttler capable of limiting execution of parallel tasks
JavaScript
12
star
15

node-couchnode

Sane Couchbase bucket interface for handling common operations the right way.
JavaScript
11
star
16

node-buffered-spawn

Buffered child_process#spawn
JavaScript
10
star
17

js-promtie

Neatly dress up your native promises with simple but powerful utils
JavaScript
9
star
18

js-address

Deal with URL manipulation crossbrowser (html5/hash)
JavaScript
7
star
19

node-level-atomics

Atomic operators for LevelDB
JavaScript
7
star
20

node-happening

Distributed network-based event emitter for NodeJS
JavaScript
6
star
21

node-webkit-builds

Module that keeps a listing of all the node-webkit builds and respective download links
JavaScript
6
star
22

node-detect-readme-badges

Scans a repository's readme file, searching for badges
JavaScript
5
star
23

js-deep-compact

Recursively compacts collection values (arrays and objects)
JavaScript
4
star
24

js-events-emitter

Simple library that allows to listen and emit events
JavaScript
4
star
25

js-proper-on-transition-end

Cross-browser transitionend event listener
JavaScript
4
star
26

node-gloth

Multiple glob expansions with exclusions and hooks
JavaScript
4
star
27

node-detect-repo-linters

Scans a repository directory, searching for configured linters
JavaScript
3
star
28

js-weighted-mean

Calculates the weighted mean of an array of numbers
JavaScript
3
star
29

jquery.destroy-event

Adds support for "destroy" event on elements
JavaScript
3
star
30

spoonjs-cli

SpoonJS CLI tool
JavaScript
3
star
31

node-is-link-working

Checks if a given hypermedia link is working or broken (2xx).
JavaScript
3
star
32

js-md-highlight

A markdown syntax highlighter
JavaScript
2
star
33

node-swift-transform

Parallelized transform streams for everyone!
JavaScript
2
star
34

js-dom-responder

Observe and respond to DOM events efficiently
JavaScript
2
star
35

node-pretty-inspect

Easy object inspection
JavaScript
2
star
36

node-couchdb-iterator

A fast and easy to ease CouchDB iterator for views and all documents
JavaScript
2
star
37

spoonjs-sample-app

A sample app made in SpoonJS
JavaScript
2
star
38

jquery.coverscroll

Improves scroll performance by adding a cover layer to prevent hover styles from triggering
HTML
2
star
39

node-couchdb-force

Update documents in CouchDB without having to fetch them
JavaScript
2
star
40

jquery.event.fastfix

Drop-in reflow-friendly replacement for jquery.event.fix
JavaScript
2
star
41

node-dejavu-optimizer

Make dejavu usages faster
JavaScript
1
star
42

opojs.com

Official website repository
CSS
1
star
43

node-detect-repo-changelog

Scans a repository directory, searching for a changelog file
JavaScript
1
star
44

pino-err-serializer

An improved error serializer that not only logs the error type, message and stack but also other properties set on the error object
JavaScript
1
star
45

node-screpto

Small utility to run a script across several repositories
JavaScript
1
star
46

spoonjs-www

SpoonJS website
JavaScript
1
star
47

node-ubiq

Real-time RPC library, bring your backend and frontend closer
JavaScript
1
star
48

node-tabular

Simple module that allows you to create fluid lists in which the first column adapts to the content
JavaScript
1
star