随分と以前に書いたJavaScriptのライブラリがありまして、それが大域名前空間を汚しまくっているわけです。当時はそれでも不都合はなかったし、罪悪感(?)もありませんでした。ですが、今見ると「これはないよなー」と。さらに悪いことに、実際に名前の衝突を起こしてしまったのです。
それで、このライブラリが定義する名前をすべて単一の名前空間に押し込めるように書き換えようと思ったのですが、けっこうな量があります。ウーン… めんどくさいなー、いじりたくないなー、と。変更は最小限にして、単一の名前空間の下に「名前のお引っ越し」をしたい。
ものすごく安直な方法でなんとかなりました。こんなんでいいんかいな? とも思います*1が、まー、出来たは出来たのでやり方を紹介します。もっとうまい方法があれば教えてください。
例題
次はサンプルです。僕が昔書いたライブラリも、このサンプルと似たような書き方をしてます。__proto__プロパティを使って継承を実現していますが、それの良し悪しとかは今は置いておきます。
/* * point.js * */ function Point(x_, y_) { if (typeof x_ === 'undefined') x_ = 0; if (typeof y_ === 'undefined') y_ = 0; this.x = x_; this.y = y_; } Point.prototype.moveTo = function(newX, newY) { this.x = newX; this.y = newY; }; Point.prototype.moveBy = function(offsetX, offsetY) { this.x = this.x + offsetX; this.y = this.y + offsetY; }; Point.prototype.toString = function() { var s = '(' + this.x + ', ' + this.y + ')'; return s; }; var BLACK = '#000000'; var RED = '#FF0000'; var LIME = '#00FF00'; var BLUE = '#0000FF'; var WHITE = '#FFFFFF'; var ColoredPoint = function(x_, y_, color_) { // call super Point.apply(this, [x_, y_]); if (typeof color_ === 'undefined') color_ = BLACK; this.color = color_; }; ColoredPoint.prototype.__proto__ = Point.prototype; ColoredPoint.__proto__ = Point; ColoredPoint.prototype.setColor = function(color) { this.color = color; }; ColoredPoint.prototype.toString = function() { var s = '(' + this.x + ', ' + this.y + '; ' + this.color + ')'; return s; };
名前のお引っ越しの手順
最初に、ライブラリ(JavaScriptプログラムのソースファイル)が定義している大域的名前を調べます。例題のコードでは、行頭のfunctionとvarが大域的名前を定義しているので、次のようにして大域的名前を列挙できます。
$ grep ^function point.js
function Point(x_, y_) {$ grep ^var point.js
var BLACK = '#000000';
var RED = '#FF0000';
var LIME = '#00FF00';
var BLUE = '#0000FF';
var WHITE = '#FFFFFF';
var ColoredPoint = function(x_, y_, color_) {$
全部で7個の名前ですね。
- Point
- BLACK
- RED
- LIME
- BLUE
- WHITE
- ColoredPoint
ファイルの先頭のほうに1行、ファイルの最後に数行を付け足しますが、ファイルの最後への追加部分で、今調べた名前を使います。
ファイルの先頭のほうへの1行追加:
var point = (function() {
ファイルの最後のほうへの数行追加:
return { Point: Point, BLACK: BLACK, RED: RED, LIME: LIME, BLUE: BLUE, WHITE: WHITE, ColoredPoint: ColoredPoint }; })();
全体は次のようになります。既存のコード部分はまったく変更していません。大域的だった名前は、pointの下に入ります。例えば、ColoredPointは point.ColoredPoint になります。
/* * point-ns.js * */ var point = (function() { function Point(x_, y_) { if (typeof x_ === 'undefined') x_ = 0; if (typeof y_ === 'undefined') y_ = 0; this.x = x_; this.y = y_; } Point.prototype.moveTo = function(newX, newY) { this.x = newX; this.y = newY; }; Point.prototype.moveBy = function(offsetX, offsetY) { this.x = this.x + offsetX; this.y = this.y + offsetY; }; Point.prototype.toString = function() { var s = '(' + this.x + ', ' + this.y + ')'; return s; }; var BLACK = '#000000'; var RED = '#FF0000'; var LIME = '#00FF00'; var BLUE = '#0000FF'; var WHITE = '#FFFFFF'; var ColoredPoint = function(x_, y_, color_) { // call super Point.apply(this, [x_, y_]); if (typeof color_ === 'undefined') color_ = BLACK; this.color = color_; }; ColoredPoint.prototype.__proto__ = Point.prototype; ColoredPoint.__proto__ = Point; ColoredPoint.prototype.setColor = function(color) { this.color = color; }; ColoredPoint.prototype.toString = function() { var s = '(' + this.x + ', ' + this.y + '; ' + this.color + ')'; return s; }; return { Point: Point, BLACK: BLACK, RED: RED, LIME: LIME, BLUE: BLUE, WHITE: WHITE, ColoredPoint: ColoredPoint }; })();
*1:この単純な方法がいつでもうまくいく自信がありません。