DCL

An elegant OOP with mixins + AOP for JavaScript.

Cheatsheet

This file is written using a literate JavaScript. You can download this cheatsheet to use as a local reference or to run it locally.

If you prefer to see text as inline comments, just click on a sidebar handle at the far right.

While dcl works great in browsers using an AMD loader or even simple <script>, this tutorial is assumed to be run with node.js.

For our examples we will need the main dcl module:

file.js
1
var dcl = require("dcl");

Declaring “classes”.

Declaring properties, constructors and methods with dcl():

file.js
1
2
3
4
5
6
7
8
9
var D = dcl(null, {
  declaredClass: "D",
  constructor: function(a, b, c){
    console.log("something");
  },
  method: function(x, y){
    return x + y;
  }
});

No base class:

file.js
1
var A = dcl(null, {});

Single inheritance:

file.js
1
var B = dcl(A, {});

Mixins:

file.js
1
2
var M = dcl(null, {});
var C = dcl([B, M], {});

Disposable one-off “class”:

file.js
1
var x = new (dcl([B, M], {}))(1, 2, 3);

Supercalls

Make a super call passing through arguments with dcl/superCall():

file.js
1
2
3
4
5
6
7
8
9
10
var E = dcl(D, {
  method: dcl.superCall(function(sup){
    return function(x, y){
      if(sup){
        return sup.apply(this, arguments);
      }
      return 0;
    };
  })
});

Make a super call with different arguments:

file.js
1
2
3
4
5
6
7
8
9
10
var F = dcl(D, {
  method: dcl.superCall(function(sup){
    return function(x, y){
      if(sup){
        return sup.call(this, x + 1, y - 1);
      }
      return 0;
    };
  })
});

Class-level AOP

Advise “before” with dcl.before():

file.js
1
2
3
4
5
var H = dcl(D, {
  method: dcl.before(function(){
    console.log("Called with arguments: ", arguments);
  })
});

Advise “after” with dcl.after():

file.js
1
2
3
4
5
var I = dcl(D, {
  method: dcl.after(function(args, result){
    console.log("Returned result: ", result);
  })
});

Advise “around” with dcl.around():

file.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var J = dcl(D, {
  method: dcl.around(function(sup){
    return function(x, y){
      console.log("Got: ", x, " and ", y);
      if(sup){
        try{
          var result = sup.call(this, x, y);
          console.log("Answered: ", result);
          return result;
        }catch(e){
          console.log("Exception: ", e);
          throw e;
        }
      }
    };
  })
});

Full-blown advising with dcl.advise():

file.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var G = dcl(D, {
  method: dcl.advise({
    before: function(){
      console.log("Called with arguments: ", arguments);
    },
    after: function(args, result){
      console.log("Returned result: ", result);
    },
    around: function(sup){
      return function(x, y){
        console.log("Got: ", x, " and ", y);
        if(sup){
          try{
            var result = sup.call(this, x, y);
            console.log("Answered: ", result);
            return result;
          }catch(e){
            console.log("Exception: ", e);
            throw e;
          }
        }
      };
    }
  })
});

Chaining

Chain after with dcl.chainAfter():

file.js
1
dcl.chainAfter(D, "method1");

Chain before dcl.chainBefore():

file.js
1
dcl.chainBefore(D, "method2");

Usually constructors are chained after, while destructors are chained before. dcl chains constructors automatically by default.

Object-level AOP

We need advise module:

file.js
1
2
3
var advise = require("dcl/advise");

var x = new J;

Advise “before” with advise.before():

file.js
1
2
3
var a1 = advise.before(x, "method", function(){
  console.log("Called with arguments: ", arguments);
});

Advise “after” with advise.after():

file.js
1
2
3
var a2 = advise.before(x, "method", function(args, result){
  console.log("Returned result: ", result);
});

Advise “around” with advise.around():

file.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var a3 = advise.around(x, "method", function(sup){
  return function(x, y){
    console.log("Got: ", x, " and ", y);
    if(sup){
      try{
        var result = sup.call(this, x, y);
        console.log("Answered: ", result);
        return result;
      }catch(e){
        console.log("Exception: ", e);
        throw e;
      }
    }
  };
});

Full-blown advising with advise():

file.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var a4 = advise(x, "method", {
  before: function(){
    console.log("Called with arguments: ", arguments);
  },
  after: function(args, result){
    console.log("Returned result: ", result);
  },
  around: function(sup){
    return function(x, y){
      console.log("Got: ", x, " and ", y);
      if(sup){
        try{
          var result = sup.call(this, x, y);
          console.log("Answered: ", result);
          return result;
        }catch(e){
          console.log("Exception: ", e);
          throw e;
        }
      }
    };
  }
});

Unadvise (in any order):

file.js
1
a2.unadvise();

Inherited

We need inherited module (its value is actually not used):

file.js
1
var inherited = require("dcl/inherited");

Make a super call passing through arguments with inherited():

file.js
1
2
3
4
5
var K = dcl(D, {
  method: function(x, y){
    return this.inherited(arguments);
  }
});

Make a super call with different arguments:

file.js
1
2
3
4
5
var L = dcl(D, {
  method: function(x, y){
    return this.inherited(arguments, [x + 1, y - 1]);
  }
});

Make a super call (works in both strict and non-strict modes):

file.js
1
2
3
4
5
var M = dcl(D, {
  method: function(x, y){
    return this.inherited(M, "method", arguments);
  }
});

Make a super call with getInherited() (works in both strict and non-strict modes):

file.js
1
2
3
4
5
6
7
8
9
var N = dcl(D, {
  method: function(x, y){
    var sup = this.getInherited(N, "method");
    if(sup){
      return sup.call(this, x + 1, y - 1);
    }
    return 0;
  }
});

Debugging

We need debug module (its inclusion adds enhanced error reporting automatically):

file.js
1
2
3
4
5
6
var dclDebug = require("dcl/debug");

var O = dcl(null, {
  declaredClass: "O"
});
var x = new O();

Log a class with dclDebug.log():

file.js
1
dclDebug.log(O);

Log an object:

file.js
1
dclDebug.log(x);

Fork me on GitHub