Subterranean Flower

JavaScriptでよく見るエラーとその対策

Author
古都こと
ふよんとえんよぷよぐやま!!!

プログラミングにおいて避けては通れないのがエラーです。JavaScriptにおいても、多数のエラーに悩まされることになります。

エラーはそのメッセージさえ読めば原因がわかることも多いのですが、プログラミングに慣れてない人がJavaScriptの柔軟なコードに振り回されていると、なかなかメッセージからは原因を推察できません。

そこでこの記事ではよくあるエラーとその対策についてまとめます。

よくあるJavaScriptエラーの原因と対策

基本的に以下の環境での実行とします。

  • Google Chrome

しかしエラーメッセージはFirefoxやNode.jsでもそう変わらないと思います。

Chromeにおいて、エラーを確認するためのコンソールはF12キーを押すと表示できます。確認したいページを開いて、そこでF12を押してください。

Uncaught SyntaxError: Unexpected token 記号

  • エラーの理由:予期しないトークンが見つかった
  • よくある原因:文法ミス。カッコの開き忘れ、カンマの位置がおかしい、など
  • 対策:文法に気をつける

エラーを訳すと「キャッチされなかった文法エラー:予期しないトークン 記号」になります。記号はカンマであったりカッコであったりします。

このエラーは基本的に文法エラーで、開き括弧が足りないとか、カンマの位置がおかしいとか、そんなので起こるエラーです。

function myF,unc() {} // そこにカンマは無理

このエラーについてはあまり便利な解決方法はなく、文法を間違えないことが大事になります。ただ、高機能なエディタを使うと、対応する括弧を探してくれる機能や、括弧の中を非表示にする機能がついていることもあります。そういったものを使うと見つけやすくなるでしょう。

Uncaught SyntaxError: Unexpected end of input

  • エラーの理由:予期しない入力の終わりが見つかった
  • よくある原因:開き括弧に対して閉じ括弧の数があっていない
  • 対策:文法に気をつける

訳すと「キャッチされなかった文法エラー:予期しない入力の終わり」です。なんのこっちゃ。

このエラーはだいたい括弧の数があっていない時に発生します。括弧開いたものの閉じてない、という状況です。

