外部からメソッドを実行可能なjQueryプラグインの定義方法

jQueryプラグインの定義方法に関してはすでに多くの記事がありますが、ただただ、**外部からのメソッドの実行**を実現するために、自分なりの考えをまとめました。
基本的な定義方法については触れません。

## 特徴
- メソッドチェーンを持続します。
- プラグイン定義後に、パブリックメソッドを外部から任意に実行できます。
- クラス名などを元に複数の要素に対してプラグインを適用した後で、個々の要素のパブリックメソッドを独立して実行できます。
- プラグインの本体となるクラスもjQueryの名前空間に追加するので、独自に呼び出すことができます。  
  単体テストを実行しやすいと思います。

### 制約
- プラグインを適用する要素に、インスタンスを保存するためのdataと同名のdata属性が存在しないこと。  
  例: jQuery.data()で`my-plugin`という名前でインスタンスを保存したいなら、`data-my-plugin`というdata属性が存在してはならない。


## 構造
### 定義
```js
// npmに登録できるように、CommonJSをサポート
// 詳細: http://blog.npmjs.org/post/112712169830/making-your-jquery-plugin-work-better-with-npm
(function (factory) {
  if(typeof module === 'object' && typeof module.exports === 'object') {
    factory(require('jquery'), window, document);
  } else {
    factory(jQuery, window, document);
  }
}(function($, window, document, undefined) {

// jQueryにプラグインとして追加されるメソッド
$.fn.myPlugin = function(arg1) {
  // サブメソッド実行に備えて、数の分からない引数をまとめて配列にする。
  // 第1引数はサブメソッド名なので、それだけは除外する。
  var subMethodArgs = Array.prototype.slice.call(arguments, 1);

  // メソッドチェーン持続のためにjQueryオブジェクトを返す
  return this.each(function() {
    // プラグインを適用済みなら、保存してあるはずのインスタンスを呼び出す
    var instance = $(this).data('my-plugin');

    if (instance && arg1 in instance && arg1.charAt(0) != '_') {
      // publicなサブメソッドを実行する
      instance[arg1].apply(instance, subMethodArgs);
    } else if (typeof arg1 === 'object' || !arg1) {
      // プラグインを適用し、インスタンスをdataに保存する
      $(this).data('my-plugin', new $.myPlugin(this, arg1));
    } else {
      // エラーとしてコンソールに表示される
      console.error('Sub-method "' +  arg1 + '" does not exist on jQuery.myPlugin');
    }
  });
};

// プラグインの本体となるクラス
$.myPlugin = function(elem, option) {
  // プロパティ
  this.elem = elem;
}

// メソッド
$.extend($.myPlugin.prototype, {
  // public
  method1: function() {
  },
  // private (名前の先頭に"_"をつける)
  _method2: function() {
  }
});

}));
```

### 利用
```js
// 適用
// オプションは第1引数にまとめてオブジェクトで指定する
$('#foo').myPlugin({option1: 'aaa', option2: 'bbb'});

// パブリックメソッドを実行
// 第1引数でサブメソッドを文字列で指定する
// 第2引数以降がサブメソッド自体の引数となる
$('#foo').myPlugin('method1', 'aaa', 'bbb');

// 無効
$('#foo').myPlugin('_method2'); // プライベートメソッド
$('#foo').myPlugin('method3'); // 存在しないメソッド
```

### テストでの利用
```js
// インスタンスを作る
var obj = new $.myPlugin();

// 各メソッドの単体テストができる
obj.method1();
obj._method2();
```

## デモ
実例として、`<a>`要素のhref属性をテキストノードに追加するだけの単純なプラグインを作りました。

- デモ: https://sutara79.github.io/jq-plugin-template/
- ソース: https://github.com/sutara79/jq-plugin-template

## 参考にした記事
- [jquery プラグイン/作成 - Qiita](http://qiita.com/k4zzk/items/11e5dbd53cb93edb8b4d) [![hatena bookmark](http://b.hatena.ne.jp/entry/image/http://qiita.com/k4zzk/items/11e5dbd53cb93edb8b4d)](http://b.hatena.ne.jp/entry/http://qiita.com/k4zzk/items/11e5dbd53cb93edb8b4d)  
  基本から丁寧に解説されています。
- [【jQuery】プラグインの様々な作り方(定義パターン)](http://matometaru.com/jquery-plugin-define-patterns/) [![hatena bookmark](http://b.hatena.ne.jp/entry/image/http://matometaru.com/jquery-plugin-define-patterns/)](http://b.hatena.ne.jp/entry/http://matometaru.com/jquery-plugin-define-patterns/)  
  4番目の方法がとても役に立ちました。
- [jQueryのdata-*属性キャッシュ仕様と対策](https://w3g.jp/blog/jquery-data-attr-cache) [![hatena bookmark](http://b.hatena.ne.jp/entry/image/https://w3g.jp/blog/jquery-data-attr-cache)](http://b.hatena.ne.jp/entry/https://w3g.jp/blog/jquery-data-attr-cache)  
  プラグインの定義方法とは関係ありませんが、この記事も役に立ちました。  
  jQueryのdata()で管理するものとHTMLのdata属性は全く同じはずだ、と勘違いしていました。  
  異なる部分もあるんですね。
- [jQuery プラグインの定義パターンについて調べてみた - Cyokodog :: Diary](http://d.hatena.ne.jp/cyokodog/20091126/define_plugin01) [![hatena bookmark](http://b.hatena.ne.jp/entry/image/http://d.hatena.ne.jp/cyokodog/20091126/define_plugin01)](http://b.hatena.ne.jp/entry/http://d.hatena.ne.jp/cyokodog/20091126/define_plugin01)  
  昔からjQueryプラグインを作ろうとして迷ったときに参考にしていました。  
  今までも「2.7 実装を prototype オブジェクトで定義」の方法を使っていましたが、「jQuery UI ではこのインスタンスを data() メソッドにより対応する要素にもたせることで...」の意味がさっぱり分かりませんでした。  
  今回、上に挙げた記事を読んでようやく理解できました。