Skip to content

Instantly share code, notes, and snippets.

@joseph
Last active August 29, 2015 13:57
Show Gist options
  • Save joseph/9766958 to your computer and use it in GitHub Desktop.
Save joseph/9766958 to your computer and use it in GitHub Desktop.
JS class pattern [2]
// AMD module definition:
define(function (require) {
// An instantiable class:
var K = function () {
this._initialize();
this._p = {};
};
// A "private" method:
K.prototype._initialize = function () {
}
// A public method:
K.prototype.update = function () {
}
// A constant
K.MEANING_OF_LIFE = 42;
return K;
});
@danshultz
Copy link

One other thing to consider here is that we also now rely on this.method invocations which will require preserving context with a bind method.

example

// AMD module definition:
define('foo', function (require) {

  // An instantiable class:
  var K = function () {
    this.update = utils.bind(this.update, this);
    // the verbose version being... utils.bind('update', 'method2', 'method3', this)
    this._initialize();
  };

  return K;

});

I am much more of a fan of the commonjs pattern and using a pre-processor to wrap the define('thing', function (module, exports, require) {}), the amd pattern works but a good preprocessor is still required, etc. Also, doing define('foo', function (require) { }) with requirejs or other amd loaders have a problem ensuring things are properly loaded, etc.

eg. https://github.com/jrburke/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#sync-require

a little bit of a shorthand to limit verbosity (imho)

var K = function () {
    this._initialize();
    this._p = {};
  };


  K.prototype = {

    // A "private" method:
    _initialize: function () {
    },

    // A public method:
    update: function () {
  }

I do prefer this approach over the later (https://gist.github.com/joseph/9766225) for a number of reasons...

  • Private methods can be tinkered with as necessary in the console and you can cross check properties a bit better when trying to debug things.
  • You can allow a more "protected" level interface
  • You can support inheritance which can be nice but also can be fairly ugly if you want to support a concept of "super". Overall I think inheritance with the thought of super is messy in JS, so I guess this is a negative
  • I think it provides better organization of the code, etc and provides indicators of where things are being invoked. If the method begins with an _ it is private and if it begins with this. you know it's a part of the class you are dealing with.
  • It encourages one to think of smaller "classes", etc but I think that comes with the module definitions also to think in terms of much smaller units of code.
  • In closing, one is still able to have "private" methods within this pattern when using modules, etc. It just involves using a set of functions or constants defined outside of the returned function

@danshultz
Copy link

One final bit of closing that I highly prefer with this pattern, I feel it's easier to grok what is going on and where things live and is actually a bit less levels of indirection...also using var thing = new Thing() explicitly says, I am creating a prototype vs the 'fake' prototype being created of var thing = thing()

Also, I find having to keep track of everything around the following hard to keep track of in my head at times.

K is for Class (and Constants!).
I is for Instance (and Interface!).
P is for Properties (and Private!).

@joseph
Copy link
Author

joseph commented Mar 25, 2014

Well, it is var thing = new Thing() in both cases — that's important. I find that I and P become second nature very quickly — that was definitely true for API and k and p in the old pattern.

I agree that tinkering with private methods in console or via inheritance is great — rarely have we needed truly private methods, and anyway, we have those if we're prepared to pass instance data in as an argument. The "protected" implication of underscore-prefixed methods is reasonably nice (well, gallingly ugly, but nice).

I must admit I don't like your shorthand version because that trailing comma is always a bitch, and especially when you have liberally applied comments to your codebase. Also it's another level of indentation (which is another problem with the first proposal too). And if you are inheriting, you can't use the shorthand, so it's better to have one rule.

I think we can more or less blacklist super — super-free inheritance is still better than our current no-inheritance. If we write small, focused methods it's not a big deal.

Alright, you've swayed me. I'm going to reimplement the Stencil (which is totally changing in the Monocle->Lens transition) in this style then review it.

@joseph
Copy link
Author

joseph commented Mar 25, 2014

Also, you want to read this for getting AMD requires working in a CommonJS way reliably: http://requirejs.org/docs/whyamd.html#sugar

@joseph
Copy link
Author

joseph commented Mar 25, 2014

Oh, I had a confusing typo from my console experiments — see https://gist.github.com/joseph/9766958/revisions. The 'foo' did not belong, it was for testing only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment