Secrets of the JavaScript Ninja 5章を読んで来た
今回は5章です.
事前にプロトタイプとは?って調べてから読んだ方がいいよ〜とid:cheesepieさんからアドバイスをもらったので,まずこの記事
を読んでプロトタイプとクラスベースはどう違うのか?ということに対するイメージを作ってこことか参照して,jsでnewするとはどういうことか!?を調べてから読み始めてみました.
すると予習のかいあって,最初の方は割とすんなり理解することができました.
以下,予習記事と本章の前半〜中盤を読んで理解したことをメモしておきます.
- jsには厳密にはクラスがない.すべてのオブジェクトはインスタンス化された状態で存在する.
- 関数に対してnewするとインスタンスができあがる.
- jsではprototypeに継承したいオブジェクトのプロパティを設定してnewすることで親オブジェクトからプロパティを継承できる.
- prototypeプロパティに設定した親オブジェクトのプロパティはnewした時点の値が代入される.親オブジェクトのプロパティを後から変更してもインスタンスのプロパティの値は影響を受けない.
- プロトタイプの特徴を利用して,組み込みのオブジェクトのインスタンスをprototypeプロパティに設定してnewすることで,組み込みオブジェクトのメソッドが使えるようになる.
ということで,前半から中盤にかけては,読書会時にはさらっと流す程度でした.
クラスっぽいコードの話
そして最後の最後に難しいのが来ました...
JavaScriptにはクラスはないけど,Base2というライブラリとPrototype.jsからインスピレーションを得たということで,クラスっぽい(traditionalと表現されていました)継承を使ったライブラリのサンプルが紹介されています.
var Person = Object.subClass({ init: function(isDancing){ this.dancing = isDancing; }, dance: function(){ return this.dancing; } }); var Ninja = Person.subClass({ init: function(){ this._super( false ); }, dance: function(){ // Call the inherited version of dance() return this._super(); }, swingSword: function(){ return true; } }); var p = new Person(true); assert( p.dance(), "Method returns the internal true value." ); var n = new Ninja(); assert( n.swingSword(), "Get true, as we expect." ); assert( !n.dance(), "The inverse of the super method's value - false." ); // Should all be true assert( p instanceof Person && n instanceof Ninja && n instanceof Person, "The objects inherit functionality from the correct sources." );
何をしているかというと,まず,ObjectオブジェクトにsubClassというメソッドを持たせて,その中にオブジェクトの形式でメソッドを渡してnewすることで,Objectオブジェクトを継承したPersonクラスを作ります.
その後,Personクラスを継承したNinjaクラスを作ります.親オブジェクトのメソッドは同名のメソッドを定義して
this._super( arg )
で呼び出す事ができます.このような動作を実現するためのライブラリが↓の実装です.
解読が難しかったです... id:cheesepieさんはスラスラ解読してましたが!
// Inspired by base2 and Prototype (function(){ var initializing = false, // Determine if functions can be serialized fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; // Create a new Class that inherits from this class Object.subClass = function(prop) { var _super = this.prototype; // Instantiate a base class (but only create the instance, // don't run the init constructor) initializing = true; var proto = new this(); initializing = false; // Copy the properties over onto the new prototype for (var name in prop) { // Check if we're overwriting an existing function proto[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-class this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } // The dummy class constructor function Class() { // All construction is actually done in the init method if ( !initializing && this.init ) this.init.apply(this, arguments); } // Populate our constructed prototype object Class.prototype = proto; // Enforce the constructor to be what we expect Class.constructor = Class; // And make this class extendable Class.subClass = arguments.callee; return Class; }; })();
このライブラリを読み解く上でつまづいたところ
- Classメソッドの意味
- ifの条件でinitializingフラグでnew this()したときにinitメソッドが実行されないようにする役割
- fnTestの意味
- tmpという変数の存在意義?
- もしオブジェクトのプロパティに_superというプロパティがあった場合,このライブラリでやろうとしているthis._super上書きされてしまうので,それを防ぐ役割.
次は6章(Timer)です!