Secrets of the JavaScript Ninja 5章を読んで来た

今回は5章です.

事前にプロトタイプとは?って調べてから読んだ方がいいよ〜とid:cheesepieさんからアドバイスをもらったので,まずこの記事
を読んでプロトタイプとクラスベースはどう違うのか?ということに対するイメージを作ってこことか参照して,jsでnewするとはどういうことか!?を調べてから読み始めてみました.

すると予習のかいあって,最初の方は割とすんなり理解することができました.

以下,予習記事と本章の前半〜中盤を読んで理解したことをメモしておきます.

  • jsには厳密にはクラスがない.すべてのオブジェクトはインスタンス化された状態で存在する.
  • 関数に対してnewするとインスタンスができあがる.
    • 関数はコンストラクタとして使うのかどうかをはっきりさせておく.
    • newしないで関数呼び出しをしてその関数にthisがあると,thisがwindowになってしまって,グローバルなオブジェクトに対してプロパティ、メソッド追加になってしまうということ.また,newはreturnの挙動を変える.
    • 関数中にthisがあると,returnで返すのはオブジェクト.returnなくてもオブジェクト.thisがない場合は通常の関数と同じく,returnに記述したものを返し,なかったらundefinedを返す.
  • 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の意味
    • testメソッド正規表現オブジェクトのメソッド
    • 与えられた引数がメソッドかつ「なにかしらの文字 + _super + なにかしらの文字」という形式かどうかを判定する.
    • this._superを実現するため
  • tmpという変数の存在意義?
    • もしオブジェクトのプロパティに_superというプロパティがあった場合,このライブラリでやろうとしているthis._super上書きされてしまうので,それを防ぐ役割.

次は6章(Timer)です!

2010/06/03修正: id:cheesepieさんのidを間違えて書いていたので直しました。スミマセン...