function myFunc() {
    if(true) {
        console.log('Helllo');
    }

これに関しても特に便利な解決法はないので、地道に閉じ忘れを探しましょう。

Uncaught ReferenceError: 変数名や関数名 is not defined

  • エラーの理由:変数名や関数名が定義されていない
  • よくある原因:名前の打ち間違え / ライブラリが読み込めていない
  • 対策:名前を打ち間違えない / ライブラリをきちんと読み込む

よく見かけるのがReferenceErrorです。このエラーを訳すと「キャッチされなかった参照エラー:変数名は定義されていません」となります。

参照エラーはよくあるエラーの一種です。jQueryを使おうとして「$ is not defined」となった経験のある人も多いのではないでしょうか。このエラーメッセージは定義していない変数を参照すると発生します。例えば以下のコードを実行してみます:

const foo = 'Hello!';
console.log(bar);

これを実行すると「Uncaught ReferenceError: bar is not defined」が発生します。このコードではfooという変数は定義していますが、barという変数は定義していません。それなのにbarという定義していない変数にアクセスしようとして、エラーが発生しています。

関数の場合においても同様です。定義していない関数を呼び出そうとするとエラーになります。

また、変数のスコープ間違えもよくある原因です。JavaScriptにおいて変数はブロック({と}で囲まれた部分)の間だけアクセスできます。しかしブロックの外からアクセスしようとするとエラーになります。この仕組みが変数のスコープです。

function myFunc() {
    const myVar = 'Hi!'; // ここで定義したけど
}

console.log(myVar); // ここではエラー!!

ブロックは関数やfor文、if文などと共によく使われます。変数のスコープには気をつけましょう。

このエラーは自分の定義した変数/関数のみを扱うのならまだ気づくことができますが、あらかじめ定義されているものを呼び出そうとしているときは気づきにくくなります。例えばsetTimeoutをsetTImeout(Iが大文字になっている!)と打ち間違えると、エラーになります。

ReferenceErrorが起きたときは、まずは打ち間違いを疑いましょう。また、変数のスコープについても注意です。

外部のライブラリについてこのエラーが出るときは、ライブラリをちゃんと読み込めているかどうかも要チェックです。URLが間違っているとか、scriptタグの記述順(上の方が先に読み込まれる!)とか、いろいろあります。例えば「$ is not defined」と言われた場合はjQueryを正常に読み込めてません。

Uncaught TypeError: Cannot read property ‘プロパティ名’ of undefined

  • エラーの理由:undefinedの’プロパティ名’にアクセスしている
  • よくある原因
    • オブジェクトの存在しないプロパティの何かにアクセスしている
    • 配列のインデックス間違い
    • 関数のreturn忘れ
  • 対策
    • 名前を打ち間違えない
    • そのオブジェクトが本当にそのプロパティを持っているか調べる

このエラーは初学者を混乱させるエラーのひとつです。メッセージを訳すと「キャッチされなかった型エラー:undefinedの ‘プロパティ名’ プロパティを読めません」となります。

このエラーは例えば以下のようなコードで発生します。

const message = 'Hello';
console.log(message.foo.length); // エラー!!

実にシンプルなコードですね。文字列であるmessageを定義し、messageのfooのlengthを呼び出しています。

ここで注目したいのは、文字列にfooなどというプロパティは存在しないというところです。しかし文字列のfooを呼び出してもエラーにはならず、undefinedが返ってきます。

例えば以下のコードではエラーは発生せず、undefinedが出力されます。

console.log('Hello'.foo); // undefined

そうです。変数の参照と違って、オブジェクトのプロパティへのアクセスではエラーは発生しないのです。かわりにundefinedが黙って返されます。そしてその返ってきたundefinedの何かにアクセスしようとして初めてエラーになります。

console.log('Hello'.foo); // undefined
console.log('Hello'.foo.length); // Uncaught TypeError: Cannot read property 'length' of undefined

ここでは’Hello’.fooが存在しないためundefinedとなり、そのundefinedのlengthにアクセスしようとしてエラーが発生しています。

オブジェクトを連想配列(辞書)として利用した時も同様の現象は発生します。

const obj = { myKey: 'myValue' };
console.log(obj['myKey2']); // myKey2は存在しないのでundefined
console.log(obj['myKey2'].repeat(10)); // undefinedのrepeatは存在しないのでエラー

名前の打ち間違えなどがよくある原因になりますね。

また、undefinedは配列の存在しないインデックスにアクセスしたときも返されます。例えば長さ3の配列の5番目にアクセスするとundefinedとなります。そのundefinedに対してプロパティアクセスを試みると……見事にエラーになります。

const array = [0, 1, 2];
console.log(array[5]); // undefined
console.log(array[5].toString()); // エラー!!
 // undefined

他にもよくあるのが関数でのreturn忘れです。returnがない関数は、undefinedを返します。

function myFunc() { const sum = 1 + 2; }
console.log(myFunc().toString()); // エラー!!

返ってきたundefinedに対してtoString()を呼び出しているので、「undefinedのtoStringにアクセスできないよ!」と怒られているわけです。

固定値や普通の変数を扱っているうちは、まだ対策がしやすいのですが、外部から値を入れられたり、通信でJSONを取ってくるなどとなると、なかなか対策が難しくなります。そういった場合は、本当にプロパティが存在するかどうか、あらかじめ調べてから使用すると安全です。in演算子を使えばオブジェクトの中のプロパティをチェックできます。

const response = { "message": "Not Found" }; // これが通信で返ってきたとする
if('token' in response) { // reponseの中のtokenというプロパティの存在を確認
    // 本来の処理
    console.log(`トークンは${token}です`);
} else {
    // responseにtokenが入ってなかった時の処理
    console.error('トークンが返ってきてないよー!');
}

オブジェクトのプロパティではなくプリミティブ型(文字列, 数値など)のundefinedチェックを行いたい場合はtypeof演算子を使うと安全です。

function myFunc(message) {
    if(typeof message === 'undefined') {
        console.error('メッセージがないよー!');
    }
}

ただ、あまりチェックを厳密にしても煩雑なだけになるので、ある程度は値はしっかり入っているということを前提に組んでもいいでしょう。

Uncaught TypeError: Cannot read property ‘プロパティ名’ of null

  • エラーの理由:nullの’プロパティ名’にアクセスしている
  • よくある原因:何かを探す関数などでnullが返ってきている
  • 対策 :nullチェックする

undefinedのTypeErrorの亜種ですね。値がnullに変わっただけで理由は同じです。

これがよくあるのは「何かを探す」系のメソッドが、何も見つからない時にnullを返すことがあるからです。つまり検索の条件とかが間違ってます。例えばdocument.querySelectorとかで起こります。

console.log(document.querySelector('adcefwhvc')); // そんな要素はないのでnullを返す
console.log(document.querySelector('adcefwhvc').toString()); // nullにtoString()はない

これを防ぐには返ってきた値がnullかどうかチェックするといいでしょう。nullチェックをするには、対象がオブジェクトの場合は単純にifの中に突っ込めば判定できます。ifにそのまま突っ込むのは文字列や数値でもできますが空文字列や0もfalse扱いになるので注意。文字列や数値のnullチェックは「str === null」みたいな感じで比較するとできます。

const elem = document.querySelector('invalid');
if(elem) { // または elem === null で比較
    console.log('発見!', elem);
} else {
    console.error('見つからない');
}

ただ、探すものが決まりきっててnull返ってこないはずな場合は、チェックせずにエラー垂れ流しの方が気付きやすいと思います。

Uncaught TypeError: オブジェクト.メソッド名 is not a function

  • エラーの理由:関数以外のオブジェクトを関数呼び出ししている
  • よくある原因:undefinedを関数呼び出ししている
  • 対策:undefinedに気をつける

これもよく見るエラーのひとつです。訳すと「キャッチされなかった型エラー:オブジェクト.メソッド名は関数ではありません」です。メソッド呼び出してるのに関数じゃないってどういうことなんだろう……。

実はこれも先ほどのundefinedと関係があることが多いです。まず前提知識として、このエラーは「関数以外を関数呼び出しする」と発生します。関数呼び出しは値の後ろにカッコをつけることです。「func()」とかです。例えば文字列を関数呼び出しするとこのエラーが発生します。

'Hello'();

当然undefinedを関数呼び出ししても起こります。undefinedは関数ではないので。そしてオブジェクトの中の存在しないプロパティにアクセスするとundefinedが返ってきます。これらをあわせて考えると……。

console.log('string'.foo); // undefined
console.log('string'.foo()); // undefined()をやろうとしてエラーになる

‘string’.fooは存在しないのでundefinedです。それに()をつけて「undefined()」とやってエラーが出ているわけです。つまり、だいたいタイプミスかメソッド名の勘違いです。undefinedには気をつけましょう。

Uncaught SyntaxError: Unexpected token < in JSON at position 0

  • エラーの理由:JSONとしてパースできない
  • よくある原因:通信でJSON以外が返ってきてる
  • 対策:ステータスコードなどをチェックする

番外編。JavaScriptというよりJSON周りのエラーですが、よく起こるので……。このエラーは訳すと「キャッチされなかった文法エラー:予期しないトークン < がJSONの0文字目にあります」となります。

これが起こる原因はだいたいの場合においてJSONを返すAPIにアクセスしたのに、JSONではなくHTMLが返ってきてるからです。HTMLの1行目は「<!DOCTYPE html>」になりますが、この最初の文字の<がJSONとしてダメなのでエラーになってます。

そもそもJSON返すAPIにアクセスしたのになんでHTMLが……?となると思いますが、404 Not Foundなどの場合はHTMLのエラーページが返る場合があります。サーバの設定にもよりますが。

アクセス先間違ってたり、API側がバグってたり、いろいろな原因があると思いますが、これを防ぐにはステータスコードをチェックするといいと思います。Fetch APIならレスポンスオブジェクトのokをチェックすると正常に通信できたか確認できます。XHRやjQueryやaxiosでやる方法は適当にググってください。

fetch('aaa').then((response) => {
    if(response.ok) {
        return response.json();
    } else {
        console.error('読み込めないよー!');
    }
});

また、JSONに変換する前に一度テキストとしてコンソールに表示して、本当にJSONが返ってくるか確認しながら作るのもいいでしょう。