JavaScriptのプラグインの構造を理解する

DOMの特定の要素を解析して、イベントを付与するたぐいのJavaScriptプラグインは、よく必要になります。
安定したものを自作したかったので、ES2015(ES6)以降の文法で書かれた、軽量なプラグインのコードをいろいろ読んで勉強しました。

書かれていることをだいぶ理解できたので、現時点のスキルで説明できる範囲でまとめます。
なお、Babelを利用すると無理に古い文法を使おうとしなくてもよいようです。

https://qiita.com/mzmz__02/items/e6fbe5e30cc3fd13788f

## 書いてみたプラグイン

要素 `.js-sample` すべてにクリックイベントを付与したり、情報を返す関数を定義するプラグインです。
`sample.init([selector]);` で要素のセレクターを指定して実行することもできます。

<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="KKmLRGp" data-user="webbingstudio" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/webbingstudio/pen/KKmLRGp">
  自動的に要素を調べる or 要素を指定して起動するJavaScriptのライブラリ</a> by Seiko Kuchida (<a href="https://codepen.io/webbingstudio">@webbingstudio</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## 全体の構造

プラグインの中で多かったのがこのパターンです。即時関数で第1引数に定義する関数名、第2引数にthis = Windowオブジェクト、第3引数に実際に定義する関数（ここではクラス）を渡しています。

```javascript
;!function (name, context, definition) {
  'use strict';
  if (typeof module !== 'undefined') module.exports = definition(name, context)
  else if (typeof define === 'function' && typeof define.amd  === 'object') define(definition)
  else context[name] = definition(name, context)
}('sample', this, function(name) {
  class Sample {
      // Sampleクラスの定義
  }
  return new Sample(name);
})
```

即時関数はグローバル変数の汚染を防ぎつつ、再利用しない処理を実行する際に使われています。

https://qiita.com/katsukii/items/cfe9fd968ba0db603b1e

## 先頭のセミコロン

コード先頭の `;` は昔からあるハックで、ひとつ前に定義されたライブラリの末尾にセミコロンが書かれていなかったときに、巻き込まれるのを防ぐため終了扱いにしているのだそうです。

https://www.koikikukan.com/archives/2013/04/09-003300.php

functionの前の `!` については資料が見つからずわかりませんでした。

## 厳格モード

これは以前から知っていて常に書くようにしていました。以下の行を最初に記述すると「厳格モード（strictモード）」となり、曖昧な文法（変数宣言を使用しないなど）を書くとエラーになります。

```javascript
'use strict';
```

## Node.js等の判定

この部分がよくわからなかったのですが、Node.jsやRequireJSを利用しているかを調べているとのことです。

```javascript
if (typeof module !== 'undefined') {
  module.exports = definition(name, context)
} else if (typeof define === 'function' && typeof define.amd  === 'object') {
  define(definition)
} else {
  context[name] = definition(name, context)
}
```

利用していることを証明できるオブジェクトが存在していれば各ツールのモジュールとして実行し、いずれもなければJavaScriptのネイティブの関数として実行しています。
ごく小さなライブラリで、JavaScriptが得意ではない層への配布予定がなければ、ここまで書かなくても良いのかもしれません。


## クラスの定義と実行

Sampleクラスの定義部分は、以下のように書きました。コンストラクタでクラスに `ライブラリの名前 = sample` が渡されているので、それに接頭辞 `'.js-'` を付与して合致したDOMを取得させています。もっとスマートな方法があるのかもしれませんが…

```javascript
class Sample {
  constructor(name) {
    this.className = '.js-' + name;
    this.className = this.className !== '.js-sample' ? name : this.className;
    this.el = document.body.querySelectorAll(this.className);
    // インスタンス作成と同時に実行したいプライベート関数
  }
  プライベート関数名(引数) {
    // 処理
  }
}
```

セレクタを指定してインスタンスを作成する場合のために、newを返すだけのinit関数を作りました。

```javascript
init(target) {
  return new Sample(target)
}
```

即時関数の最後に `.js-sample` を対象とした最初のインスタンスを作成しています。

```javascript
...

  return new Sample(name);
})
```

もしもセレクタを指定した場合は、 `this.className !== '.js-sample'` が `false` となるので接頭辞なしでセレクタをそのまま解析し、インスタンスを作成します。

```javascript
const sample2 = sample.init("#sample2");
```

## void 0

後述の理由で使用していませんが、以下のような条件式を見かけました。

```javascript
if(a === void 0)
```

`void` は必ず `undefined` を返します。昔、クリックしても何もおきないリンクを作成するときに `href="javascript:void(0)"` と書いていた理由がやっとわかりました。
ですがstrictモードで書くと `undefined` が上書きされることはなくなるそうなので、素直に `undefined` と書いてよいようです。

## プラグインの作り方を解説している記事が少ない

当初「プラグイン 作り方」で検索したのですが、有用な記事が見つからず、自力でコードを読むことにした結果、ようやく内容を理解できてきました。今更ですがjQueryも即時関数だったんですね…

JavaScriptの仕様を理解できれば「作り方」を意識しなくても書けるし、必ずしも著名なプラグインと一言一句同じでなくてもよいようです。