JavaScript Archive
JavaScript code review 000
- 2009-08-25 (Tue)
- JavaScript
今日は指向を変えて、JavaScript の code review をしてみたいと思います。DOM の操作をする以下のような DOM ノードとスクリプトがあるとします。
<div class="description expandTo_500 canToggle">...</div>
<script type="text/javascript">
var someFunction = function (elm) {
var classes = elm.className.split(' ');
for( var i in classes )
if( /^expandTo_[0-9]*/.test( classes[i] ) )
var lim_char = Number( classes[i].split('_')[1] );
...
};
</script>
ぱっと見て、何をするかまぁだいたいわかると思うんですが、この someFunction が実行されると、引数として受け取った element を、その class にある一定の数字を解析して、それによって何かの処理をしたい、ということだと思います。
問題としたいのは以下の 4点あります。
- curly braces の欠如
- array の 列挙
- Number Objectの使用
- 正規表現を有効につかっていない
では順番に見ていきましょう。
curly braces の欠如
文法がそれを許している限り、これは好みの問題だ、という見方もあるんですが、Douglas Crockford によれば “Style isn’t subjective” だそうです。僕もそう思います。Curly braces はいかなる状況でも、省略すべきではないです。
array の 列挙
array 型の内容を列挙する時、JavaScript では、2つの方法があります。
var arr1 = [0, 1, 2, 3, 4];
for (var i = 0, len = arr1.length; i < len; ++i) {
console.log(arr1[i]);
}
var arr2 = [0, 1, 2, 3, 4];
for (var j in arr2) {
console.log(arr2[j]);
}
上記の二つの for ループは、基本的に同じことを目的としてしていますし、多くの場合は同じ結果をもたらすと思いますが、後者は違う結果をもたらす可能性があります。for の中で使用される in 演算子はもともと object のプロパティを列挙する為にあって、array の列挙の為ではありません。例えば Array.prototype.foo = '99'; という文がアプリケーションのどこかにあると、それだけで違う結果をもたらします。Crockford氏の jslint を使うと、for (…in…) は object の列挙の時ですら、 hasOwnProperty を一緒に用いないと、警告が出ます。
Number Object の使用
number型、boolean型、string型、には、wrapper クラスが存在するのですが、これを使う十分な理由、というのは僕には考えられません。前にも一度書きましたが、ポインタの比較をしたい時(というのがもしあれば、ですが)に、確かにこれらを使うことはできますが…ここでしているようなことの為なら parseInt で十分でしょう。
正規表現を有効につかっていない
さて最後に、文字列から数字を取り出すのに、split を使うのはもったいないです。せっかく正規表現で確認しているわけですから、一緒に切り出し作業もしてもらいましょう。
というわけで、書き直したコードを以下に。
var someFunction = function (elm) {
var classes = elm.className.split(' ');
var matches;
var regex = /expandTo_(\d+)/;
for (var i = 0, len = classes.length; i < len; ++i) {
matches = classes[i].match(regex)
if (matches) {
var lim_char = parseInt(matches[1]);
}
...
};
正規表現の一節以外は、ほとんど Douglas Crockford の受け売りになってしまいました。この本を読むと、Crockford氏の JavaScript に対する考え方を理解できると思います。(そして Java のような JavaScript を書くことになると思いますが…まぁそれはまた別の機会に。)
- Comments: 0
- Trackbacks: 0
Hello World to Meetup.Tweener
- 2009-08-24 (Mon)
- JavaScript | meetup.tweener
今更、Hello World, という感じでもないんですが、Meetup.Tweener の解説記事を書いていこうと思います。
Meetup.Tweener は僕が Open Source として開発している JavaScript のライブラリで、HTML + JavaScript の環境で非常に簡単にアニメーションを作ることができます。もし Flash での開発経験があれば、Tweener という Library は絶対聞いたことがあると思うのですが、Original の Tweener と API 互換性を持たせつつ、JavaScript 用に最適化をしています。
今やどのメジャー JavaScript ライブラリもアニメーション機能があるというのになんでいまさらアニメーションライブラリ?と思われるかもしれません。Meetup.Tweener の利点は
- 依存性は低く、(理論上では)どのメジャーライブラリとも共存できる
- 長い間 Flash で培われた API
- Meetup での実用実績
(1) に関して、今のところ対応しているのは jQuery と MochiKit だけなんですが、Prototype.js, MooTools, YUI, Dojo には対応する準備があります。(2) に関して、HTML5 が台頭してくるにあたって、多くの Flash 製作者が JavaScript に流れてくることが予想されます(楽観的)。彼らにとってなれしたしんできた syntax が使えるのはとても大きな資産になることでしょう。また、アニメーションをメジャーライブラリに頼らないことで、突然のコードベース変更にも強いでしょう。
このライブラリは Google Adplanner によれば 6M の Unique Visitor をほこる Meetup.com で運用の実績があります。実地で使われているものですから、問題があれば対処は素早く行われます。
開発は Github 上の meetup.tweener レポジトリにて行われていて、ドキュメントも Github 上にあります。
開発にあたって、Yuichi Tateno (id:secondlife) さんの JSTweener を参考にさせていただきました。ありがとうございます。
- Comments: 0
- Trackbacks: 0
Pseudo 3D by canvas2D
- 2009-08-20 (Thu)
- JavaScript
Today, I had a Hack-a-thon day at my work. Basically, no meeting, just do whatever you wanna do. It should be related to the work, but it doesn’t have to (am I right?). So I have been thinking that I wanted work on something 3D by using canvas and my Meetup.Tweener. I read lots of articles (which I have done before as well..), and gosh, I forgot so much about matrix transformation. But anyway, let me show you what I did. It’s only half done but it’s still sort of interesting how much you can do with canvas.
Click on picture, it will flip around.
This is even worse than CSS transformation because it only works for x-axis scaling as opposed to, CSS transformation, you can do 3d, at least more 3D-ish, flipping animation. BUT ANYWAY, this just fire on my heart to learn 3D again.
- Comments: 0
- Trackbacks: 0
Visitor and Iterator pattern in JavaScript
- 2009-06-08 (Mon)
- JavaScript | Tech
最近また実験的なアプリを JavaScript で書いているんですが、node walker を書く必要にかられ、デザインパターン入門をまた開いてみました。以下のページを開いて、Firebug の console で、例えば window.d = new DOMNodeDest(document.body); d.accept(new Visitor()); などとすれば、各ノードを列挙してくれます。
実際、頭を悩ましたのは、Visitor パターンというより、むしろ Iterator パターンでした。結構面白いと思うんですけど、どうでしょう?
Iterator = function (obj) {
var i, chld, len, pub, mth,
result = {},
BIND = Iterator.bind;
if (obj instanceof Array) {
this.children = obj;
}
else if (typeof(obj) === 'object' && obj !== null) {
this.children = [];
chld = this.children;
for (i in obj) {
if (obj.hasOwnProperty(i)) {
chld[chld.length] = obj[i];
}
}
}
pub = Iterator.publics;
for (i = 0, len = pub.length; i < len; ++i) {
mth = pub[i];
result[mth] = BIND(this[mth], this);
}
return result;
};
Iterator.publics = ['next', 'hasNext', 'rewind'];
Iterator.prototype.index = -1;
Iterator.prototype.children = null;
Iterator.prototype.next = function () {
if (this.children === null) {
return null;
}
var CHLD = this.children,
len = CHLD.length,
result = null;
++this.index;
if (len !== 0 && this.index < len) {
result = CHLD[this.index];
}
return result;
};
Iterator.prototype.hasNext = function () {
if (this.children === null) {
return false;
}
var result = false,
len = this.children.length;
if (len !== 0 && this.index < (len - 1)) {
result = true;
}
return result;
};
Iterator.prototype.rewind = function () {
this.index = -1;
var it,
i = 0,
CHLD = this.children,
len = CHLD.length;
if (len) {
for (;i < len; ++i) {
it = CHLD[i].iterator;
if (it) {
it.rewind();
}
}
}
};
Iterator.bind = function (func, context) {
return function () {
return func.apply(context, arguments);
};
};
- Comments: 0
- Trackbacks: 0
Google Page Speed and Google’s JavaScript
- 2009-06-07 (Sun)
- JavaScript | Tech
Google recently introduced a Firebug extension, called Google Page Speed. Simply, it’s a Google version of YSlow. I heard the author of YSlow, Steve Souders moved to Google, so I thought it’s a bit awkward corporate feeling but he himself said that he is not involved with this project. But even so, Yahoo has a hard time anyway.
As other Google’s products, Google Page Speed is also open source project, hosted on Google Code. So I was curious to browse their code. I don’t really read anything but JavaScript, and I found very familiar code on their internal library: /src/js/closure/base.js.
Do you remember when I talked about Google’s bad contamination on google_service.js before?
The problem is fixed now but you can find very identical code to the code I quoted on the post (well, it’s obfuscated, but you can imagine). I have strong feeling that the one I accused, “google_service.js” contains the google closure, or maybe it was just a google closure.
And I also found a project named Google Closure. It’s empty so far, and its founder is… ExplorerCanvas guy!
I originally had a very difficult feeling about so-called Google Closure, because it potentially breaks lots of other library as I stated on the post. But if it’s open sourced and I will definitely be more interested in. What it does so far is very… engineering smart. Somethng you must need on JavaScript. It comes with lots of lots of comments, and its abstraction level is very clear. Maybe I should do drill down series of blog post about it?
- Comments: 0
- Trackbacks: 0
Timer Performance in Browsers
- 2009-04-30 (Thu)
- JavaScript | Tech
courtesy of Telstar Logistics / Todd Lappin
After finishing Meetup.Tweener, I started to do lots of animation by JavaScript. In JavaScript, all animation happens by built-in timer. You can call either window.setTimeout or window.setInterval. So it’s all about how accurate the timer is fired, if you want to do precise animation.
I guess I have read somewhere before, but I just made up tiny JavaScript page here.
Timer Check @ archive.nydd.org
My observation:
- Firefox 3 is not that accurate. The interval tends to be much more wobbling than others. My Firefox is significantly slow.. Maybe because theme (Gray Modern 2), Firebug or something else.
- Safari is not bad. Sometimes, it does GC? it takes 10 times more than usual. But mostly, the performance is constant.
- Google Chrome is supreme. It has the best accuracy. If I cut counter update, I guess it can do much more.
- IE 6 is actually very good too. It never hits less than certain point, but the interval is very steady.
- Opera is also not too bad too. I don’t understand why, but it sometimes hits double time, very periodically.
- IE7, IE8, I will take a look at it soon.
Try the page! Questions and comments are all welcomed, And let me know if you find something interesting.
- Comments: 0
- Trackbacks: 0
Dear Google: Stop contaminating Standard Global Objects in JavaScript
- 2009-04-29 (Wed)
- JavaScript | Tech
So, I was at work in this morning, tried to start to work on the task I’ve been working on a while. I looked up my task note (wiki) and run some code (on browser, naturally, I’m a JavaScript geek), then I realized that it stopped because of unfamiliar errors. Wait. It was working last night. Then I ran some debuggers, then I found weird functions on each function. I asked people on the internal IRC channel. And I figured this:
http://partner.googleadservices.com/gampad/google_service.js
Let me just quote its high light here:
if (t.MODIFY_FUNCTION_PROTOTYPES = f) {
Function.prototype.bind = function(a) {
if (arguments.length > 1) {
var b = Array.prototype.slice.call(arguments, 1);
b.unshift(this, a);
return t.bind.apply(c, b)
} else return t.bind(this, a)
};
Function.prototype.partial = function() {
var a = Array.prototype.slice.call(arguments);
a.unshift(this, c);
return t.bind.apply(c, a)
};
Function.prototype.inherits = function(a) {
t.inherits(this, a)
};
Function.prototype.mixin = function(a) {
t.mixin(this.prototype, a)
}
};
It looks even different from what I saw at the office, actually. but I believe it’s the very same result since variable f is actually assigned for “true” in the very beginning.
And I googled a while and found this:
Question: google_service.js conflicts with prototype.js
Good job jimthoburn! Google usually doesn’t listen to outside voice.
I really hope that Google will stop doing this very very soon. I mean like tomorrow. It’s not only about Prototype.js, in face I don’t use Prototype.js at work. But IT IS REALLY BAD practice to contaminate Standard Global Objects in JavaScript. Please please do not do this.
- Comments: 0
- Trackbacks: 0
Meetup.Tweener (0.5.1)
- 2009-04-14 (Tue)
- JavaScript | Tech
まだ正式なアナウンスなどをしていないんですが、Github 上で Meetup.Tweener の開発を始めました。サンプルなどを充実させられたらと思っています。コメントは NaturalDocs でパース可能な状態になっていますので、ドキュメントに必要な方は NaturalDocs を使ってみてください。NaturalDocs のスタイル化が出来たらそれも package に含めようと思っています。
- Comments: 0
- Trackbacks: 0
Meetup.Tweener (0.5.1)
- 2009-04-14 (Tue)
- JavaScript | Tech
Not really announcing yet, but I started a repository Meetup.Tweener on Github. Check it out, if you’re interested in. I will write more samples soon. Comments are all written in NaturalDocs compatible style in its source code. Once I finish styling for it, I will include that in the repository, which, I wish, will happen soon…
- Comments: 0
- Trackbacks: 0
Safari バグ: TD エレメントの間違った offsetTop 値
- 2009-03-20 (Fri)
- JavaScript
# 追記: このバグは最新の開発版では修正されています。WebKit Bugzilla を参照のこと→ WebKit Bugzilla 19094. Nightly でも既に修正されているみたいですね。もしこれに関してハックをしたなら(つまり僕のことですが)、以下のようなもう一つ、ハックが必要です。
var webkit = indow.navigator.userAgent.match(/WebKit\/([0-9]{3})/);
if (webkit && parseInt(webkit[1]) < 528) {
...
}
JavaScript ライブラリの多くは、HTML エレメントの(ページ上での)ピクセル位置(絶対座標)を返すメソッドを備えています。jQuery の jQuery.offset() しかり、MochiKit の MochiKit.Style.getElementPosition() しかり、MooTools の Element.getPosition() 等々… ほとんどの場合これらはうまく動くのですが、今日動かないケースを確認し、おそらく Safari のバグだと思うのでここに報告しておきます。
以下の条件が揃ったとき、ピクセル絶対座標を取得するメソッドは間違った値を返します。
- ブラウザは Safari
- 取得しようとしているエレメントは TD (あるいは display:table-cell)
- CSS で vertical-align を top 以外の値 (middle or bottom) が設定されている
次にこのケースの例を作りました。Safaribug: wrong offsetTop on TD. テーブルセルをクリックすると、アラートで、そのテーブルセルのピクセル座標を返します。Safari とその他のブラウザで開いてみてください。違いがあります。
ではコードで解説します。以下は Google doctype project で見つけた PageOffset: How to calculate the position of an element on the page (goog.style.getPageOffset) を下にしています。オリジナルの PageOffset のコードは非常に細かく説明してあるので、一度読んで見ることをお勧めします。長くないし。
function getPageOffset(el) {
var pos = {
x: 0,
y: 0
};
var viewportElement = document;
if (el == viewportElement) {
return pos;
}
var parent = null;
var box;
pos.x = el.offsetLeft;
pos.y = el.offsetTop;
parent = el.offsetParent;
if (parent != el) {
while (parent) {
pos.x += parent.offsetLeft;
pos.y += parent.offsetTop;
parent = parent.offsetParent;
}
}
if (el.style.position == 'absolute') {
pos.y -= doc.body.offsetTop;
}
parent = el.offsetParent;
while (parent && parent != document.body) {
pos.x -= parent.scrollLeft;
parent = parent.offsetParent;
}
return pos;
};
Safari では、指定したエレメントから、その相対目標(これを指定しなければ、document.body を相対目標とします。つまりページの絶対座標ですね。)まで、エレメントを上り詰めて、その間の offset を累積加算していきます。問題は、一番最初の el.offsetTop (15行目) で起こります。これは table cell で、padding/margin/border が設定されていませんから、0 であるべきなのですが、Safari は、子孫要素への offset を返しているようなんですね。
どうにもいい方法が見つからなかったので、実際のアプリケーションではダーティハックを入れましたが、WebKit チームの早急な fix を望みます!!。
- Comments: 0
- Trackbacks: 0
- Search
- Feeds
- Meta
- Links
- Ads!
-
