Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

os'ing js style guide #9

Merged
merged 5 commits into from
Oct 18, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions styleguides/JavaScript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Welcome to the Obvious JavaScript Style Guide!

Enclosed you'll find stylistic rules and guidelines for JavaScript authors contributing code back to the Obvious Corporation.

This styleguide may be reused or repurposed for your own projects under the Obvious [Apache 2.0 License](https://github.com/Obvious/open-source/blob/master/apache-license-2.0.md). For those wishing to contribute to an Obvious Corp JavaScript project, this is essentially *highly recommended reading* material before you begin submitting code back.

* [Style Rules](./JavaScript/style-rules.md)
* [Language Rules](./JavaScript/language-rules.md)
* [JavaScript Inheritance](./JavaScript/inheritance.md)
* [Closure Library](./JavaScript/closure-library.md)

**Note:** With much thanks and gratitude, portions of this style guide have been borrowed from the [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml).
49 changes: 49 additions & 0 deletions styleguides/JavaScript/closure-library.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Closure Usage #

We use [Plovr](http://plovr.com/docs.html) to compile and compress our client-side JavaScript. It
comes bundled with a copy of the (Closure Library)[closure-library.googlecode.com] which we can
`goog.require` seemlessly from anywhere.

Even though we can include any piece of Closure in Medium, we should not, since some of the classes
are very heavy, primarily due to their flexibility and to support for older browsers (e.g. IE6).

As well as anything in [base.js](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.html)
the following classes are approved:

* `goog.Disposable`
* `goog.array`
* `goog.asserts`
* `goog.async.Deferred`
* `goog.crypt`
* `goog.events.KeyCodes`
* `goog.labs.net.xhr`
* `goog.net.XmlHttp`
* `goog.math`
* `goog.object`
* `goog.pubsub.PubSub`
* `goog.string`
* `goog.structs`
* `goog.uri.util`
* `goog.userAgent`
* `goog.testing`

New classes should be approved on a case by case basis

## goog.Disposable ##

The correct usage of `dispose` is to override `disposeInternal()` and to always call the base-class. The reasons is that `dispose()` checks whether the object is already disposed before calling `disposeInternal()`, this can help avoid NPEs and such. If you implement `dispose()` yourself you'd want to assert that you weren't disposed. It is very important to call the super class, particularly in the case of screens, as there is a lot of clean-up done higher up that will avoid memory leaks.

```js
MyClass.prototype.disposeInternal = function () {
unlisten(this.listenerRef)
goog.base(this, 'disposeInternal')
}
```

In turn, you can call dispose on instances you have ownership over.

``` js
MyClass.prototype.abort = function () {
goog.dispose(myDisposableClass)
}
```
142 changes: 142 additions & 0 deletions styleguides/JavaScript/inheritance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# JS Inheritance #

**Obvious JS should use Node's inheritance functions on the server and Closure's inheritance
functions on the client.**

What follows is an explanation of how prototypical inheritance works in JavaScript, how Node and
Closure handle inheritance, and a discussion of alternative approaches.

## Background ##

JavaScript includes a prototype based inheritance model as a language feature. This is rather
powerful, though most people restrict themselves and follow a more traditional class-based
inheritance approach. For example, when was the last time you saw an object change its behavior?

```js
function Car() {}
Car.prototype.move = function (x, y) {
alert('Just driving along in 2D')
}

function Plane() {}
Plane.prototype.move = function (x, y, z) {
alert('I can fly!!')
}

var batMobile = new Car()
batMobile.move(1, 2)

batMobile.__proto__ = new Plane()
batMobile.move(2, 3, 10)
```

Traditional class-based inheritance leads to systems where the taxonomy and relationships between
different entities are easier to understand. Code is also easier to reason about; if, at any point,
you can say “The Bat Mobile is a car” you don't have to worry about checking it's type before
calling operations on it (though Duck-Typing can be useful in some situations).

## Native Inheritance ##

If you wanted to make use of prototypical inheritance directly you need to jump through a few hoops.
The simplest way to define a "class" hierarchy is as so:

```js
function BatMobile() {}
BatMobile.prototype = new Car()
```

This is all very well, but what happens if `Car`'s constructor has non-trivial, instance specific
logic in? Yes, the BatMobile could get messed up. So to get around this we'd need to create a
temporary constructor:

```js
function BatMobile() {}
function TempClass() {}
TempClass.prototype = Car.prototype
BatMobile.prototype = new TempClass()
```

This sets up a prototype chain without executing `Car`'s constructor.

In order to call a super-classes implementation of a function you can use `.call` or `.apply` to
execute the super class's method in the right scope:

```js
BatMobile.prototype.doBreak = function () {
Car.prototype.doBreak.call(this)
this._deployChute()
}
```

The downsides of doing inheritance natively are additional boiler plate when setting up
subclasses, also note how each method calling a super method needs to know the actual super-class.
This can make maintaining or renaming classes annoying.

Luckily for us both Node and Closure have affordances built in to make this process simpler, while
still delegating to native prototypes.


## Node.js ##

Node's `util` package contains an `inherits` function which sets up the prototype chain and adds a
static reference to the constructor. Typical usage is as follows:

```js
function BatMobile(arg) {
Car.call(this, arg)
}
util.inherits(BatMobile, Car)

BatMobile.prototype.go = function () {
BatMobile.super_.go.call(this)
this.fireRockets()
}
```

## Closure ##

Closure has a similar helper, that predates node, but is basically the same. Closure includes a
magical function called `goog.base` which the compiler understands and will optimize.

```js
function BatMobile(arg) {
goog.base(this, arg)
}
goog.inherits(BatMobile, Car)

BatMobile.prototype.go = function () {
goog.base(this, 'go')
this.fireRockets()
}
```

## Other Systems ##

There are a multitude of different class systems for JavaScript. Most of them introduce a DSL that
involves passing object literals to helper methods, though the underlying implementations vary.

Some libraries use a "mixin" approach, which will break the `instanceof` operator. Other libraries
will actually extend the prototype. But unless you are familiar with the library and read the
implementation it can be unclear what is happening.

Often this style works well for small classes, but when you have a large class with hundreds of
lines maintenance can become an issue. For example, if you drop into the file it is not immediately
clear whether the function is being included as a `method` or a `static` or something completely
separate to the class.

While more verbose, seeing `ClassName.prototype.instanceMethod` or `ClassName.staticMethod` is self-
documenting and clear.

Another issue that can arise in systems that do more than delegate to native prototypes is that
classes become incompatible. Sticking close to the language primitives means there is nothing at
all stopping you from doing this:

```js
function Vehicle() {}

function Car() {}
util.inherits(Car, Vehicle)

function BatMobile() {}
goog.inherits(BatMobile, Car)
```
Loading