2007年1月アーカイブ



ここんとこ漸く javascript 頭になってきて、


チームの自分より詳しい人にも教わってるうちに、


やっとこの人の言う事がわかるようになって来た。



Function.prototypeを拡張して遅延実行を実現する


call と apply がムニャムニャムニャな人はこれを読むとよいです。



* call は固定引数


* apply は可変引数



で。


this はどこ問題


この問題の正解がやっとわかった。



hoge っていう変数にインスタンス化したクラスの fuga っていうメソッドを、


setTimeout で呼び出して、その中で this 使いたかったら、こうするのだ。



こうするのだ。とか書いてるけど、こんなの文章でもなんでもないよな。


hoge の fuga で this がどうとか、こんなの人に聞かれたら頭がおかしいと思われるか、


その逆かだ。



Hoge = function(){
   this.something='lalala..'
}

Hoge.prototype = {
fuga: function() {
alert(this.something);
}
}

var hoge = new Hoge();
setTimeout(function(){hoge.fuga.call(hoge);}, 100);

test はしてないけど、こういうことだ。



チームで仕事するとスキルアップが断然早い。



javascript やる上で、 call と apply は第一関門だと思う。


漫画とかで頭の上で電球がピカッってなるやつ、


ほんとそんな感じだったよ!


うそだけどね。本当はじわじわきた。


染みが広がっていって、あ、世界地図か!みたいなイメージ。



第二関門は、イベントドリヴンへの理解だな。


(個人差があります)




2007/02/10追記


この方法には問題がありました。


http://d.hatena.ne.jp/am11op/20070209/1171039704



JavaScript Template なる javascript のテンプレートエンジンを使ってみた。


あまりにもそのまんまなネーミングである。


ダウンロード、使い方は下記リンクに。


http://trimpath.com/project/wiki/JavaScriptTemplates


英語だけど、簡単だから僕でもわかったよ!



でも日本語にしてくれてる親切な方もいます!


http://d.hatena.ne.jp/shogo4405/20060928/1159422730


http://d.hatena.ne.jp/m-hiyama/20051201/1133396794



使い方は簡単で、


ライブラリをインクルードして、こんな感じにするだけ。



var template = "Hello ${name}!";
var data = {name: 'somebody'}

var myTemplateObj = TrimPath.parseTemplate(template);
var result = myTemplateObj.process(data);

alert(result); //出力結果は「Hello somebody!」


で、javascript からHTML弄る時に、



  • チマチマ弄るならDOM操作

  • ダイナミックに弄るなら innerHTML で(ただし、HTML の組み立てはローカル変数内で行うこと)


と相場は決まってますが、


決まってるらしいですが、


決まってるとどこかで聞いた覚えがありますが、


ダイナミックに弄る場合は HTML テンプレートによる出力で


innerHTML を入れ替えるのがすっきりしてよさそうです。


コードと HTML も分離しやすいしね。



で、これを使ってこんなサンプル作ってみました。


JSTを使ってHTML中に直接テンプレートを書くテスト


こんなソースです。



<html>
<head>
<script type='text/javascript' src='../js/template.js'></script>
<script type='text/javascript'>

</script>
</head>
<body>

<div id='contents'>

${say} World!

</div>

</body>
</html>


ソース中に直接テンプレートを記述しちゃって、


それを getElementByID により取得、


さらに JST かませた結果を差し替えてます。



これによるメリットは



  • HTML を分離できる

  • javascript から出力されるものがそのままそこにあるので、わかりやすい (てことは、意欲的なデザイナさんとの共同作業が可能!)

  • javascript で改行コード挟んだらエラー出るのとか気にしなくてよい


片や、デメリットは



  • javascript をオフにされると、トホホなことに

  • SEO って知ってる?とか意地悪言われるかも



結構強力なデメリットがありますが、


まあ SEO 気にしなくてよいようなページではやってみる価値あるかも。




factory メソッドが好きで、よく使う。


