-
-
Save mooz/2ac889f4b0276ddf9586 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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