2007年02月21日

Javascript:Array オブジェクトの継承では length が予期しない値を返す

02/22 後尾に追記

アルファブロガー達※1が、一時取り上げていた3行の継承に関してですが、これで Array オブジェクトを複製しても組み込み済みのメソッドは、継承元でしか動作しないですね。

■ 試した関数
404 Blog Not Found さんより
javascript - プロトタイプ的継承

function object(o){
  function F(){}
 F.prototype = o;
 return new F();
}

※1 私だけの認識じゃないと思う。



 

var a = [1];
var c = object(a);

c[1] = 2;
c[2] = 3;
alert(c.length); // 1 <-- 期待した値は 3

継承した側のオブジェクトに追加しても c.length は 1 を返してしまいます。試しに配列 a に追加すると c.length は、その分変化します。

また、c instanceof Array とすれば c が Array オブジェクトであるという結果にはなります。ここから、一瞬 this が a のままか?なんてことも考えましたが、そんな訳は無いですね。んが、一応確認しました。期待通りの this ですw


 length だけか?

上のスクリプトコードに以下を足して試すと。
alert(c.join(""));// 1 <-- 期待した値は 123
alert(c.concat([8,9]));// 1,8,9 <-- ie6.0 は [object] となる

// push の場合さらに混沌とする
c.push(10);
alert(c[1]);// 10 <-- c[3] じゃなく c[1] に push された
alert(c[2]);// 3

// こちらはOK
alert(c.hasOwnProperty(1));// true <-- 期待通り

// こちらもOK
a.see = function(){alert(this[0]);}
c[0] = "Change!";
a.see(); // 1 <-- 期待通り
c.see(); // Change! <-- 期待通り


んー。どうも混沌としている。
自分は、継承などの概念を完全に把握している訳ではないので・・・。他にも、なにかとんでもない根本的な見落としがあるかもしれません。

ただ、ちょっとこれは複製とか継承とか言うのはマズいのではないかと。

ちなみに alert(c); とすると ie の場合 [object] となります。生粋?wの配列なら連結された値が alert されます。この辺りも気になるところです。

試したのは IntenetExplorer6.0 と FireFox1.5。どちらも同じ結果だったので、バグやブラウザの相違と言うことではないようです。

ま、登場してからずいぶん時間が経つので、どこぞですでにやってるかも知れません。


 追記 02/22

いきおい投稿してみたもののアホな事してないか非常に不安になりますね。ということで仕様書を再読してみた。(ああ、避けたかった・・・。)

その他参考:プリミティブ値でもプロトタイプ的継承(Days on the Moonさんより)
String#toString および String#valueOf が String 型以外のオブジェクト上では (そのオブジェクトのプロトタイプが String 型のオブジェクトであったとしても) 呼び出せないからだ。ECMAScript 仕様ではこのことを not generic (汎用的ではない) と表現している。

これは何も String 型に限った話ではない。Array#toString も汎用的ではなく・・・

仕様書を確認しても push や join は、not generic ではありません。ただ join は toString メソッドを使っています。そこで、toString の対策をしている、Days on the Moonさんの prototypal.js (上記リンクの中にあります)やその他の対策済みコードを使ってみましたが、結果は同じになります。

やっぱ length かなぁ。コードいじってる間は何も考えてなかったんですが、length はインスタンスのプロパティなんですね。push も join も length を使っている。

とりあえず ecma-262
15.4.5 Properties of Array Instances
Array instances inherit properties from the Array prototype object and also have the followingproperties.

15.4.5.2 length
The length property of this Array object is ・・・

当然、length は継承元の値を受け継いでしまう。それならば length も差換えれば期待通りになるのか?ということで以下。

var a = [ ];
a.length = 10;
alert(a.length);// 10

var aa = [ ];
var b = object(aa);
b.length = 10;
alert(b.length);// 0 <-- まただ。また期待した値にならない・・・。

aa.length = 10;
alert(b.length);// 10 <-- length は、意地でも渡さないらしい。

ちなみに ie の場合です。firefox は異なります。

ま、そもそもコピーしたオブジェクトにさらに追加しようなどというのが間違いの発端なんだろうか・・・。しかし、それでは継承も空しい。
concat でも使って複製した配列にメソッド追加する方がマシになってしまう。
posted by HiFa at 16:02 | 🌁 | Comment(0) | TrackBack(0) | JavaScript雑感 | このブログの読者になる | 更新情報をチェックする
>>> スパムコメントは消してますよん。 お互い無駄な労力は避けましょう。 <<<

この記事へのコメント

コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/34320363
※言及リンクのないトラックバックは受信されません。

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。