なんで好きかって言うと、if 文を減らせるからだ。


if 文をいかに減らすかで、コードの可読性はだいぶ違ってくると信じてる。


phpでもよく使ってた。



けど、今まで javascript で factory メソッドにお目にかかったことが無い。


フレームワーク的なもののソースをいろいろ見てきたけど


(流し読みとか、エディタで開いたことがあるもカウント)


(いや、ほんとのこというと、そんなにいろいろ見てません。嘘つきました。)、


factory メソッドを使ってるものを寡聞にして知らない。


「寡聞にして知らない」って言葉を使ってみたかった。


ちょっと前まで、「寡聞にして」を全く逆の意味と寡聞にして思ってた。だから、なんて鼻にかかる言葉だと思って使う機会がなかったのだ。



まあ、なんせなんでかなーと思って、試しに使ってみた。


正確に言うと、なんでかなー、便利なのになーとか、


実はみんな知らないんじゃねえのとか、


あれ、俺間違ってんのかなとか、


なんか俺やっぱついていけてない?とか、いろいろ思って使ってみた。



いや、違うわ。


何も考えずに普通に使ってみた後で


いろいろ思うことがあった。



ただ、その時本当に何も考えてなかったかというと(略



例えば javascript で気になる if 文といえば、


やっぱり クロスブラウザ対策だよね。ってことで、


こんな感じでやってみた。



hoge = {}

hoge.factory = function() {
var ua = navigator.userAgent.toLowerCase(),
isOpera = (ua.indexOf('opera') > -1),
isSafari = (ua.indexOf('safari') > -1),
isGecko = (!isOpera && !isSafari && ua.indexOf('gecko') > -1),
isIE = (!isOpera && ua.indexOf('msie') > -1);

if (isIE) {
return new hoge.IE();
} else if (isSafari) {
return new hoge.Safari();
} else {
return new hoge.Gecko();
}
}


で、思ったのは、


これってハードコーディングだよね。ってことです。



ブラウザごとにクラス作ってるけど、


例えば IE6.0 と IE7.0 でも挙動はぜんぜん違うわけで。


バージョンごとにクラス作ってくつもりかよ、っていう。



javascript におけるクロスブラウザ対策では


「関係する機能が使えるか」で処理を分岐させることが多いぽいですね。


attachEvent が使えるか、とか、addEventListener が使えるか、みたいな。



ただし、ブラウザによって判別する他ないような場合もあるわけで。


そういう時には factory メソッドも悪くないんじゃないかと思った。


例えば、ブラウザの「戻る」対策とか。



というわけで、クロスブラウザ対策に factory メソッドが使えるかも。


と思ったけど、実際は微妙だったって結論なわけですが、


factory メソッドの用途はクラスブラウザ対策以外にも全然あるので、表題は間違い。




因みに、デザパタの知識はうろ覚えです。


僕が今まで見てきたプログラマ(数人)は僕を含めてほぼ100%の確立で、


プログラマのバイブル、


結城先生の「サルでもわかる、デザインパターン入門」を持っていました。


amazon:デザインパターン入門 結城浩



大半の人は、実際のバイブルと同じように、睡眠薬として使っていたようです。


僕は、分厚い本なので、まくらとして使っていたことがあります。


今は、分厚いので、本立てとして使っています。


後半はうそです。




メールする前に、電話で確認した方が早い事ってのが、確実にある。


仮定を元に論理立てて解決策提示したつもりが


仮定が間違ってたら話にならん。



とっとと電話で確認して可能性潰してから


じっくり考えた方が、結果的に断然早い。



製作の人って、自分から電話したがらないんだよな。


窓口になっちゃうと、いろいろめんどくさいもんね。


でも、それを人に頼むと伝言ゲームで余計めんどくさかったりして。


人(内外問わず)とのコミュニケーションを億劫がらないようにしないとね。


自戒として。



無理だな、俺には。




と思いきや、


prototype.js には bind なる便利メソッドがあるぽい。


その中身はというと、



Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}


