FrontPage

メモ

  • 最近 Ajax で再評価されるようになった。
  • Java applet と異なり、クラスファイルのロードは不要。(逆にソースなのだから転送量は膨らむ気がする。)
  • Java applet と異なり、HTML フォーム(もしくは HTML データ本体)へのアクセスが可能。HTML を書き換えることで画面を動的に書き換えられる。(Dynamic HTML)
    • テキスト整形、表組みをブラウザに任せられる。(Java applet だと死ぬ)
  • インストールの必要がない。
    • Flash, Director はインストールしてもらえるのだから、JDK のインストールも今では負荷にはならないと思うのだが…
  • Netscape が作ったのが JavaScript
  • Microsoft が真似したのが JScript (MS的には VBScript を普及させたかったようだが)
  • JavaScript と JScript との違いが問題化し標準化されたものが ECMAScript
  • ブラウザにより動作が異なるため、検証がかなり大変。
  • 言語仕様的には若干ダーティ。
  • 弱い型付き言語
    • 変数には型がない。
    • クラスはある。
  • 変数には宣言が「必要」
    • 不要に見えるケースがあるが、それはおそらく変なところにメンバ追加している。
  • いわゆるクラス定義構文がない。(function として定義する。)
  • オブジェクトにメンバを動的に追加、削除できる。(便利でもあり危険でもあり。)
  • 関数の動的定義ができる。
  • 関数は定義するといきなり関数オブジェクトになっている。
  • インターフェイス定義がなく、「名前」が一致していればそれを使う。(レイトバインディング(late-binding)、名前呼び出し(call-by-name))
  • カプセル化(情報隠蔽)がない。丸見え。(処理系が持つライブラリには DontEnum 属性という特殊な隠蔽属性がある。)
  • スコープに暗黙のオブジェクトがある。
    • var a; 抜きで a = 1; などと書いた場合、その暗黙のオブジェクトのプロパティが追加/更新されてしまう。(Global オブジェクトが破壊されて、スコープを超えたトラブルを引き起こす可能性がある。)
      var a = b = c = 0; // これはダメ。bとcは宣言されていないとみなされる。
      var a, b, c; a = b = c = 0 // これは正しい。
      var a=0, b=0, c=0; // これは正しい。
  • with (変数名) で暗黙のオブジェクトを指定することができる。
    • with を使うと名前空間もどきを作ることができる。
  • 「プロトタイプ」(prototype プロパティ)を使うことで、「継承」と「クラス変数」「クラスメソッド」を実現している。

あんちょこ

HTML に入れるときの基本

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<script type="text/javascript"><!--
// ここにスクリプト
// --></script>
</head>
<body>
<noscript>
<p>JavaScript を ON にしてね。</p>
</noscript>
</body>
</html>
  • Content-Type 指定は必要。これがない上に、HTTP レスポンスヘッダでも指定なしだと自動判別になって文字化けする場合がある。最悪スクリプトがまったく動かなくなる。
  • onxxx 属性のスクリプト言語の指定のために、Content-Script-Type 指定が必要。
  • noscript では、「JavaScriptを使え」という指示ではなく、JavaScriptが使えない環境向けの表示をすべき。(テキストベースでの表示にするとか。)
  • 別ファイルを呼び出す場合は src 属性で指定する。
    <script type="text/javascript" src="hoge.js"></script>

変数の定義、参照

var x;
x = 1;
var x = 1;
alert(x);

