🧊

JavaScriptでのライブラリ作成時のデザインパターンは、オブジェクトか関数か

なんてあやふやなタイトルなんや・・。

例えばタブのUIとか、簡単ですがライブラリちっくなものを書く機会が最近多いです。
そんなとき、オブジェクト作って全部やるパターンと、Functionで作ってnewして使うパターンと、結局どっちが良いんやろ?って思ったんです。

あと、他にも思うところがあったので、やっつけ仕事で2パターン書いてみて考察。

というわけでタブのUI

HtmlとCssは共通のものを使います。

Html/Css

<!DOCTYPE HTML>
<html lang="ja-JP">
<head>
<meta charset="UTF-8">
<title>Tab lib</title>
<style>
/* For css */
.my-tab-container .my-tab-header {
	display: -webkit-box;
	list-style: none;
	padding: 0;
	background-color: #eee;
	text-align: center;
}
.my-tab-container .my-tab-header li {
	-webkit-box-flex: 1;
}
.my-tab-container .my-tab-header .js-active {
	background-color: #000;
	color: #fff;
}
.my-tab-container .my-tab-body {
	padding: 0;
}

/* For js */
.js-tab-container .js-tab-header .js-active {
	pointer-events: none;
}
.js-tab-container .js-tab-body li {
	display: none;
}
.js-tab-container .js-tab-body .js-active {
	display: block;
}
</style>
</head>
<body>
<div class="js-tab-container my-tab-container">
	<ul class="js-tab-header my-tab-header">
		<li>Title1</li>
		<li>Title2</li>
		<li>Title3</li>
	</ul>
	<ul class="js-tab-body my-tab-body">
		<li>Text1 is this. JavaScript is really difficult and ...</li>
		<li>Text2 is this. JavaScript is really difficult and ...</li>
		<li>Text3 is this. JavaScript is really difficult and ...</li>
	</ul>
</div>

<script src="//cdnjs.cloudflare.com/ajax/libs/zepto/1.0/zepto.min.js"></script>
<!-- ここでそれぞれjsを読み込んで実行 -->
</body>
</html>

Javascript

まずはそれぞれのソース。
今回はZepto.jsを使ってみた。

Objectパターン

// Act like "tab-obj.js" file.
;(function(global, $, undefined) {
	'use strict';

	var classNames = {
		header: '.js-tab-header',
		body: '.js-tab-body',

		active: 'js-active' // for addClass! Dont prepend ".".
	};


	var Tab = {
		trigger: $(classNames.header).find('li'),
		body: $(classNames.body).find('li'),

		init: function() {
			Tab.showThis(Tab.trigger.eq(0));
			Tab.trigger.on('click', Tab.showThis);
		},

		showThis: function(e) {
			var index = Tab.trigger.index(e.target),
			c = classNames;

			Tab._refresh();

			Tab.trigger.eq(index).addClass(c.active);
			Tab.body.eq(index).addClass(c.active);
		},

		_refresh: function() {
			var c = classNames;
			
			Tab.trigger.removeClass(c.active);
			Tab.body.removeClass(c.active);
		}
	};
	
	global.Tab = Tab;
}(this.self || global, Zepto));

実行する時は、

;(function(global, $, undefined) {
	'use strict';

	global.Tab.init();

}(this.self || global, Zepto));

Functionパターン

// Act like "tab-func.js" file.
;(function(global, $, undefined) {
	'use strict';

	var classNames = {
		header: '.js-tab-header',
		body: '.js-tab-body',

		active: 'js-active' // for addClass! Dont prepend ".".
	};


	var Tab = function($elm) {
		this.init($elm);
	};

	Tab.prototype.init = function($elm) {
		var that = this,
		c = classNames;

		that.trigger = $elm.find(c.header).find('li');
		that.body = $elm.find(c.body).find('li');
		that.trigger.on('click', $.proxy(this.showThis, this));

		that.showThis(that.trigger.eq(0));
	};

	Tab.prototype.showThis = function(e) {
		var that = this,
		c = classNames,
		index = that.trigger.index(e.target);

		that._refresh();

		that.trigger.eq(index).addClass(c.active);
		that.body.eq(index).addClass(c.active);

	};

	Tab.prototype._refresh = function() {
		var that = this,
		c = classNames;

		that.trigger.removeClass(c.active);
		that.body.removeClass(c.active);
	};

	global.Tab = Tab;
}(this.self || global, Zepto));

実行する時は、

;(function(global, $, undefined) {
	'use strict';

	new global.Tab($('.js-tab-container'));

}(this.self || global, Zepto));

結局どっちがいいのか

良いと思ったところを列挙。

Objectパターン

  • 記述もわかりやすいので、実装するのが楽。
  • コードが短い!(本質的には同じでも、印象として)

Functionパターン

  • プロトタイプ活用って省メモリぽい。
  • 拡張しやすい!(1ページにタブが2つ以上ある時とか)
  • クラス名などを引数で渡せるので柔軟性がある。

やっぱり・・・基本的にやれることは同じやと思うので、その時々で使い分けるしかないかしら。

クラス名決め打ちでも問題ないものや、そのページでしか使わんやろ!みたいな汎用性の低いUIについてはObjectパターンで決め打ちで。

それ以外は基本的にFunctionパターンで作れば良いって感じかな・・?