Instantly share code, notes, and snippets.

@chrisdickinson /class-style.js Secret
Last active Dec 15, 2015

Embed
What would you like to do?
// whatever I'm exporting comes first.
// in this case it's a class, though lately
// I've tended towards *never* making classes
// publically available as an interface.
module.exports = ClassName
// builtin requires come first.
// they're ordered by line length
// descending.
var path = require('path')
, fs = require('fs')
// userland comes next, again
// ordered by line length desc.
var through = require('through')
, brake = require('brake')
// local modules come next.
// these are usually really good
// candidates for splitting off into
// other packages.
var ParentClass = require('./parent')
// module level constants.
// if they're just enums, I use the following
// pattern, which lets me reorder them easily,
// and add new ones:
var _ = 0
, CONSTANT_JUBILATION = _++
, CONSTANT_AGONY = _++
, CONSTANT_PAIN = _++
function ClassName(arg1, arg2) {
// make sure we can use it without "new":
if(!(this instanceof cons)) {
return new cons(arg1, arg2)
}
// if there's a parent class, call it here:
ParentClass.call(this)
// I try to keep arg assignment in argument order.
// I very rarely do any "real" work in the constructor.
this.arg1 = arg1
this.arg2 = arg2
this._attr = 0
// if an attribute *will* exist on this class,
// but doesn't exist now, I assign it to `null`
// in the constructor. this ensures that all instances
// of my class share the same hidden class, and keeps
// me sane.
this._defined_later = null
// chained assignment is okay for "placeholder" values.
// I try to order the assignment from smallest var name
// to longest var name.
this._many =
this._defined =
this._at_some_point = null
}
// I don't use inherits, inh, utils.inherits, or anything.
// I alias the classname to `cons`, and the prototype to
// `proto`, which lets me rename the class easily.
var cons = ClassName
, proto = cons.prototype = Object.create(ParentClass.prototype)
proto.constructor = cons
// classmethods are attached to `cons`.
// these are pretty rare.
cons.clsmethod = function() {
}
// Yes, I use comma first and no semicolons.
// I switch between python and JS -- which makes adding semicolons
// a big context switch; and I find that comma-first lets me align
// my code in a more clear fashion / helps me spot "extra comma"
// issues sooner.
//
// Tangentially, I also use `snake_case` except when exposing public
// APIs.
// For that reason, most of my public APIs are very laconic -- usually
// one word verbs. My `snake_case` usage can't be an issue if no one can
// see it! :D
proto.method = function(arg, arg2) {
return arg + arg2
}
// my async callbacks functions always take node-style callbacks
// as the last argument, which accept `(err, data)`. the callback
// is *always* called "ready", to indicate that the result is "ready".
// "callback" is too vague for my tastes.
proto.async = function(arg, ready) {
// undefined vars come last.
// if I ever refer to `this` in a child scope,
// I put `self = this` at the top of the function
// and change all references of `this` to `self`. It's
// one or the other!
//
// further, I try to pull all of my `var` statements to the
// top of scope (except for loop var declarations). I initially
// resisted this, but I've found that it helps me avoid accidental
// globals.
var self = this
, stat
fs.stat(arg)
// steps that take place after this event loop turn (or
// that would unnecessarily complicate the code) are placed
// in named functions after an explicit return statement.
// hoisting makes them available to the entire function.
return
function got_stat(err, _stat) {
// if statements *always* have braces.
// prefer returning early to having `if / else` statements.
if(err) {
return ready(err)
}
self.do_something()
// if there are multiple stats, share state through the
// parent scope.
stat = _stat
fs.readFile(arg, got_data)
}
function got_data(err, data) {
if(err) {
return ready(err)
}
return ready(null, {stat: stat, data: data})
}
}
// private-ish methods.
proto._private = function(list) {
// I very nearly always use native `for` loops
// instead of `forEach`, `map`, and friends. I've
// found that using functions in those situations is both
// slower than the native variant, and induces more mental
// overhead when reading it (but that's just me.)
// `len` is always, always, always cached.
for(var i = 0, len = list.length; i < len; ++i) {
// if the loop does something very complicated,
// I borrow the function hoisting trick from above.
i % 2 === 0 ? even() : odd()
}
return result
function even() {
// i is still available!
}
function odd() {
}
}
// actually private methods.
function private_method() {
}
// I almost *never* use `options` objects
// as arguments for my functions.
//
// they remind me of the bad old days of jQuery plugins,
// for one; but a better reason I avoid them is that it
// forces me to consider, up-front, the API contract of the
// function I'm writing. If it's too large, that means it
// should probably be two or more functions. Defaulting to
// writing options-style functions leaves the question open
// -- often until it's too late to avoid having written a
// monolithic mega-function.
function options_func(opts) {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment