jQuery
この文章はjQuery 2.1.4を元に書かれています。
jQueryでは$.fn
を拡張することで、$()
の返り値となるjQueryオブジェクトにメソッドを追加できます。
次のgreenify
プラグインでは、$(document.body).greenify();
というメソッド呼び出しが可能になります。
(function ($) {
$.fn.greenify = function () {
this.css("color", "green");
return this;
};
})(jQuery);
実際に利用する際は、jquery.js
を読み込んだ後にgreenify.js
を読み込ませる必要があります。
<script src="jquery.js"></script>
<script src="greenify.js"></script>
どのような仕組み?
このjQueryプラグインがどのような仕組みで動いているのかを見てみましょう。
jQueryプラグインはprototype拡張のように $.fn.greenify = function (){}
と拡張するルールでした。
jQuery.fn
の実装を見てみると、実態はjQuery.prototype
なので、prototype拡張していることがわかります。
// https://github.com/jquery/jquery/blob/2.1.4/src/core.js#L39
jQuery.fn = jQuery.prototype = {
// prototypeの実装
};
$()
は内部的にnew
をしてjQueryオブジェクトを返すので、このjQueryオブジェクトではprototypeに拡張したメソッドが利用できます。
$(document.body); // 返り値はjQueryのインスタンス
つまり、jQueryプラグインはJavaScriptのprototypeをそのまま利用しているだけに過ぎないということがわかります。
どのような用途に向いている?
jQueryプラグインの仕組みがわかったのでどのような用途に有効な仕組みなのか考えてみましょう。
単純なprototype拡張なので、利点はJavaScriptのprototypeと同様です。動的にメソッドを追加するだけではなく、既存の実装を上書きするmonkey patchのようなものもプラグインとして追加できます。
どのような用途に向いていない?
これもJavaScriptのprototypeと同様で、prototypeによる拡張は柔軟すぎるため、 jQuery自体がプラグインのコントロールをすることは難しいです。
また、プラグインが拡張するjQueryの実装に依存しやすいため、 jQueryのバージョンによって動かなくなるプラグインが発生しやすいです。
jQueryではドキュメント化されてないAPIを触っていけないというルールを設けていますが、これは必ずしも守られているわけではありません。
実装してみよう
calculator
という拡張可能な計算機をjQuery Pluginと同じ方法で作ってみたいと思います。
calculator
は次のような形となります。
function calculator(value = 0) {
if (!(this instanceof calculator)) {
return new calculator(value);
}
this.value = value;
}
// alias
calculator.fn = calculator.prototype;
export default calculator;
$.fn
と同様にprototype
へのaliasを貼っているだけのただのコンストラクタ関数です。
calculator.fn = calculator.prototype;
calculator(初期値)
と書けるようにしているため、少し特殊なコンストラクタとなっていますが、この拡張の仕組みとは関係ないのでとりあえず置いておきましょう。
calculator.jsには何も実装が入ってないので、プラグインで四則演算の実装を追加してみます。
import calculator from "./calculator";
calculator.fn.add = function (x) {
this.value += x;
return this;
};
calculator.fn.sub = function (x) {
this.value -= x;
return this;
};
calculator.fn.multi = function (x) {
this.value *= x;
return this;
};
calculator.fn.div = function (x) {
this.value /= x;
return this;
};
calculator-plugin.jsでは、calculator.fn.add
というようにadd
というメソッドを追加しています。
また、モジュールで依存関係を示していますがやっていることはjQueryと同じで、calculator.jsを読み込んでからcalculator-plugin.jsを読み込んでいるだけですね。
<script src="calculator.js"></script>
<script src="calculator-plugin.js"></script>
これを使うとcalculator#add
といったメソッドが利用できるようになるので、次のように書くことができます。
import assert from "assert";
import calculator from "./calculator";
import "./calculator-plugin"; // Extend
const resultValue = calculator(0).add(10).multi(10).value;
assert.equal(resultValue, 10 * 10);
実装をみてもらうと分かりますが、JavaScriptのprototype
の仕組みをそのまま利用しています。そのため、「拡張する時はcalculator.prototype
の代わりにcalculator.fn
を拡張する」というルールがあるだけともいえます。
エコシステム
このプラグインの仕組みはあるグローバルオブジェクトに依存しており、これはスクリプトを<script>
要素で読み込むだけで拡張することを前提とした作りです。
<script src="jquery.js"></script>
<script src="greenify.js"></script>
Node.jsで使われているCommonJSやES6 Modulesなどがなかった時代に作られた仕組みなので、それらと組み合わせる際には少し不向きな拡張の仕組みといえるかもしれません。
まとめ
ここではjQueryのプラグインアーキテクチャについて学びました。
- jQueryプラグインは
jQuery.fn
を拡張する jQuery.fn
はjQuery.prototype
と同じである- jQueryプラグインとは
jQuery.prototype
を拡張したものといえる - 何でもできるためプラグインが行うことを制御することは難しい