読者です 読者をやめる 読者になる 読者になる

Javascript 注意点

Javascript

 Javascriptを使うにあたって

注意すべきところをメモっておこう。

会社、家でJavascriptプログラミング時に

経験したのを書くつもりだ。

 

もちろん、全て汎用的な内容だし、本に書いてある内容も

あるはずだ。

-----------------------------------------------

1)オブジェクトリテラルで最後の要素の後にコンマをつけてはならない。

=> IE7でエラーになる。

例)var o = {    name : "Duke",    hobby : ["movie", "animation", "reading", ""]    , // ここにコンマを入れちゃだめ!!};

 hobbyの次にコンマを入れるとIE7でエラーになる。

IE8とFireFoxChromeではコンマを入れても問題ないが、

世の中未だIE6を使う人がいる現実を考えると

コンマを抜く方が精神健康上、いいだろう。

 

2)配列の最後の要素の後にもコンマをつけてはいけない。

いけないというか、ブラウザ間の動作が違うので避けましょうとの話だ。

 

今回もIEが問題だ。バージョン関係なく(正確にはIE7とIE8)。

var a = [1, 2, ];

console.log(a.length);

// 結果

// IE7 : 3

// IE8 : 3

// Chrome : 2

// FireFox : 2

IEでは最後のコンマの後をundefined要素と見なしているようだ。

ChromeFireFoxでは本当の要素のみを配列の要素と見なしているようだ。

とにかく、結果が一つになる文法を使おう。

配列の最後の要素の後にコンマを入れちゃアカン!

 

3)with文は使っちゃだめ。

ある意味すごく便利なキーワードだが、間違った動作を起こす恐れがある。

(Visual BasicのWith-End Wih文と同じ感じ) 

単純に参照名の省略だけでなく、内部で参照オブジェクトの有無を判別する

分岐文など、余分のコードが実行される。

コスト的にも問題だが、with文の内部がおかしくなると、バグを探すのが

非常に難しくなるので、使うのはやめた方がいい。

 

例えば、以下のコードは

with (obj) {

    a = b;

}

  実際は以下のコードと同じ処理をする。

f (obj.a === undefined) {

    a = obj.b === undefined ? b : obj.b;

} else {

    obj.a = obj.b === undefined ? b : obj.b;

}

 

 

出典 

JavaScript: The Good Parts: Working with the Shallow Grain of JavaScript

JavaScript: The Good Parts: Working with the Shallow Grain of JavaScript

 

  

これを見ると分かるように実際どの処理が行われるかを

すく知ることはできない。

よって、with文は避けた方がいい。

 

4)parseInt使用する時、2番目の引数に進数を指定しなければならない。

var str = "051";

var a = parseInt(str);

console.log(a);

 

 

さあ、何が表示されるか。

 

51?

 

ブー。

 

41が表示される。

 

なぜなら、parseIntにソース文字列が何進数かを指定しないと、

文字列表現を見て自動判別するからだ。

 

前に0がつくと、8進数と判断する。

前に0xがつくと、16進数と判断する。

 

例の051は前に0がついているので8進数の51と判断し、

それを10進数の41に変換したのだ。

 

こういう自動判別を防ぐためには2番目の

引数に進数を指定すればいい。

 

var str = "051";

var a = parseInt(str, 10); // "strは10進数です"と指定する。

console.log(a);

 

  

結果は正しく51になる。

 

私たちが普段外からもらう数字は10進数なので、

parseIntの2番目の引数には必ず10をつけるように

しよう。

 

5)==, !=の代わりに===、!==を使おう。

Javascriptで==と!=は互いのオペランドの型が違う場合、

強制に型変換が行われる。

便利な面もあるが、分かりにくいバグを出してしまう場合もある。

 

以下の例のように想像と全然違う結果を出してしまう。

1)'' == '0'           // false

2)0 == ''            // true

3)0 == '0'          // true

4)false == '0'    // true

5)' \t\r\n ' == 0 // true

 

===と!==は型の変換が行われず、実際型までチェックするので

安心だ。

 

6)より安全なクラス宣言の仕方  --- 7)を参照すること。

Javascriptでクラス宣言する方法は色々ある。

 

次にPersonというJavaクラスがある。

public class Person {

    private final String name;

    private final int age;

    public Person(String name, String age) {

        this.name = name;

        this.age = age;

    }

    public String getName() {        return name;

    } 

    public int getAge() {

        return age;  

    }

}

 

上記のPersonクラスをJavascriptで書いてみると、

方法は色々あるが、二つだけ、挙げてみる。

 

1番目

var Person = function(name, age) {

    this.name = name;

    this.age = age;

};

Person.prototype = { // publicメソッド

    getName : function() {

        return this.name;

    },

    getAge : function() {

        return this.age;

   }

};

  

 

Personクラスの宣言とメンバーメソッドの宣言が分かれている。

長所 : 

1)インスタンス生成時、prototypeに宣言されている内容を再利用するのでメモリ管理面で有利である。

短所 :

1)宣言部が分かれているので少々分かりにくい印象がある。

2)publicメソッドでprivateメソッドが呼べない。prototypeではthisで始まるメンバーしか呼べない。

 

2番目

var Person = function(name, age) {

    this.name = name;

    this.age = age;

 

    // private field

    var count = 0;

 

   // private method

    var getCalledCount = function() {

       return count++;

    }

 

    // public method

    this.getName = function() {

        return this.name + " : " + getCalledCount();

    };

    this.getAge = function() {

        return this.age;

    };

};

 長所:

1)宣言部が統合されているので分かりやすい(変数の使用スコープが把握しやすいので)。一般的なクラス宣言に近い。

2)publicメソッド内でprivateメソッドが呼べる。

長所:

1)インスタンス生成時(つまり、new時)、毎回メソッドを動的に生成するので遅い。

 

答えは出た。

Javascriptはプロトタイプ型OOP言語だが、まだprototypeに慣れていない人が多いので

無理して使っても、メンテナンスする人に文句ばかり言われるだろう。

 

で、2番目のような宣言方式を使えば無難ではないかと思われる。

 

速度の問題はあるが、これ以外の問題(間違ったJavascriptの使用を意味)がもっと多いはずなので、

別に問題にならないと思う。クライアントPCの性能もよくなったし。

 

まず、内容が把握しやすくてメンテナンスがしやすい2番目を取ってみて、

本当に遅くなったと感じた(おそらくないと思うのだが)時、1番目を取るという方式がいいだろう。

 

7)クラスをnewしてオブジェクトを生成するより、オブジェクトリテラルを使う。

6)でより安全なクラス宣言の仕方を言っているが、実はこの記事全体は2年前に書いた内容で、今では好ましくないと思われる。

 

なぜなら、Javascriptではクラス宣言にも関数宣言にもfunctionキーワードを使うため、

クラスとして宣言しているのに間違って関数として実行してしまうおそれがあるからだ。

 

例えば、以下のように点を意味するPointクラスを定義したとしよう。

function Point(x,y) {   

    this.getX = function() {       

        return x;   

    }   

    this.getY = function() {       

        return y;   

    }

};

 

 これは普通に以下のようにnewキーワードをつけて呼び出すと、問題なくオブジェクトを生成できるが、

var p = new Point(10, 15);

p.getX();

10

  

以下のようにnewをつけないと、グローバルオブジェクトを汚してしまう。

Point(10,15);

window.getX();

10

 

 簡単な例だが、大規模なプロジェクトでは、探しにくいバグの元になるだろう。

 

なので、代わりにオブジェクトリテラルを使う方がいい。

例えば、上記のPointは以下のようにオブジェクトリテラルを使える。

 function createPoint(x, y) {

    return {

         getX : function() {

             return x;

         },

         getY : function() {

             return y;

         }

    };

}

var p = createPoint(10, 15);

p.getX();

10

newをつける必要もなく、グローバルオブジェクトを汚すこともない。

Object.createを使うと、継承もできる。

なので、prototype方式を使ったクラス宣言も使う必要がなさそう。

Javascriptでオブジェクト生成はオブジェクトリテラルに限ると思う。