Javascriptのstring型とboolean型、oneOfメソッド他
Javascriptの特殊演算子inをPython風に利用する (agenda)のTupleクラスはあまりにも無駄が多く、潜在的な用途に対して実際に可能な用途が無意味に限られてしまうので、Object.prototype.oneOf(arg, arg2..)
で「厳密な」比較を行なえるようにと考えてみた。
しかしまだ型だのオブジェクトだののJavascriptの基本部分で混乱することがあるとは。Javascriptの文字列リテラルはstring型なのに、Stringインスタンスであるかのようにプロパティアクセスが可能だ。
型なのにプロパティ
"文字列".length;
"文字列".link();
|
しかしこの時、メソッド内のthis
キーワードは、自身を参照していない。これが型がプロトタイプチェインを通じてプロパティにアクセスできる仕掛けと大いに関係がありそうだが、別に興味はないのでそこは深入りしない。
thisは何
Object.prototype.equals = function(arg){
return this === arg;
};
alert("文字列".equals("文字列")); // false
|
調べてみると型が違う:
string型の場合
Object.prototype.tellMyType = function(){
return typeof this;
};
alert(typeof "文字列"); // string
alert("文字列".tellMyType()); // object
|
boolean型についても同じ:
boolean型の場合
alert(typeof true); // boolean
alert(true.tellMyType()); // object
|
仕方ないから両者のみ上書き。
equalsメソッドをオーバーライド
String.prototype.equals = Boolean.prototype.equals = function(arg){
return this == arg;
};
|
脳log[2008-02-15] Re: Javascriptのstring型とboolean型、oneOfメソッド他 (agenda)にて、つっこみを頂いていました。その通りだと思います。ありがとうございました。
そしてObject.prototype.oneOf
メソッドを書いた。引数が一つしかない場合、Arrayインスタンスであると看做す。
Object.prototype.oneOfメソッド
Object.prototype.oneOf = function(arg /*, arg2, arg3, .. */){
if (arguments.length >= 2) {
for (let i=0; i<arguments.length; i++)
if (this.equals(arguments[i]))
return true;
return false;
}
// 引数一つのみなので、Arrayインスタンスであると推測し、forEachを試す
try {
arg.forEach( function(e){
if (this.equals(e))
throw StopIteration;
}, this );
} catch (err if (err instanceof TypeError)) {
// forEachメソッドを持っていない := Arrayインスタンスではないと看做す
return this.equals(arg);
} catch (err if (err instanceof StopIteration)) {
// 一致する要素が見つかった
return true;
}
return false;
};
|
しかし書いてみれば欲が出てくるもので、oneOf
の引数が一つでしかもそれがジェネレータ(またはイテレータ)だったら、yield
されるオブジェクト(next()
の戻り値)との比較を行なうよう変更したくなった。ジェネレータも__iterator__
プロパティを持っていることから、このプロパティを見て判別すれば良さそう。しかもジェネレータは自分自身がイテレータらしく、次の比較はtrue
だ。
ジェネレータ自身イテレータと看做せる
function gen(){
yield null
}
var g = gen();
g === g.__iterator__(); // true
|
で、改定。
Object.prototype.oneOfメソッド・改
Object.prototype.oneOf = function(arg /*, arg2, arg3, .. */){
if (arguments.length > 1) {
for (let i=0; i<arguments.length; i++)
if (this.equals(arguments[i]))
return true;
return false;
}
try {
// 引数一つのみなので、Arrayインスタンスであると推測しforEachを試す
arg.forEach( function(e){
if (this.equals(e))
throw StopIteration;
}, this );
} catch (err if (err instanceof TypeError)) {
// forEachメソッドを持っていない := Arrayインスタンスではないと看做す
try {
// イテレータを試す
for (let e in arg.__iterator__())
if (this.equals(e))
return true;
} catch (err if (err instanceof TypeError)) {
// Arrayでなくイテレータもないので、引数自身と比較する
return this.equals(arg);
}
} catch (err if (err instanceof StopIteration)) {
// 一致する要素が見つかった
return true;
}
return false;
};
|
そんなこんなで完成し、便利に使っている。例えばDOMではNode.oneOf(_NodeList)
でノードリスト中にノードがあるかどうかの判別が可能に。ただし_NodeList
は自作のイテレータだけど。"xhtml".oneOf("html", "xml", "xhtml")
のような文字列比較もよく使うが、Python使いにお馴染みのList Comprehensions(Javascript1.7ではArray Comprehensions)と併せて使うときに本領発揮。例えばURL.resolve(url, base)というコードで基底URL(base)に基づきurlを絶対URLに解決できるとする。この時、"dontEnum"というクラス名を持つ要素を除いた文書中の全てのhref属性値のうち、あるURL(url)を参照しているものがあるかどうかは次のように書ける。
oneOfとArray Comprehensions
url.oneOf(
[URL.resolve(e.href, location.href) for (e in document.selectNodes("/descendant::*[@href]") )
if (!e.isClassOf("dontEnum")) ]
);
|
実際にはもっと読みやすいように工夫して書くわけだが、それはコンテクスト依存なので使いやすさは(Python使い以外には)中々伝わらないかもしれない。別にどうでもいいけど。
webmaster@jintrick.net
公開: 2008年02月06日
カテゴリ: Javascript