追記 2014-12-19
この記事は大変古いので参考にしないでください。クロージャ (closure) で囲むのは今でもやる場面はあります。node.js (io.js) にはCommonJS由来のmoduleがあり、以下の方法は不要です。Webブラウザには未だ安定したmoduleはありません (ReuireJSやBrowserifyなどはわたしは嫌いです)。ES6 moduleが使へるやうになれば、それを使へるとおもひます。
JavaScriptにはライブラリ等のパッケージ化の方法が無いなどといわれているけど、ちゃんとあるし、ちゃんと使われているよ。(お粗末ではあるけどね。)
基本
とりあえずクロージャに纏める。
(function(){ })();で囲む。
(function(){ // useful codes })();
なにが嬉しいか。変数はvarで宣言しておけば、つまりローカル変数で作っておけば、相当無神経なことをしても、ライブラリの外に汚染はない。
勿論速度はわずかに遅くなるはずだ。細心の注意さえ払えばクロージャで纏めなくたってpackage化は出来るが、やはりそれには細心の注意が必要だ。
グローバル変数は最少数にする。
exportするglobal変数は出来る限り少なくする。
(function(){ var a = 0, b = "bbbbbbbbb"; gg = { a: a, b: b, }; })(); alert(gg.a + " : " + gg.b);
何がexportされているのかわかりにくい為、読み易くはないが、aとbという二つのlocal変数を、ggという一つのglobal変数に纏められる。
exportを解り易くする方法。
どう見てもわかりやすいよ、という場合はべつによい。僕のこのまえのNaturalクラスhttp://d.hatena.ne.jp/Kureduki_Maari/20090106/1231221669、クロージャで括る必要はまったくないのにくるんでるのは、臆病さ故――そんな場合をのぞいて。
まず一つ目、クロージャの引数として与える。
c4sey = {}; (function($y){ var a = "にー"; var fb = function(u){return u;}; $y.gg = { a: a, b: fb }; })(c4sey); alert(c4sey.gg.b(c4sey.gg.a));
jQuery pluginで使われる方法。pi.jsでも。
僕はこの方法を使っている。
そういや「引数」って「ひきすう」なんだね。「いんすう」じゃないんだね。でも喋るときは僕は「いんすう」と言い続ける。
次、クロージャのreturn値を代入。
c4sey = {}; c4sey.gg = (function(){ var a = new Array(0,1,2,3,4,5,6,7,8,9); var f = function(a){ return a.map(function(elm){ return Math.pow(elm, 3); }); }; return { a: a, b: f }; })(); alert(c4sey.gg.b(c4sey.gg.a));
どっかで見た。どこかはわすれた。
初期化は一回しかしたくない。
よね。
これはif文でチェックするのが一番でしょう。
try{c4sey;}catch(e){c4sey = {};} (function($y){ if($y.gg) return $y.gg; $y.gg = { a: "くぷくぷ", b: function(s){ return s.split("").join("ー"); } }; })(c4sey); alert(c4sey.b(c4sey.a));
global objectをifで、if(!c4sey) c4sey = {};とやると、ReferenceError: c4sey is not definedとなるので、気を付けましょう。
なんらかのlocal objectの場合――この場合だとc4sey.ggですが――は読み出そうとしてもundefinedを返すだけです。つまり、c4seyが定義されているけどc4sey.ggが無い時は、(c4sey.gg ? true : false);はfalseを返します。undefinedはfalseです。
でもglobal objectだと、ReferenceErrorです。暗黙で宣言してくれたりはしません。だから、try{}catch(){}で捕まえなけりゃなりません。今回はtry{c4sey;}catch(e){c4sey = {};}。
ライブラリ読み込みが無い!
残念でした。読み込みの為のコードを書きましょう。それしかありません。
uupaa.jsやscriptaculousのようにライブラリ読み込みを初めからサポートしているものはいいですが、でなきゃ自分で書くしかありません。
<script src=""></script>を書き足せばいいんです。document.write()でもいいし、document.appendChild()でも構わないでしょう。僕はdocument.write()です。読み込み順を心配しないでいいので。
因みに自分自身がどこのフォルダにいるかを調べるコード。
(function(){ var src = ""; var scripts = document.getElementsByTagName("script"); for(var i=0; i<scripts.length; i++) if(/c4sey\.js$/.test(scripts[i].src)) src = scripts[i].src.slice(0, -8); // lording codes })();
srcの中にフォルダ名が文字列で入るはずです。c4sey.jsと、slice(0, Number)のNumberを変更すれば、使えるはず。
Ajax読み込みとかは、僕がサーバーを使えないので無視。
自分でグローバル変数の名前を決めたくない。
慎重な皆さん、歓迎です。
でもその前に、
window.["ありえない"] = (function(){ return { a: 0, b: "" }; })();
などとすることも可能ですよ。JavaScriptはHashTableでできているので、global objectであるwindowにありえないobject名を入れてもいいんです。a = 0とするのはwondow.a = 0とするのと大体いっしょです。(当然違いはありえますが。)
それでも満足できない方。
臆病な皆さん、大歓迎です。
そうですよね。global名までlibrary作者に決めさせるのは負担が大きすぎますよね!――と、そんな場合。
ユーザーに決めさせちゃえ(きゃはっ)。
そうそう。
<script src="lib/c4sey.js?g=$$yy"></script>
とか書かせればいいのよー。
上記のライブラリ読み込みのコードを応用して。
(function(_scope){ var param = {}; var scripts = document.getElementsByTagName("script"); for(var i=0; i<scripts.length; i++) if(/c4sey\.js/.test(scripts[i].src)){ var _e = /(.*)c4sey\.js\?(.*)/.exec(scripts[i].src); param.src = _e[1]; var _param = _e[2].split("&"); for(var i=0; i<_param.length; i++){ _param[i] = _param[i].split("="); param[_param[i][0]] = _param[i][1]; } } _scope[param.g] = { a: 0, b: "", c: null, d: false, e: undefined }; })(window);
ぎゅっとしてどっかーん!
param.srcに"lib/"、param.gに"$$yy"が入るはず。
あぁ、これを応用すれば、JavaScriptにCGI風にパラメータを渡せますね。