JavaScirptでサンドボックスパターン

JavaScriptパターン本の5章で紹介されているサンドボックスパターンがおもしろかったので試してみました。


これは広く使われている名前空間を使ったモジュールパターンとは違うアプローチになっています。モジュールパターンというのは以下のような書き方で、即時関数が返すオブジェクトをモジュールとして利用するものです。

* Module Pattern
[javascript]
// 名前空間を定義
MYAPP = { utilities: { } };

// …

MYAPP.utilities.array = (function() {
// 依存関係
var uobj = MYAPP.utilities.object,
ulang = MYAPP.utilities.lang,

// プライベートプロパティ
array_string = “[object Array]”,
ops = Object.prototype.toString();

// パブリックAPI
return {
inArray: function(needle, haystack) {
// …
},
isArray: function(a) {
// …
}
};
}());

// 使い方
MYAPP.utilities.array.inArray([‘a’, ‘b’, ‘c’], ‘c’); // => 2
MYAPP.utilities.array.isArray([1, 2, 3]); // => true
[/javascript]
サンドボックスパターンはその名前の通り、モジュールが他のモジュールとそのサンドボックスに影響を与えることなく動作できるような環境を提供します。このパターンは例えば YUI 3.x 以降で利用されていて、以下のように書くことができます。
[javascript]
// node, anim モジュールはコールバック関数の中でのみ利用可能でグローバルを汚さない
YUI().use(‘node’, ‘anim’, function(Y) {
Y.one (‘div.status’).setContent (‘ready!’); // node モジュールを利用
new Y.Anim({ node: ‘#demo’, to: {opacity: 0} }); // anim モジュールを利用
});
[/javascript]
YUIはバージョン3からなかなか便利になってるなぁという印象で、なによりYQLとの連携が大変強力になっているので、そっち方面に興味がある人は使ってみるといいんじゃないでしょうか。

今回はCanvasを使った簡単な画像処理ライブラリのベースをこのサンドボックスパターンを使って書いてみます。画像I/Oを core モジュール、畳み込みフィルタを filter モジュールに実装することにします。使い方としては以下のようにフィルタ適用の処理をコールバック関数内に書くことになります。new は外に出てきません。

* Sandbox Pattern
[javascript]
var context = document.getElementById(‘Canvas’).getContext(‘2d’);
var kernel = [-1, -1, -1,
-1, 8, -1,
-1, -1, -1]; // 畳み込み用のフィルタカーネル
var divisor = 1; // 変換中に使用する除数
var bias = 0; // 加算するバイアス値

// CV: サンドボックス
// core, filter の2つのモジュールを利用
CV([‘core’, ‘filter’], function(self) {
self.load(‘./images/sample.jpg’, context); // 画像を読み込む
var result = self.convolution(kernel, divisor, bias); // 畳み込みフィルタをかける
self.draw(result, context); // 結果をCanvasに描く
});

// モジュールを個々のパラメータとして指定してもOK
CV(‘core’, ‘filter’, function(self) {
// …
});

// ワイルドカード ‘*’ を指定すると利用できる全てのモジュールを読み込む
CV(‘*’, function(self) {
// …
});

// モジュール指定を省略した場合も同様
CV(function(self) {
// …
});
[/javascript]
CV コンストラクタの中身を見る前に、まずはモジュールの追加方法を見てみます。各モジュールを実装する関数は現在のインスタンスである self を引数として受け取ります(名前はなんでもいい、YUI 3だとYという名前に統一されている)。この self をいちいち付けるのは面倒かもしれませんが、Pythonにおける self と同じような感じで扱えばいいかと。モジュール内で浮気者の this を相手にしなくていいというメリットは大きいです。
[javascript]
CV.modules = {};
/*— core モジュール —*/
CV.modules.core = function(self) {
var p = self.constructor.prototype; // 必要なら prototype にアクセス可能

self.load = function(source, context) {
// 画像を読み込む
};
self.draw = function(data, context) {
// Canvasに描く
};
};
/*— filter モジュール —*/
CV.modules.filter = function(self) {
self.convolution = function(kernel, divisor, bias) {
// 畳み込みフィルタをかける
};
};
[/javascript]

最後に CV コンストラクタの中身を見てみます。
[javascript]
function CV() {
var args = Array.prototype.slice.call(arguments), // 引数を配列に
callback = args.pop(), // 最後の引数はコールバック関数
modules = (args[0] && typeof args[0] === “string”) ? args : args[0], // モジュール
i;

// this が CV のインスタンスでなければコンストラクタとして呼ぶ
if(!(this instanceof CV)) {
return new CV(modules, callback);
}

// モジュールの指定がない、または “*” で指定された場合はすべてのモジュールを組み込む
if(!modules || modules[0] === “*”) {
modules = [];
for(i in CV.modules) {
if(CV.modules.hasOwnProperty(i)) {
modules.push(i);
}
}
}

// 必要なモジュールの初期化
for(i=0; i
HTML5 Image Processing (Convolution Filter)
jsdo.it への投稿: Convolution Filter – jsdo.it – Share JavaScript, HTML5 and CSS

フィルタカーネルは3*3のサイズであれば WebWorkers を使う必要はないですが、カーネルサイズあるいは対象画像が大きい場合は使った方が良さそうです。周波数解析や特徴量抽出などを行う時は必須になると思います。

実はこの畳み込みフィルタのデモですが、2009年にCanvasの勉強のために書いたものをサンドボックスパターンで書き直したものになります (参照: HTML5でConvolutionFilter) 。当時はActionScript3.0に近いインタフェースになるように実装することを心掛けていましたが(クラスの真似事)、今はそういう考え方をしなくなりました。同じECMAScriptを親に持つ2つの言語ですが、今では文化がかなり異なってきているので、これからは”JavaScriptらしさ”を大切にしたいと思います。

また、2, 3年前にDouglas Crockford氏がウチのオフィスに来てJavaSciprtのレクチャーをしてくださったことがあります。セキュリティなども含めてJavaScriptの再教育が必要だ、と。確かにjQueryなどのライブラリを使ってAPIをただペタペタ貼ってるだけだと知識は深まらないですね。僕もJavaScriptを丁寧に勉強しなおす必要があると感じました。

* 関連記事
HTML5でSymmetric Nearest Neighbor

あわせて読む:

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です