JavaScript 速習チュートリアル

JavaScript でよくある間違い

1. 代入演算子の誤用

JavaScriptプログラムにおいて、if ステートメント内で比較演算子(==)の代わりに代入演算子(=)を誤って使用すると、予期しない結果が生成されることがあります。

以下の if ステートメントは、x が 10 と等しくないため、(期待通り)false を返します。

let x = 0;
if (x == 10)

しかし、以下の if ステートメントは 10 が true と評価されるため、(期待に反して)true を返します。

let x = 0;
if (x = 10)

また、以下の if ステートメントは 0 が false と評価されるため、(期待に反して)false を返します。

let x = 0;
if (x = 0)

代入(Assignment)は常に代入された値を返します。

1.1 緩やかな比較への期待

通常の比較では、データ型(Data type)は考慮されません。以下の if ステートメントは true を返します。

let x = 10;
let y = "10";
if (x == y)

厳密な比較(Strict comparison)では、データ型が考慮されます。以下の if ステートメントは false を返します。

let x = 10;
let y = "10";
if (x === y)

switch ステートメントが厳密な比較を使用することを忘れてしまうのは、よくある間違いです。

以下の case switch はアラートを表示します。

let x = 10;
switch(x) {
  case 10: alert("Hello");
}

しかし、以下の case switch はアラートを表示しません。

let x = 10;
switch(x) {
  case "10": alert("Hello");
}

2. 加算と連結の混同

加算(Addition)は数値を足すことであり、連結(Concatenation)は文字列を繋ぐことです。
JavaScriptでは、どちらの操作も同じ + 演算子を使用します。

このため、数値を数値として加算する場合と、数値を文字列として加算する場合では結果が異なります。

let x = 10;
x = 10 + 5;       // x は 15 になります

let y = 10;
y += "5";        // y は "105" になります

2つの変数を加算する場合、その結果を予測するのが難しい場合があります。

let x = 10;
let y = 5;
let z = x + y;     // z は 15 になります

let x = 10;
let y = "5";
let z = x + y;     // z は "105" になります

3. 浮動小数点の誤解

JavaScriptのすべての数値は、64ビットの浮動小数点数(Floats)として保存されます。
JavaScriptを含むすべてのプログラミング言語は、正確な浮動小数点値の扱いに困難を抱えています。

let x = 0.1;
let y = 0.2;
let z = x + y      // z の結果は 0.3 にはなりません

この問題を解決するには、一度整数に変換してから計算し、再び割る方法が有効です。
例:

let z = (x * 10 + y * 10) / 10;       // z は 0.3 になります

4. JavaScript文字列の改行

JavaScriptでは、ステートメントを2行に分割することができます。
例 1:

let x =
"Hello World!";

しかし、文字列の途中でステートメントを分割すると動作しません。
例 2:

let x = "Hello
World!";

文字列内でステートメントを分割する必要がある場合は、「バックスラッシュ(\)」を使用する必要があります。
例 3:

let x = "Hello \
World!";

5. セミコロンの配置ミス

セミコロンの配置を誤ると、x の値に関わらず以下のコードブロックが実行されてしまいます。

if (x == 19);
{
  // コードブロック 
}

6. returnステートメントの改行

行の終わりで自動的にステートメントを閉じるのが、JavaScriptのデフォルトの挙動です。
そのため、以下の2つの例は同じ結果を返します。

例 1:

function myFunction(a) {
  let power = 10 
  return a * power
}

例 2:

function myFunction(a) {
  let power = 10;
  return a * power;
}

JavaScriptでは、ステートメントを2行に分割することも可能です。そのため、例3も同じ結果を返します。

例 3:

function myFunction(a) {
  let
  power = 10; 
  return a * power;
}

しかし、次のように return ステートメントを2行に分割するとどうなるでしょうか。
例 4:

function myFunction(a) {
  let
  power = 10; 
  return
  a * power;
}

この関数は undefined を返します。

なぜでしょうか? JavaScriptはあなたが次のように意図したと解釈したからです。
例 5:

function myFunction(a) {
  let
  power = 10; 
  return;
  a * power;
}

解説:
もしステートメントが以下のように不完全であれば:
let
JavaScriptは次の行を読んでステートメントを完了させようとします:
power = 10;

しかし、このステートメントは完結しているため:
return
JavaScriptは自動的に次のように閉じます:
return;

これは、JavaScriptにおいてセミコロンによるステートメントの終了がオプション(任意)であるために起こります。
return はそれ自体で完結したステートメントであるため、JavaScriptは行末で return ステートメントを閉じます。
絶対に return ステートメントを途中で改行しないでください。

7. 名前付きインデックスによる配列アクセス

多くのプログラミング言語は、名前付きインデックス(Named indexes)を持つ配列をサポートしています。
名前付きインデックスを持つ配列は、連想配列(Associative arrays / ハッシュ)と呼ばれます。
JavaScriptは、名前付きインデックスを持つ配列をサポートしていません。

JavaScriptでは、配列(Arrays)は数値インデックス(Numbered indexes)を使用します。
例:

const person = [];
person[0] = "John";
person[1] = "Doe";
person[2] = 46;
person.length;       // person.length は 3 を返します
person[0];           // person[0] は "John" を返します

JavaScriptでは、オブジェクト(Objects)が名前付きインデックスを使用します。
配列へのアクセスに名前付きインデックスを使用すると、JavaScriptはその配列を標準のオブジェクトに再定義します。
自動的な再定義の後、配列のメソッドやプロパティは undefined や誤った結果を生成することになります。

例:

const person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
person.length;      // person.length は 0 を返します
person[0];          // person[0] は undefined を返します

8. 定義末尾のカンマ

オブジェクトや配列の定義における末尾のカンマ(Trailing commas)は、ECMAScript 5 で合法となりました。

オブジェクトの例:

person = {firstName:"John", lastName:"Doe", age:46,}

配列の例:

points = [40, 100, 1, 5, 25, 10,];

警告!!
Internet Explorer 8 ではクラッシュします。
また、JSONでは末尾のカンマは許可されていません。

JSON:

person = {"firstName":"John", "lastName":"Doe", "age":46}

JSON:

points = [40, 100, 1, 5, 25, 10];

9. UndefinedはNullではない

JavaScriptのオブジェクト、変数、プロパティ、およびメソッドは undefined になることがあります。
さらに、空のJavaScriptオブジェクトは null という値を持つことができます。

これにより、オブジェクトが空であるかどうかのテストが少し難しくなる場合があります。
型が undefined であるかどうかをテストすることで、オブジェクトが存在するかどうかを確認できます。

例:

if (typeof myObj === "undefined")

しかし、オブジェクトが null であるかどうかをテストすることはできません。なぜなら、オブジェクトが undefined の場合にエラーがスローされるからです。

誤った方法:

if (myObj === null)

この問題を解決するには、オブジェクトが null ではなく、かつ undefined でないことをテストする必要があります。
しかし、これでもまだエラーをスローする可能性があります。

誤った方法:

if (myObj !== null && typeof myObj !== "undefined")

そのため、null であるかどうかをテストする前に、必ず undefined ではないことをテストしなければなりません。

正しい方法:

if (typeof myObj !== "undefined" && myObj !== null)