2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

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

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

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

書いてみたプラグイン

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

See the Pen 自動的に要素を調べる or 要素を指定して起動するJavaScriptのライブラリ by Seiko Kuchida (@webbingstudio) on CodePen.

全体の構造

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

;!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);
})

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

先頭のセミコロン

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

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

厳格モード

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

'use strict';

Node.js等の判定

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

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を取得させています。もっとスマートな方法があるのかもしれませんが…

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関数を作りました。

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

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

...

  return new Sample(name);
})

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

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

void 0

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

if(a === void 0)

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

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

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

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

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
2
Help us understand the problem. What are the problem?