Skip to content

Instantly share code, notes, and snippets.

@mooz
Created July 24, 2010 05:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mooz/2ac889f4b0276ddf9586 to your computer and use it in GitHub Desktop.
Save mooz/2ac889f4b0276ddf9586 to your computer and use it in GitHub Desktop.
/*
* def.js: Simple Ruby-style inheritance for JavaScript
*
* Copyright (c) 2010 Tobias Schneider
* This script is freely distributable under the terms of the MIT license.
*
* Featuring contributions by
* John-David Dalton
* Dmitry A. Soshnikov
* Devon Govett
*/
(function (global) {
// deferred は三つの顔を持つ.
//
// 1. 直前に呼ばれたクラスの _super, _props を保存しておく, ストレージとしての deferred
// 2. deferred() と呼ぶことによりクラスにメンバを定義する, 関数としての deferred
// 3. deferred.valueOf 関数を暗黙的に呼ぶことにより継承を行う, 値としての deferred
//
// def が呼ばれる度にリセットされるので
// Foo({ ... })
// def("Bar")
// などとしても Foo は Bar に継承されない
var deferred;
// メソッド内で base() のようにして用いる.
// これが呼ばれると, 同じ名前の SuperClass のメソッドが呼ばれる.
// 実際には Klass.prototype._super = base; のようにしてクラスの _super メソッドとなっている.
// 注: この _super と deferred._super, Klass._super を混同しないように!
/* Klass.prototype._super = function base() { ... */
function base() {
// base を呼んだメソッドを取得して caller とする.
var caller = arguments.callee.caller;
// それぞれのメソッドには, そのメソッドが属するクラスへの参照が入った _class というプロパティがある.
// また, クラスには SuperClass への参照が入った _super プロパティがある.
return caller._class._super.prototype[caller._name]
.apply(this, arguments.length ? arguments : caller.arguments);
}
//
function def(context, klassName) {
// if (!klassName) {
// klassName = context;
// context = global;
// }
klassName || (klassName = context, context = global);
// create class on given context (defaults to global object)
// クラス (関数) をコンテキスト内に定義する.
// コンテキストが省略された場合は global オブジェクトが用いられる.
// ex) def("Ninja") => window.Ninja が定義される. (注: ブラウザにおける global オブジェクトは大抵 window
var Klass = context[klassName] = function Klass() {
if (context != this) {
// new Ninja() などとして呼ばれた場合.
// こちらの場合はインスタンスを生成する.
// ここで this はインスタンスの種 { __proto__ : Klass.prototype }
// (http://nanto.asablo.jp/blog/2005/10/24/118564 を見よ!)
print("context != this (:: " + uneval(this));
// コンストラクタ init を呼ぶ (存在すれば」).
return this.init && this.init.apply(this, arguments);
// init は異なるクラスやオブジェクトを返すこともできる.
}
// this が context と一致. 大抵はどちらも global.
// this が global となっているということは, つまり Ninja() などとして呼ばれていると考えられる.
// def("Foo") << Bar({...}) の Bar({...}) がまさにこちら.
// 現在のスーパークラスに自分を設定 (ex. Bar)
// 現在のプロパティへ Bar へ渡された引数を設定
// 後は何もしない. 何も返さない.
deferred._super = Klass;
deferred._props = arguments[0] || { };
};
// make this class extendable
Klass.extend = function extend(source) {
// this は Klass
var prop, target = this.prototype;
for (var key in source) {
if (source.hasOwnProperty(key)) {
prop = target[key] = source[key];
// 関数 (メソッド) の追加や上書きだった場合,
if ('function' == typeof prop) {
// その関数のプロパティとして
// _name : メソッド名
// _class : メソッドの属するクラス
// を設定. これらの情報は _super 呼び出しで使われる.
prop._name = key;
prop._class = this;
}
}
}
return this;
};
// def("Foo") の返り値は deferred.
// def("Foo")({ init: ... }) のような記述を可能にするため, deferred は関数に.
// この関数は定義された Foo にメソッド, プロパティを設定する
deferred = function (props) {
return Klass.extend(props);
};
// dummy subclass
function Subclass() {}
// valueOf is called to setup inheritance from a superclass
// def("Bar") << Foo({ ... })
// みたいなとき, def("Bar") の返り値は deferred.
// そして, deferred に valueOf という関数を定義しておくことで, 上の式実行時には
// 1. def("Bar") という関数呼び出し
// 2. Foo({ ... }) という関数呼び出し
// 3. def("Bar") の返り値 deferred の valueOf メソッド呼び出し
// という一連の処理が行われることとなる.
// Foo({ ... }) という関数呼び出し時にスーパークラスや Foo への引数は deferred へ保存されているので
// そいつを使ってクラス Bar を定義する
deferred.valueOf = function () {
// 保存されていたスーパークラスを取得
var Superclass = deferred._super; // deferred._super = Klass; で保存されていたモノ
// スーパークラスが指定されているかどうか
if (!Superclass) {
// 単に def("Bar") のような場合はここにくる.
// Klass を返す
return Klass;
}
// def("Bar") << Foo({ ... }) のような場合はここにくる
// inherit from superclass
// この二行が謎
Subclass.prototype = Superclass.prototype;
var proto = Klass.prototype = new Subclass;
// reference base and superclass
Klass._class = Klass;
Klass._super = Superclass;
Klass.toString = function () {
return klassName;
};
// constructor プロパティを設定
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Object/constructor
proto.constructor = Klass;
// 上に出てきた _super とは種類が異なる (アレはスーパークラス)
// コレはスーパークラスのメソッドを呼ぶための関数. base 関数.
proto._super = base;
// 最後に関数としての deferred を呼ぶことで Klass を deferred._props により拡張.
// これは def("Bar") << Foo({ ... }) みたいなものがあったときに
// クラス Bar を { ... } で拡張するためのもの.
deferred(deferred._props);
};
return deferred;
}
// expose def to the global object
global.def = def;
}(this));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment