2007年04月12日 23:30 [Edit]

javascript - 勝手に添削 - JavaScript入門

釈迦に説法を承知で。

Object.prototype = { /* ... */ }は避けるべし

みにくいのでオブジェクトを入れ替える方式に
Box.prototype = {
    speed: 4,
    move: function() {
        this.left += this.speed;
        this.element.style.left = this.left + 'px';
        if (this.left > 400) clearInterval(this.id);
    },
    start: function() {
        var self = this;
        this.id = setInterval(function() { self.move() }, 20);
    }
};

これだと、Boxが他のプロトタイプを継承していた場合に、親オブジェクトのプロトタイプにアクセスできなくなってしまう。極端な例だと

function MyBox(id){
    this.element = document.getElementById(id);
    this.left = 0;
}
MyBox.prototype = Box.prototype; // ここで継承しているのに
MyBox.prototype = {              // ここで台無し
  speed:8
};

このあたりは、プロトタイプベースの欠点で、クラスベースのものだとclass Klass{...}が複数回登場しても、前のものを「上書き」してしまうのではなく、「重ね合わせ」になるのだけど、JavaScriptの場合prototypeもまた単なるオブジェクトなので、丸ごと代入するとそこで定義されていないものは丸ごと消えてしまう。

ここは面倒でも

Box.prototype.speed = 4;
Box.prototype.move = function() {
    this.left += this.speed;
    this.element.style.left = this.left + 'px';
    if (this.left > 400) clearInterval(this.id);
};
Box.prototype.start = function() {
    var self = this;
    this.id = setInterval(function() { self.move() }, 20);
};

で留めておくか、

(function(c, o){
  for (var p in o){
     c.prototype[p] = o[p];
  }
})(Box, {
    speed: 4,
    move:  function() { /* ... */ },
    start: function() { /* ... */ }
});

とするか、

Object.prototype.addPrototypes = function(o){ // 便利だけど推奨できない
   for (var p in o){
      this.prototype[p] = o[p];
   }
};
Box.addPrototypes({
    speed: 4,
    move:  function() { /* ... */ },
    start: function() { /* ... */ }
})
var ary = [];
alert(ary.speed);

とするか、prototype.jsのObject.extend()を使うかするか、とにかく「重ね合わせ」をする癖をつけておいた方がいいと思う。Proto.prototype = { /* ... */ }は私も使いたい誘惑によくかられるのだけど、今後JSはさらに大規模の開発にも使われて行くはずなので。

with()は徹底的に避けるべし

次。

複数要素
for (var i = 0, l = nl.length; i < l; i ++) {
    var e = nl[i];
    with({e:e})
    setTimeout(function() {
        var box = new Box(e);
        box.start();
    }, i * 500);
}

with()は金輪際あきまへん。ましてやこういう高度な使い方はamachangとかでないと出てきません。サイ本にもそう書いてある。

この場合はずっと素直に

Box.prototype.later =  function(delay) {
   var self = this;
   setTimeout(function() { self.start() }, delay);
};
var nl = document.getElementById('target010').getElementsByTagName('div');
for (var i = 0, l = nl.length; i < l; i ++) {
    var box = new Box(nl[i]);
    box.later( i * 500 );
}

と書いた方がはるかにわかりやすい。

JavaScriptに限らず、ユーザーが使いそうな機能は、ユーザーに実装させるのではなくクラス/プロトタイプ側で用意しておくべし。

無名関数を紹介しとこうよ

使い捨て変数名前空間
(function() {
    var nl = document.getElementById('target011').getElementsByTagName('div');
    for (var i = 0, l = nl.length; i < l; i ++) {
        var e = nl[i];
        with({e:e})
        setTimeout(function() {
            var box = new Box(e);
            box.start();
        }, i * 500);
    }
})();

ここまで来たら、やはり無名関数まで紹介して欲しかった。

(function(id, tag, interval){
  var nl = document.getElementById(id).getElementsByTagName(tag);
  for (var i = 0, l = nl.length; i < l; i ++) {
    var box = new Box(nl[i]);
    box.later( i * interval );
  }
})('target10', 'div', 500);

こうしておけば、HTMLデザイナーとの連携も楽で、

(function(id, tag){
  var nl = document.getElementById(id).getElementsByTagName(tag);
  for (var i = 0, l = nl.length; i < l; i ++) {
    var box = new Box(nl[i]);
    box.later( i * interval );
  }
}) // ここまで変更しないで!
(
  'target10', // 要素のid
  'div',      // 要素内のタグ名
  500         // スタートの間隔をミリ秒で指定して
);

と出来るし。

余談だけど、JavaScriptは無名関数で再帰が出来る。これが出来るのは、私が日頃使っている範囲ではPerl 6 と R ぐらいだ。

単にスコープを重ねるためだけに使うにはあまりにもったいないではないか。

まとめ

そろそろ JavaScript Best Practices が欲しいなあ。Perl 5よりも玉石混淆度が激しいし....

Dan the JavaScripter


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

この記事へのトラックバック
404 Blog Not Found:javascript - 勝手に添削 - JavaScript入門
404 Blog Not Found:javascript - 勝手に添削 - JavaScript入門【】at 2012年01月23日 11:14
以前 404 Blog Not Found:javascript - 勝手に添削 - JavaScript入門 with()は金輪際あきまへん。ましてやこういう高度な使い方はamachangとかでないと出てきません。サイ本にもそう書いてある。 と書いたのですが、Resigちゃんが John Resig - Untold JavaScript Secr...
javascript - with(second.thought) // with再考【404 Blog Not Found】at 2008年06月15日 22:06
404 Blog Not Found:javascript - 勝手に添削 - ...
添削必要?【p0t】at 2007年04月13日 11:37
http://blog.livedoor.jp/dankogai/archives/50808279.htmlhttp://d.hatena.ne.jp/amachang/20070413/1176421425「Object.prototype」って書いちゃうと勘違いする人も多いと思うので、MyObject.prototypeとかSomeClass.prototypeとか何でも良いけど別の書き方にすべきだと思...
全力で勝手に添削に添削に添削の自転車置き場にマジレス【最速インターフェース研究会】at 2007年04月13日 10:46
このエントリーは以下のエントリーへの解答です http://blog.livedoor.jp/dankogai/archives/50808279.html はじめに 僕はあまりブログを一生懸命書くのは嫌いです。で、いつも適当に言葉は少なめにソースだけで解説しているが。今回は、それだけでは伝わらないところまで突
[javascript]「勝手に添削 - JavaScript 入門」を勝手に添削【IT戦記】at 2007年04月13日 08:43
この記事へのコメント
おい プログラムって個性だろ?
誰にプログラムを書くスタイルのどうこうを言える権利あるんだ?
おまいは教科書そのまんまコピーするのか?!
Posted by purogurama at 2007年06月06日 22:35
これは恥ずかしい・・・
人の書いたものに文句つけるなら、せめてもう少し理解度上げてください。
Posted by 通りすがり at 2007年06月05日 16:01
>>> (lambda f:lambda x:f(f,x))(lambda f,x:x and f(f,x-1)*x or 1)(10)
3628800

頑張れば、pythonでも。
Posted by tmiz at 2007年04月17日 04:07
なんかコメントする気もなくなるくらいクオリティが低いコメント欄ですね。
いずれにせよ、楽しませていただきました。
Posted by aa at 2007年04月14日 09:38
他人の揚げ足取ってまで(しかも墓穴ほって)アクセス数稼ぎたいか?
Posted by 通りすがり at 2007年04月13日 23:46
何が「ありがとうございました。」なんだ??
でも、堂々と恥晒せていられるアンタは立派だよな。
Posted by 通りすがり at 2007年04月13日 17:55
通りすがり(上の方)さん、
ありがとうございました。
Dan the Typo Generator
Posted by at 2007年04月13日 16:48
かっこわるぅ〜 このエントリ・・・
Posted by 通りすがり at 2007年04月13日 12:16
s{
親オブジェクトのプロトタイプにアクセスできなってしまう
}{
親オブジェクトのプロトタイプにアクセスできなくなってしまう
}
Posted by 通りすがり at 2007年04月13日 10:46
それこそ釈迦に説法でしょうが一応。
ArrayやDateなども含めた全てのオブジェクトに継承されるのでObject.prototype.addPrototypesな実装だとちょっとマズいケースがありますよ。
prototype.jsも昔はObject.prototype.extendな実装だったのをver1.4.0あたり(この辺ははっきり覚えてない)から単なるObject.extendに変更したという経緯があります。
Posted by Ren at 2007年04月13日 06:09