2009年5月28日木曜日

HTML DOCTYPEについて

DOCTYPE宣言はHTML、XHTMLがどのような仕様に沿って記述しているかを宣言する。
具体的には下記のように書く

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

ブラウザによって解釈は異なるが
基本的に
HTMLタグ以前にDOCTYPの宣言があれば
Stdrds="標準モード"=CSS 仕様に従った正しい解釈をするモード

・DOCTYPE宣言がない
・スペルミス、記述が省略されている
などの場合は
Quirks="互換モード"=
として解釈(レンダリング)される

また、XHTMLの宣言をする場合
通常,<?XML ...を一番先頭に記述するが
IE6ではこの記述がDOCTYPEより前にあるとQuirksモードとして
判断されてしまうのでXML宣言は省略して書く(エンコードがUTF-8であれば省略してもOKということになっている)

補足:XHTMLを使う場合の注意点

各部ブラウザのモードの識別については
ここなどにまとめられている
DOCTYPE スイッチについてのまとめと一覧表 (HTML 5 や IE 8 Beta 2 のモードスイッチなどの情報も含んだ 2008 年版 )

2009年5月27日水曜日

ハングルで"ジャバスクリプト"

자바스크립트="ジャバスクリプト"
らしい

JavaScript arguments.callee

callee - MDC
現在実行している関数を示します。

無名関数内でcalleeを参照することで
無名関数であっても自身を参照できる

下のように、無名再帰関数を実行することができる

function makeFactorialFunc() {
alert('making a factorial function!');
return function(x) {
if (x <= 1)
return 1;
return x * arguments.callee(x - 1);
};
}

var result = makeFactorialFunc()(5); // 120 (5 * 4 * 3 * 2 * 1) を返す

JavaScript @cc_on

一行で IE の JavaScript を高速化する方法
に@cc_onなんてのがあり
何者なのか調査

MSDNによると
@cc_on ステートメント
は"条件付きコンパイルの機能をアクティブにします。"とのこと
コメント/**/内に記述するので
@cc_onに対応していないブラウザでは、コメントとして無視される

JScriptの実装のようなので
対応するブラウザ=IEのみ
となる
下のようにすれば、IEとIE以外で実行するコードを分けられる

var type;
/*@cc_on
//IEのみ
type = "IE";
*/
//IE以外
type = "NOT IE";

JavaScript グローバル変数へのアクセスとパフォーマンス

一行で IE の JavaScript を高速化する方法
amachang の 「一行で IE の JavaScript を高速化する方法」を掘り下げてみた
などで書かれていたが
document、windowなどのグローバル変数へのアクセスは
パフォーマンスがよろしくない。
(latest logさんの速度比較は大変興味深い)

そこで、グローバル変数をローカル変数に代入し
アクセスする際はローカル変数にアクセスすることで
だいぶパフォーマンスが上がるみたい

var _doc = document;

jQueryでも同様の方法がつかわれている

// Will speed up references to window, and allows munging its name.
window = this,

prototype.jsでは使われておらず
普通にグローバル変数へアクセスしている
こういったあたりでjQueryの方が速度が速かったりしてるんだろう、きっと。

2009年5月26日火曜日

JavaScript 継承とプロトタイプチェイン

JavaScriptがプロトタイプベースのオブジェクト指向言語ってどういうこと?

まずは、擬似的な継承はこんな感じで実現できる

function Hito(name) {
this.name = name;
};
Hito.prototype.hello = function() {
alert('Hello! My name is ' + this.name);
};
function SuperMan(name) {
this.name = name;
}
//Hitoオブジェクトをプロトタイプに設定
SuperMan.prototype = new Hito();

var hulk = new SuperMan('Hulk');
//SuperManにhelloはないがプロトタイプチェインをたどり
//Hitoのhelloが見つかり実行される
hulk.hello();

JavaScriptにおける全てのオブジェクトは
Object.prototypeを継承しています。
・constructor
・toString
・toLocaleString
・valueOf
・hasOwnProperty
・isPrototypeOf
・propertyIsEnumerable
などのメソッドがObject.prototypeに含まれる

後からObject.prototypeに追加もできる

Object.prototype.hoge = function() {
alert('hoge');
}
"text".hoge();

JavaScript prototypeとメモリの関係

下の例では
Hitoコンストラクタから3つのオブジェクトを作成している
コンストラクタ内ではthisを使い、変数・関数を割り当てている
この場合だと、変数・関数ともにそれぞれ別にメモリ上に割り当てられてしまうことになり
メモリの無駄遣いとなる

function Hito(name) {
this.name = name;
this.hello = function() {
alert('Hello! My name is ' + this.name);
};
}
var saito = new Hito('saito');
var ishii = new Hito('ishii');
var kume = new Hito('kume');
saito.hello();
ishii.hello();
kume.hello();


そこでprototypeを使い
下のように書きかえることで
関数が共用されるようになる。

function Hito(name) {
this.name = name;
};
Hito.prototype.hello = function() {
alert('Hello! My name is ' + this.name);
};
var saito = new Hito('saito');
var ishii = new Hito('ishii');
var kume = new Hito('kume');
saito.hello();
ishii.hello();
kume.hello();

JavaScrit スコープチェイン

JavaScriptクロージャを完全理解!スコープチェインを知る
を参考に勉強

ローカル変数を宣言した場合、JavaScript内部では
管理用のハッシュテーブル(変数オブジェクトと呼ぶ)
に格納される。
グローバル変数の場合はwindowオブジェクトのプロパティに格納される
例えば

var hoge = 100;
alert(window.hoge);

という風にwindow.hogeでグローバル変数にアクセスできる

以下例ではグローバル変数にf()からアクセスできる
実行時、順にf()内の変数オブジェクトを検索しaはないので
次にグローバル変数を探し、アクセスしている。(関数外の変数にアクセスできる)
この変数の探索をスコープチェインという

var a = 0;
function f() {
alert(a);
}
f(); // 0が表示される

JavaScript クロージャ

クロージャー
プログラミング言語において引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決する関数のことである。関数とそれを評価する環境のペアであるともいえる。この概念は少なくとも1960年代のSECDマシンまで遡ることができる。
まれに、関数ではなくとも、環境に紐付けられたデータ構造のことをクロージャと呼ぶ場合もある。


JavaScriptでのクロージャー

var cl = function() {
var counter = 0;
return function() {
counter += 1;
return counter;
};
};
var c = new cl();
alert(c()); //1
alert(c()); //2
alert(c()); //3

JavaScript apply呼び出し

第1引数で指定したオブジェクトをthisとし
また、第2引数で指定した配列を引数として
関数を実行する

var human = function(name) {
this.name = name;
}
function func1(pre) {
alert(pre + ' ' + this.name);
}
var h = new human('hoge');
//humanオブジェクトをthisとして['hello']を引数として
//func1を実行
func1.apply(h,['hello']);

JavaScriptスコープ

JavaScriptにおける変数のスコープは
ブロックスコープを持たず
関数内であれば、関数内のどの変数にもアクセスできる。
関数外からはアクセス不可

var func = function() {
var a = 3, b = 1;
var inner = function() {
// var b = 7;とするとbは再定義され外側のbへは影響しない
b = 7; //外側の関数内の変数にアクセス可能
};
alert(b); //1
alert(a); //3
{
var a = 2;
}
alert(a); //2 ブロックスコープはないためaが上書きされる
inner();
alert(b); //7
};
var x = new func();

JavaScriptクラスとコンストラクタ

JavaScriptでクラス的なものを実現させるには
関数リテラルがクラスに相当するもので
関数リテラルをnewで初期化したものがインスタンスとなる

//関数リテラル(クラス)の定義
var animal = function(name) {
//thisは呼び出しものnekoオブジェクトの参照となる
this.name = name;
this.sayName = function() {
alert(this.name);
};
};
//newで初期化、オブジェクトの生成
var neko = new animal('tama');
neko.sayName();

JavaScript関数リテラル

JavaScriptにおいて関数はオブジェクト(Functionオブジェクト)となる

関数オブジェクトの表記方法には何種類かある

1つ目はfunction+関数名+引数()とする表記

function hoge(a) {
}

もう1つは関数リテラルとして表記する方法
変数名 = function+引数とする

var hoge = function(a) {
}


