Kanasan.JS #6 で prototype.js を読んだよ!

あ…ありのまま 今日 起こった事を話すぜ!

『前回参加したときに読み始めたprototype.jsが,次に参加したときには読み終わっていた』

というわけで,#1にいったっきり参加できていなかった,Kanasan.JS prototype.js CodeReading#6に参加してきました.全部で,4221行あるprototype.jsも6回目で,ついに,最後にたどりつきました.

といっても,うちはなかなか参加できて無くて,今回が2回目の参加であるばかりか,10:00開始だったのに起きたら11:00とかというぐだぐだっぷりで,あまり読めてないのですが><.しかし,せっかくなので,今日読んだ部分の後半部分で気になったところのメモを残しておきます.以下,行数はprototype.js 1.6.0.2に対応します.

3843行目あたり
  function getEventID(element) {
    if (element._prototypeEventID) return element._prototypeEventID[0]; # こっちは第一要素
    arguments.callee.id = arguments.callee.id || 1;
    return element._prototypeEventID = [++arguments.callee.id];         # こっちはArray全体
  }

この関数は_prototypeEventIDがすでにキャッシュされていればそれの0番目を返す.設定されていなければ,新しくIDを作ってキャッシュに設定し,それを要素に持ったArrayを返す.キャッシュされた値とキャッシュされてない時の値の形式に対応がとれていないっぽい.

でも,['dummy']も'dummy'もtoStringしたら'dummy'になるので問題になっていないみたいだ.

3902行目あたり
  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }

IEのときだけunloadイベントが発生したときにキャッシュを削除する.IEはページから出るときに,うまくオブジェクトが解放されない場合があるので,明示的に処理してるみたい.どういう時にオブジェクトが解放されないかは,議論されていたけどよくわからなかった.サイ本にのっているらしいので読む.

3907行目あたり
    observe: function(element, eventName, handler) {
      element = $(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

いわゆる,element.observe.prototype.jsはhandlerを渡すとソレをそのままイベントハンドラとして登録するのではなく,wrapしてからイベントハンドラに登録する.3866行目あたりのcreateWrapperがwrappingしていて,handlerに渡ってくるeventオブジェクトをprototype.jsのEventオブジェクトに変換したりしている.

3954行目あたり
    fire: function(element, eventName, memo) {
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      var event;
      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

      event.eventName = eventName;
      event.memo = memo || { };

      if (document.createEvent) {
        element.dispatchEvent(event);
      } else {
        element.fireEvent(event.eventType, event);
      }

      return Event.extend(event);
    }

イベントをむりやり発生させる.fireという名前がかっこいい.dataavilableのあたりがキモっぽいけど,何でこのイベントが必要なのかはよくわからなかった.このへんもサイ本(id:nanto_viさんによると図17-8あたり)にのってるらしい.

4151行目あたり
if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  // 略
  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
     // XPathが使えるときのgetElementsByClassName
  } : function(element, className) {
     // XPathが使えないときのgetElementsByClassName
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

この関数はDEPRECATEDで,もはやメンテされてないコードなんだけど,あまりにひどすぎたのでのっけとく.全部かくとまったくわかんないので,一部略.

なんかわかりににくいのだけど,この一文で,

  • document.getElementsByClassName への関数の設定
  • Elements.Methods への関数の設定

を同時にやってる.なんでわざわざ一文でやりたかったのか理解不能.getElementsByClassNameの実装もひどい感じだけど省略.

1.6.0.3 とのdiff

1.6.0.2が読み終わったあとは,1.6.0.3とのdiffを読みました.めちゃめちゃ苦労して読んだところにコメントが追加されまくっていてショックだったりね.

まとめ

というわけで,prototype.js コードリーディングでした.はじめとおわりしか参加してないので,肝心のAjaxのところとかを読めてないのだけど,重要なEventに関する部分は読めてかなり勉強になりました.javascriptプロみたいな人がまわりにいっぱいいると,わからないところをすぐに聞けて勉強のスピードが速くなっていいですね.むしろわからなかったら,聞かないとだめみたいな雰囲気なので,やりやすい感じです.

今回でprototype.jsのコードリーディングは終わったので,次からどんな展開があるか期待ですね.

てなわけで主催者のKanasan,スタッフのSixeightさん,yaottiさん,(今日は来てなかったけど)37toさんおつかれさまでございました.次は遅刻しないようにがんばります.