なるほど!


やっぱ、 apply が正解か。


ほんとはあんまりわかってないけどね!




で、javascript ちゃんとやりだして、


最初に躓いたっていうか、イマイチ腑に落ちなかったのが


いつの間にか this がどこかいってしまう問題。


多分、初歩的な問題なんだろうけど。



XMLHTTPRequest で callback 指定する時とか、


setTimeout とかの時に、クラスのメソッドを参照渡しすると、


そのクラスの属性が参照できなくなってしまう件である。



function hoge() {
    this.test = 'hello';
}
hoge.prototype = {
    fuga: function() {
      alert(this.test);
    }
}
var hoge_ = new hoge;

//この実行結果は undefined になる。ハズ。
setTimeout(hoge.request, 100);

まあ首をかしげながら、変数をグローバルにしたり、


callback に渡すのはメソッドじゃなくて関数にしたり、


挙句の果てにその関数の中でクラスをもう一度インスタンス化したりして


解決(なかったことに)してたんだけど、



今から思えばまさに迷走である。



こんな感じでやってた。テストしてないので、動くかどうかは知らんです。


動いてもしょうがないし。



var g={}

function hoge() {
g.param='global';
g.text = '';
}

function hoge.prototype = {
request: function() {
var myAjax = new Ajax.Request(
url,
{
method: 'get',
parameters: pars,
onComplete: getResponse
});

},

display: function() {
alert(g.param + g.text);
}
}

function getResponse(o) {
var ins = new hoge();
ins.text = o.responseText;
ins.display();
}

function init() {
var ins = new hoge();
ins.request();
}

Event.observe(window, 'load', init, false);

オブジェクト指向に片思いしてる自分としては、


クラスはそれなりに意味のあるものにしたい。


だから、問い合わせ→データ取得→出力を何とか


一つのクラス内で行いたくって四苦八苦してるのである。


あと、グローバルもなるべく汚したくないしね。



まあ、これで何となく動いてるし、まいっか。


検索してもパキッと出てこないし。


みんなどうしてんだろうとも思うんたけど、


近くの人に聞いてみても、


「this がどっかいっちゃうんですよね~」


「うにゃむにゃむにゃ」って答えだったので、(十中八苦、聞き方が悪い)


放置してた。



これが最近になってなんとなく解決した。


参照を渡せるのは、 php でいうところの static なメソッドのみなんだ。きっと。



だから、 XMLHTTPRequest で callback 指定する時とか、


setTimeout とかの時に this がなくなっちゃうのだ。



これを解決するには、インスタンスをグローバル変数にしてしまえばよい。ハズ。


で、参照渡しされるメソッド内でのみ、そのグローバル変数のインスタンスを使用すればよい。ハズ。



相変わらず、テストはしてないす。



var g={}
function hoge() {
this.param='global';
this.text = '';
}

function hoge.prototype = {
request: function() {
var myAjax = new Ajax.Request(
url,
{
method: 'get',
parameters: pars,
onComplete: g.hoge.getResponse
});

},

display: function() {
alert(this.param + this.text);
},

getResponse: function(o) {
g.hoge.text = o.responseText;
g.hoge.display();
}

}


function init() {
g.hoge = new hoge();
g.hoge.request();
}

Event.observe(window, 'load', init, false);

apply っていう関数使えば何とかなるんじゃないかとも思ったけど、


多分、これ違う。



つーことで体で覚えた javascript の作法でした。


ほんとの正解はどんななんだろう。




php4 はそこそこ書けると自負してる自分だけど、javascript は全然な自分が


今度 javascript メインの案件を任されちゃった。



今まで javascript は php で作ったアプリの管理画面の UI に


prototype.js とか scriptaculous 使ってみてた程度。(それすら半年前くらいから初導入。)



年も年なので、ぼちぼちやりま~す。