後者は無名関数と呼ばれるもので
前者の表記と比較すると
・関数名を与える必要がない場合、簡潔に書ける
・変数名、関数名の衝突を防げる
などのメリットがある。

また

var hoge = new function (a) {
}

のような書き方も可能

JavaScriptオブジェクトリテラル

オブジェクトは{}で囲み
プロパティ名、値(文字列、数値など)もしくは関数を設定する

//オブジェクトリテラル
var obj = {};

obj = {
name: 'hoge',
age: 30,
address: {
zip : 00-0000,
city : 'tokyo'
},
say : function() {
//関数
alert('yhaaa!');
}
}

//.プロパティ名でアクセス
alert(obj.name);
//[プロパティ名]でアクセス
alert(obj["address"].city);
//関数呼び出し
obj.say();

2009年5月25日月曜日

addEventListener

event.addEventListener
addEventListenerは
 第一引数=イベントタイプを表す文字列(ex."click")
第二引数=EventListener インターフェースで実装されたオブジェクトあるいはJavaScriptの関数名
 第三引数=falseの場合は子から親へ順番にイベントが伝播されるが、trueの場合は優先される。

第二引数の関数に引数を渡す場合、下の書き方ではダメ

document.getElementById('btn').addEventListener("click", doAlert('msg1'), false);
function doAlert(msg) {
alert(msg)
};

渡す方法としては
無名関数として書く

document.getElementById('btn').addEventListener("click", function(e) {doAlert('msg1')}, false);
document.getElementById('btn').addEventListener("click", function(e) {doAlert('msg2')}, false);
function doAlert(msg) {
alert(msg)
};

だが、無名関数で書くと、removeEventListenerでイベントの削除ができない
下記のように、それぞれ名前付き関数で関数参照を保持すればよいのかな?

var listener1 = function (event) { doAlert('msg1'); };
var listener2 = function (event) { doAlert('msg2'); };
document.getElementById('btn').addEventListener("click",listener1,false);
document.getElementById('btn').addEventListener("click",listener2,false);



また、リスナーに登録できるのは
"EventListener インターフェースで実装されたオブジェクト"
ともあるので、以下のような書き方もできる(Firefox、OperaなどでIEではダメ)

var listener1 = {
handleEvent : function (event) {
doAlert('msg1');
}
}
document.getElementById('btn').addEventListener("click",listener1,false);


上のやり方で、それぞれthisの挙動が異なるみたい
あとで

JavaScriptのイベント処理の書き方

1.HTMLタグ内の属性として記述する

  
Click


この書き方だとメンテナンス性が落ちる、あと1つのイベントしか処理できない

2.elementオブジェクトのonXXXXに関数を定義する

document.getElementById("btn").onmousedown = funciont(evt) {
evt = evt || window.event;



}

1と同様に1つのイベントしか処理できない

3・addEventListener(IEではattachEvent)を利用する

document.getElementById("btn").addEventListener('click', goFunc, false);
function goFunc(evt) {
evt = evt || window.event;



}

複数のイベントを処理できる

DIV要素の移動

2秒毎にDIV要素を下方向に10px移動させるJavaScirpt


var timerID;
$('runBtn').onmousedown = startDown;
$('stopBtn').onmousedown = stopDown;

//2秒ごとにgoDown()を実行
function startDown(){
timerID = setInterval("goDown()", 2000);
}

//Interverl処理をキャンセル
function stopDown() {
if (timerID) clearInterval(timerID);
}

function goDown() {
//offsetTopは親の座標軸を基準とした座標を返す
//offsetTopはreadOnlyのため
//div要素の座標変更にはstyle.topに代入する

//staticではstyle.topは無効のため、absoluteにする
$('boxA').style.position = "absolute";
$('boxA').style.top = $('boxA').offsetTop + 10 + 'px';
}


2009年5月22日金曜日

ブラウザのJavaScriptコールスタックサイズ


Safari3はスタックサイズがなんでこんなに少ないんだろう?
JavaScriptエンジンがNitroになったSafari4ではどうなのか

  • IE7 (1,789)
  • Firefox3 (3,000)
  • Chrome1 (21,837)
  • Opera 9.62 (10,000)
  • Safari 3.2 (500)
  •