Prototype JavaScript Frameworkを使ってみた感想

当記事では、Prototype JavaScript Frameworkの導入方法や操作方法を解説するとともに、使ってみた感想をつらつらと書いています。





## 1. Prototype JavaScript Frameworkとは

Prototype JavaScript Framework、通称prototype.jsは2005年2月に発表されたJavaScriptライブラリです。

[公式サイト](http://prototypejs.org/)

[公式ドキュメント](http://api.prototypejs.org/)

[GitHub](https://github.com/prototypejs/prototype)

私が知っているかぎり、JavaScriptフレームワークと呼ばれるライブラリのなかでは最も古いものであり、さらに主要なJavaScriptライブラリのなかで最も古いものでもあり、そしてCSS・JavaScript双方の主要なライブラリのなかでも最も古いものです。

なにせ、あのjQueryが初めて登場したのが2006年8月26日ですから、その1年半前にリリースされたことを考えると、その古さがよく分かると思います。





### 1-1. 特徴

まずは公式サイトのトップページにある概要を読んでみましょう。原文は英語ですが、Google翻訳にかけて和訳し、さらに意訳しています。

> Prototype JavaScript Frameworkは、クライアントサイドのプログラミングから複雑さを取り除きます。このライブラリは現実に直面している問題を解決するために構築されており、ブラウザ組み込みのJavaScriptオブジェクトをprototype拡張して便利な機能を追加し、AjaxとDOMの不器用なインターフェイスの周りにエレガントなAPIを提供します。

1文目に、このライブラリが提供するのは何らかの新機能ではなく、既存の処理を組みやすくすることであると書かれています。そして2文目以降では、そのためにブラウザ組み込みオブジェクトを拡張し、AjaxとDOMの操作を容易にするAPIを実装していると書いてあります。

続けて、公式ドキュメントにある概要も読んでみましょう。こちらもGoogle翻訳にかけたうえで意訳しています。

> Prototype JavaScript Frameworkは、動的Webアプリケーションの開発を容易にすることを目的としたJavaScriptフレームワークです。使い慣れたクラスベースのオブジェクト指向プログラミング、Ajaxの広範なサポート、高次のプログラミング構造、および簡単なDOM操作を提供します。

このライブラリを使うことで動的に書き換えるWebアプリ開発が容易になると謳っています。また、そのためにクラスベースのオブジェクト指向プログラミングができるような機能の追加、Ajax関連処理の容易化、高次のプログラミング構造（これがよく分からない）、そして簡単にDOMを操作できるようにすると言っています。

以上の情報をまとめると、このライブラリの特徴が見えてきます。

+ Webコンテンツ、特に動的にWebページを書き換えるWebアプリの開発が容易にする。
+ そのために、組み込みオブジェクトを拡張して便利なメソッドなどを追加する。
+ また、他言語のようにクラスベースのオブジェクト指向プログラミングができるようにする。
+ そしてAjax周辺処理とDOM操作を容易にする。

およそ1年半後に登場するjQueryと似ているところもありますが、Webアプリに言及している点、クラスベースのオブジェクト指向プログラミングに言及している点、この2点は当ライブラリ独自のものです。また、何でもjQueryオブジェクトに包んでしまうjQueryに比べて、組み込みオブジェクトを拡張していくところは決定的に異なります。





## 2. 導入手順

導入手順はいくつかあるようですが、最も簡単なのは公式サイトから最新バージョンを落としてくることでしょう。

[公式サイト ダウンロードページ](http://prototypejs.org/download/)

当記事執筆時点で、最新バージョンはv1.7.3です。リリース日が2015年9月22日なので、5年以上開発が止まっているようですね。

読み込み方法としては、当ライブラリを参照する.jsファイルよりも先に\<script>タグで読み込んでおくタイプの読み込み方法になります。残念ながら、標準ではES Modulesには対応していません。独自にES Modules化しようとしても、組み込みオブジェクトのprototypeをガリガリ書き換える都合上、かなり難しい作業になるでしょう。

依存しているライブラリはなく、単一のファイルで動作します。





## 3. 使用時の注意点

当ライブラリを使うときはアロー関数の使用は避け、function文・function式で関数を定義することを推奨します。主要なAPIのいくつかはFunctionオブジェクトを引数にとって動作するのですが、アロー関数で定義されたFunctionオブジェクトだと正常に動作しない場合があります。





## 4. API紹介

詳細は公式ドキュメントに譲り、当記事ではいくつかのAPIを紹介します。





### 4-1. $$メソッド

\$\$メソッドは、jQueryの$メソッドに相当するものです。document.querySelectorメソッドなどと同様にセレクターを利用して、HTML要素を取得することができます。

ただし、jQueryの$メソッドがjQueryオブジェクトを返すのに対して、こちらは普通のElementオブジェクトを配列に格納して返します。

```javascript
// <p>タグを全部取得します。
const pElements = $$("p");

// 複雑なセレクターにも対応しています。
const pInBody = $$("body > p:nth-of-type(1)");
```





### 4-2. $Fメソッド

$Fメソッドは、引数で指定したidを持つフォーム部品の状態・入力値を返します。

```html
<input id="name">
```

```javascript
$F("name");

// 以下処理に相当します。
document.getElementById("name").value;
```





### 4-3. $Rメソッド

$Rメソッドは、数値や文字の範囲で生成します。他言語でいうところのrangeメソッドや..演算子のようなものですね。

```javascript
// 0, 1, 2, 3と表示されます。
$R(0, 3).each(function (value) {
  console.log(value);
});

// 他言語的には、こういう書き方になるのでしょうか。
$R(0..3).each(function (value) {
  console.log(value);
});

// 文字列にも対応しています。
// a, b, cと表示されます。
$R("a", "c").each(function (value) {
  console.log(value);
});
```





### 4-4. $wメソッド

\$wメソッドは、半角空白区切りで文字列を分割するような挙動をします。ちなみに、$記号に続くwは小文字ですので、大文字のWと間違えないように注意が必要です。

```javascript
// ["Hello", "world."] となります。
const array1 = $w("  Hello     world.        ");

// この処理に相当します。
"  Hello     world.        ".split(" ").filter(function (value) {
  if (value === "") {
    return false;
  } else {
    return true;
  }
});
```





### 4-5. Class.createメソッド

Class.createメソッドは当ライブラリ独自のクラス定義処理を実行します。このライブラリが登場した2005年当時のJavaScriptにはclass文・class式がなく、フロントエンドエンジニアはFunctionオブジェクトのprototypeを拡張してクラス定義用のFunctionオブジェクトを作っていました。

[Google流JavaScriptにおけるクラス定義の実現方法(ES6以前) | yunabe.jp](https://www.yunabe.jp/docs/javascript_class_in_google.html)

当ライブラリには、そのような時代に生まれただけあってか、独自のクラス定義処理を実装しているのです。

第1引数にオブジェクトを指定するのですが、initializeプロパティに指定した関数がインスタンス化時に実行されるコンストラクタとなります。また、他プロパティに指定した関数はメソッドとして参照できるようになります。

```javascript
const Dog = Class.create({
  initialize: function (animaiSound) {
    this.animaiSound = animaiSound;
  },
  call: function () {
    return this.animaiSound;
  }
});

const dog = new Dog("bowwow");
console.log(dog.call()); // bowwow
```

継承する場合もClass.createメソッドを使用します。ただし、第1引数に継承元となるクラス定義を格納した変数を指定します。

```javascript
const Dog = Class.create({
  initialize: function (animaiSound) {
    this.animaiSound = animaiSound;
  },
  call: function () {
    return this.animaiSound;
  }
});

const Doge = Class.create(Dog);

const doge = new Doge("ワンワン");
console.log(doge.call()); // ワンワン
```

サブクラス側で実装したいメソッドなどは、第2引数にオブジェクトとして指定します。

```javascript
const Dog = Class.create({
  initialize: function (animaiSound) {
    this.animaiSound = animaiSound;
  },
  call: function () {
    return this.animaiSound;
  }
});

const Doge = Class.create(Dog, {
  jump: function () {
    return "Wow";
  }
});

const doge = new Doge("ワンワン");
console.log(doge.call()); // ワンワン
console.log(doge.jump()); // Wow
```

もちろん、オーバーライドできます。

```javascript
const Dog = Class.create({
  initialize: function (animaiSound) {
    this.animaiSound = animaiSound;
  },
  call: function () {
    return this.animaiSound;
  }
});

const Doge = Class.create(Dog, {
  call: function () {
    return this.animaiSound + "！";
  }
});

const doge = new Doge("ワンワン");
console.log(doge.call()); // ワンワン！
```

オーバーライドされたメソッドを呼び出すときは、オーバーライドしたメソッドの定義の第1引数に`$super`という名前の引数を定義します。そして、オーバーライドしたメソッド内で当該引数を関数として呼び出します。ただし、インスタンスから呼び出すときは、この特殊な第1引数のことは無視します。





### 4-6. Ajax.Requestオブジェクト

Ajax.Requestオブジェクトをインスタンス化すると非同期通信を実行します。第1引数に通信先、第2引数にイベントリスナーを定義したオブジェクトを指定します。

いくつかのイベントが用意されており、イベント名と対応したプロパティに指定された関数が実行されます。基本的には以下3つのイベントを使うことになるでしょう。なお、onCompleteはonSuccess・onFailureの後に呼び出されます。

```javascript
new Ajax.Request("./example.php", {
  onComplete: function (responce) {
    console.log("通信完了");
  },
  onFailure: function (responce) {
    console.log("通信失敗");
  },
  onSuccess: function (responce) {
    console.log("通信成功");
  }
});
```





### 4-7. Ajax.Updaterオブジェクト

Ajax.Updaterオブジェクトをインスタンス化すると非同期通信を実行し、通信結果を表す文字列を任意のidを持つHTML要素に挿入します。

```html
<p id="result"></p>
```

```javascript
new Ajax.Updater("result", "./example.php");
```





### 4-8. Ajax.PeriodicalUpdaterオブジェクト

Ajax.PeriodicalUpdaterオブジェクトはAjax.Updaterオブジェクトを繰り返し実行します。例えば、初期設定では2秒間隔でサーバーと通信し、その戻り値を任意のHTML要素に挿入します。

```html
<p id="result"></p>
```

```javascript
new Ajax.PeriodicalUpdater("result", "./example.php");
```





### 4-9. Abstract.TimedObserverオブジェクト

Abstract.TimedObserverオブジェクトは一定秒間隔で、任意のidを持つフォーム部品の変更を監視するイベントリスナーを実装することができます。

第1引数に対象のid、第2引数で通知間のクールタイムとなる秒数、第3引数に通知が来たときに実行する処理となる関数を指定します。

document.addEventListerメソッドにinputイベントを指定することで似たようなことはできますが、何らかの変化が生ずるたびに通知されて鬱陶しいという場合があるでしょう。そんなときに便利なオブジェクトです。

```html
<input id="text-box">
```

```javascript
// まずはインスタンスを作成します。
// 引数に指定した関数内の処理は無視してもらってかまいません。
const TimedObserver = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

// イベントリスナーを実装します。
// text-boxというidを持つフォーム部品の状態の変移を監視します。
// ただし、最後に通知してから0.5秒間は、変移しても通知しません。
new TimedObserver("text-box", 0.5, function (element, value) {
  console.log(element.id); // text-box
  console.log(value); // 変化後の値
});
```





## 5. 使ってみた感想

組み込みオブジェクトの積極的な拡張・DOM操作の容易化・Ajax操作の容易化・独自のクラス処理・フォーム部品の状態監視などなど、とにかく色々なことをやっているライブラリという印象を持ちました。Prototype JavaScript Frameworkという名前どおり、既存のプログラムに後付けするライブラリというよりは、処理全体の土台となるフレームワークと呼ぶに相応しいでしょう。

また、Classオブジェクトを使って、プロトタイプベースのJavaScriptをクラスベースのオブジェクト指向言語っぽく表現しようとしているあたりに、開発者の（当時の）JavaScriptに対する思いが見え隠れしている気もします。

ただ、さすがに登場時期が時期だけあって、ES2015以後のフロントエンド開発環境が一般的となった2021年現在では、jQueryと同様に「わざわざ採用する必要あるかな？」と疑問を禁じ得ません。jQueryと違い、プラグインが豊富というわけでもなく、コミュニティも小さく、5年以上開発が進捗していない点などからも、このライブラリを採用するのは厳しいです。

強いて挙げるとすれば、このライブラリはIE6以降に対応しています――つまり、おそらく大半のブラウザで動作します。IE11などのレガシーなブラウザへの対応をしなければならない案件で、クラスベースのオブジェクト指向プログラミングを行いたいという場合は、もしかすると候補に入ってくるかもしれません。あるいは、フォーム部品がたくさんあって、しかも非同期通信による動的な書き換えを頻繁に実施する必要があるようなWebアプリとは相性が良いでしょう。