var を書かず、いきなり x = 1 と書いた場合、スコープが持つ暗黙のオブジェクトに対するプロパティ追加とみなされる。(一般には Global オブジェクトに追加/更新 :( )

変数のスコープ

var x = 1;
function do_test()
{
    alert(x); // undefined
    var x = 2;
    alert(x); // 2
    if (true) {
      var x = 3;
      alert(x); // 3
    }
    alert(x); // 3 (注意!)
}
  • ブロックスコープがない。ブロック内で同名の変数を再宣言しても、関数スコープの変数とみなされる。

関数の定義、参照。

function myFunc(arg)
{
    alert(arg);
}

myFunc("Hello, world!");

関数オブジェクトを変数に取り込む

function myFunc(arg)
{
    alert(arg);
}
var alertObj = myFunc
alertObj("Hello, world"); 

関数の引数の数が不定の場合の取り扱い

  • 不足している場合は undefined が引き渡される。
  • arguments 配列で引数にアクセスできる。

関数を動的に定義する。

var myFunc = new Function("arg", "alert(arg);");
myFunc("Hello, world!");

以下と同等。

function myFunc(arg)
{
    alert(arg);
}
myFunc("Hello, world!");

関数スコープに動的に変数を追加する。

function myFunc()
{
    eval("var x");
    x = 1;
}
  • 意味ある?

クラス定義方法色々

  • JavaScript OOP におけるクラス定義方法 http://d.hatena.ne.jp/amachang/20060516/1147778600
  • 「継承」を気にしないなら、プロトタイプを使わなくてもOOPの実装はできる。
    • 単なる「名前呼び出し」なので「名前」さえ合わせればポリモーフィズムになる。
  • 「継承」を考えると、プロトタイプを使う方が、どちらかというと「正しい」
  • 「継承」を考えると、プロトタイプに追加する方が、どちらかというと「正しい」
    • 継承ツリーの最上位ならプロトタイプの上書きでよい。

クラス定義せずに、いきなり複数のプロパティ持ったインスタンスを生成する。

var point = { x:1, y:2 };

空のコンストラクタ

コンストラクタはただの関数。

function MyClass()
{
}

インスタンスを生成する。

function MyClass()
{
}
var myObj = new MyClass();

new は、まず、空のインスタンスを生成して、関数 MyClassの this にそれを引き渡す。

もしも new を使わないと this には Global インスタンスが使われる。(要注意!)

コンストラクタでメンバ変数を定義する。

this に相当するインスタンスは new 演算子が作って渡してくれる。後はそこにプロパティを追加する。

function MyClass()
{
    this.message = "Hello";
}

コンストラクタで引数を受け取る。

function Point(x, y)
{
    this.x = x;
    this.y = y;
}
 
var myPoint = new Point(1, 2);

メンバを定義する。(プロトタイプ版)

function MyClass()
{
    this.initialize.apply(this, arguments) // ← 委譲
}

MyClass.prototype = {
    initialize: function(n, m) {
      this.n = n;
      this.m = m;
    }, // ←ハッシュなので、カンマ区切りである点に注意。

    myMethod: function(x) {
        ...
    },

    foo: function(a, b) {
        ...
    }
}

メンバを追加する。(プロトタイプ版)

function MyClass()
{
} 

// プロトタイプとして変数を定義すると、
// インスタンスに変数が未設定の場合には、
// この値が参照されることになる。デフォルト値のイメージ。
MyClass.prototype.myVar = null;

MyClass.prototype.myMethod = function(x) {
        ...
}; // ← 代入なのでここはセミコロン

MyClass.prototype.foo = function(a, b) {
        ...
};

メンバを定義する。(非プロトタイプ版)

  • プロトタイプを使わない、最も単純な実装。
function MyClass()
{
    this.myVar = null;
    this.myMethod = function(x) {
        ...
    };
    this.foo = function(a, b) {
        ...
    };
}

メンバ関数を追加する。(非プロトタイプ版)

function MyClass()
{
}
MyClass.myMethod = function(x) {
    ...
}

MyClass.foo = function(a, b) {
    ...
}

変数はプロトタイプで定義するべきか、コンストラクタで this に定義するべきか?

// this に定義
function MyClass()
{
  this.myInstanceVar = 0;
}
// prototype に定義
this.prototype.myPrototypeVar = 0;
  • どちらを使っても、インスタンス単位でのデータ保持になるので問題はない。
    • プロトタイプで定義してあっても、インスタンスに対しての書き込みでインスタンスに値が保持されるようになる。
    • 上の例で this.myPrototypeVar = 1; とすると、そのインスタンスからは 1 が見え、他のインスタンスからは 0 が見える。
  • プロトタイプで定義すると、デフォルト値として利用することができる。
  • プロトタイプの上書きを使うと、「クラス変数」のように扱える。(ただし、インスタンスに対する書き込みをするとだめ。)
  • this で定義すると、インスタンスのメモリ容量を食うことになる。(普通はそれほど気にする量ではない。)

インスタンスのメンバの参照

myObj.myProp

あるいは

myObj["myProp"]

文字列が使えるので、文字列での動的な処理変更ができる。

コンストラクタにメンバを動的に追加する。

MyClass.prototype.message = "Hello";

コンストラクタからメンバを動的に削除する。

delete MyClass.prototype.message;

インスタンスにメンバを動的に追加する。

function Point(x, y)
{
    this.x = x;
    this.y = y;
}

var myPoint = new Point(1, 2);
myPoint.z = 3; // いいのか悪いのか…

インスタンスからメンバを動的に削除する。

function Point(x, y)
{
    this.x = x;
    this.y = y;
}

var myPoint = new Point(1, 2);
delete myPoint.y; // いいのか悪いのか…

クラスのインスタンスかどうかを判定する。

if (myObj instanceof MyClass)

インスタンスにメンバがあるかどうかを判定する。

if ("myMember" in myObj) 
  • myObj.myMember == undefined であることを期待するコードが結構あるが、あまり良くない。
    • メンバがあることと、undefined が読み出せることとは別の話。メンバはあるけど、undefined が読み出されることはある。
  • DontEnum 属性が付いている場合は in は使えない。
    • システムで用意されているメンバの存在確認をしたい場合は、以下のようにして判断する。
      if (typeof obj.member == "undefined")
    • typeof 演算子は JavaScript 1.1 以降。ふつうは問題にならないはず。

インスタンス自身にメンバがあるかどうかを判定する。

function MyClass()
{
}
MyClass.prototype.myMember = 1;

var myObj = new MyClass();
alert(myObj.hasOwnProperty("myMember")); // false
alert(myObj.myMember);
// myObj には myMember メンバはあるが、実際には prototype が所持している。

myObj.myMember = 2;
alert(myObj.hasOwnProperty("myMember")); // true
alert(myObj.myMember);
  • prototype が持っていても、インスタンスが持っていなければ持っていないと判定される。
  • 何に使うんだろう… 継承できないことを意味しているので、デバッグのときくらいしか用がなさげ。

インスタンスのメンバを列挙する。

for (var m in myObj) // m にはメンバ名が入る
{
    document.writeln(m + " = " + myObj[m]);
}
  • DontEnum 属性の付いているメンバは列挙されない。
    • 一般に、組み込みのメンバは DontEnum が付いていて列挙されない。
    • DontEnum 属性をつける方法がない。自作のメンバには付けられない。
  • 一般的な for each とはまったく違う。「メンバ列挙」以外に使うのは危険!

with を使った名前空間もどき

myspace = {}; with(myspace) {

hello = function() {
    alert("Hello, world!");
}

}

myspace.hello();

文末のセミコロンについて

  • セミコロンが文末を示す。
    x = 1;
  • セミコロンを付けなければ、1文を複数行に分けることができる。
    x =
     1;
  • しかし、セミコロンを付けなくても文末になる場合がある。簡単に言うと、セミコロンがない場合で、複数行つながっていると解釈すると文法的に成立しない場合に行末にセミコロンが自動挿入される。(色々条件あり)
    x = 1 ←ここに自動挿入
    y = 2
    • こんな自動挿入あてにするな! バグの元。常に文末にはセミコロンを付けよ。

デバッガ

メモ


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-11-27 (金) 15:51:20 (284d)