本日、彼らの研究の翻訳を公開します。
分析手法
最近はデータがすべてなので、 JavaScriptプロジェクトで最もよく見られるエラーを見つけ、分析し、ランク付けしました。 つまり、プロジェクトごとにエラー情報が収集され、その後、各タイプのエラー数が計算されました。 エラーはチェックサムごとにグループ化され、その計算方法はこちらで確認できます 。 このアプローチを使用すると、たとえば、あるプロジェクトで特定のエラーが見つかった場合、そのエラーが別の場所で見つかった場合、そのようなエラーはグループ化されます。 これにより、調査に関係するすべてのプロジェクトを分析した後、エラーの簡単な概要を取得することができ、巨大なログファイルのようなものではなく、作業に不便です。
調査中、最も一般的なエラーに特別な注意が払われました。 そのようなエラーを選択するために、エラーが発生したさまざまな企業のプロジェクトの数によってランク付けされました。 この評価に特定のエラーの発生総数のみが含まれている場合、非常に大きなプロジェクトに典型的であるが、他のプロジェクトではめったに見られないエラーは、結果をゆがめます。
以下は、調査の結果に基づいて選択された10個のエラーです。 それらは、発生したプロジェクトの数でソートされます。
JSプロジェクトで最も頻繁に発生するエラー
エラー名は、システムが発行するエラーメッセージの短縮版です。 システムメッセージに依存しているため、エラーが発生したときに簡単に特定できます。 次に、それぞれを分析し、それらの原因と対処方法について説明します。
1.不明なTypeError:プロパティを読み取れません
JavaScriptでプログラムを作成する場合、おそらく、このエラーは頻繁に発生します。 たとえば、プロパティを読み取ったり、未定義の変数、つまり値がundefinedのメソッドを呼び出したりしようとすると、Google Chromeで同様のエラーが発生します。 Chromeデベロッパーコンソールを使用して、このエラーの実際の動作を確認できます。
プロパティを読み取れませんエラー
このエラーはさまざまな理由で発生する可能性がありますが、多くの場合、ユーザーインターフェイス要素をレンダリングする際の状態の誤った初期化が原因です。 これが実際のアプリケーションでどのように発生するかの例を見てみましょう。 ここではReactを使用しますが、Angular、Vue、およびその他のフレームワークでは一般的に同じ初期化エラーが発生します。
class Quiz extends Component { componentWillMount() { axios.get('/thedata').then(res => { this.setState({items: res.data}); }); } render() { return ( <ul> {this.state.items.map(item => <li key={item.id}>{item.name}</li> )} </ul> ); } }
ここでは、2つの重要なことに注意する必要があります。
- 最初は、コンポーネントの状態(つまり
this.state
)は値undefined
表されます。
- 非同期的にデータをロードする場合、コンポーネントが
componentWillMount
またはcomponentWillMount
で行われたかどうかに関係なく、データがロードされる前に少なくとも1回表示されcomponentDidMount
。Quiz
要素が初めて表示されるとき、undefined
this.state.items
書き込まれthis.state.items
。 これは、itemList
が値undefined
表されるアイテムを受け取ることを意味します。 その結果、コンソールに次のエラーが表示されます:"Uncaught TypeError: Cannot read property 'map' of undefined"
。
このエラーは簡単に修正できます。 最も簡単な方法は、コンストラクター内の状態を適切なデフォルト値で初期化することです。
class Quiz extends Component { // : constructor(props) { super(props); // this.state = { items: [] }; } componentWillMount() { axios.get('/thedata').then(res => { this.setState({items: res.data}); }); } render() { return ( <ul> {this.state.items.map(item => <li key={item.id}>{item.name}</li> )} </ul> ); } }
アプリケーションコードは異なるように見えますが、プロジェクトでこのエラーを修正する方法と、外観を回避する方法を知っていることを望みます。 あなたが話していることがあなたにふさわしくないなら、おそらく以下のエラーがあなたを助けるでしょう。
2. TypeError: 'undefined'はオブジェクトではありません(評価中...
このエラーは、プロパティを読み取ったり、未定義のオブジェクトのメソッドを呼び出したりしようとすると、Safariブラウザーで発生します。 Safari開発者ツールコンソールを使用して、このエラーを確認できます。 実際、ここではChromeについて上記で説明したのと同じ問題がありますが、Safariでは別のエラーメッセージが表示されます。
エラー「未定義」はオブジェクトではありません
前の例と同じように、このエラーを修正します。
3. TypeError:nullはオブジェクトではありません(評価中
null
表される変数のメソッドまたはプロパティにアクセスしようとすると、Safariでこのエラーが発生し
null
。 Safari開発者コンソールでは、次のように表示されます。
エラーTypeError:nullはオブジェクトではありません
JavaScriptでは、
null
と
undefined
は同じものではないことを思い出してください。そのため、異なるエラーメッセージが表示されます。 変数に書き込まれた
undefined
値の意味は、変数に値が割り当てられていないことを示し、
null
は空の値を示します。
null
undefined
と等しくないことを確認するに
null
、厳密な等価演算子を使用してそれらを比較できます。
非厳密な等価演算子と厳密な等価演算子を使用して未定義とヌルを比較する
実際のアプリケーションでのこのエラーの原因の1つは、要素をロードする前にJavaScriptでDOM要素を使用しようとしたことです。 これは、DOM APIが空のオブジェクトへの参照に対して
null
を返すためです。
DOM要素を使用するJSコードは、DOM要素を作成した後に実行する必要があります。 JSコードは、HTMLドキュメントに表示されるとおり、上から下に解釈されます。 したがって、プログラムの
<script>
がDOM要素を記述するコードの前にある場合、プログラムはページの解析中にその完了前に実行されます。 このエラーは、スクリプトからアクセスするDOM要素がこのスクリプトをロードする前に作成されなかった場合に発生します。
次の例では、イベントリスナをコードに追加することで問題を修正できます。イベントリスナは、ページが完全にロードされたことを通知します。
addEventListener
を使用して追加されたイベントハンドラー
addEventListener
された後、
init()
メソッドはDOM要素で正しく動作できるようになります。
<script> function init() { var myButton = document.getElementById("myButton"); var myTextfield = document.getElementById("myTextfield"); myButton.onclick = function() { var userName = myTextfield.value; } } document.addEventListener('readystatechange', function() { if (document.readyState === "complete") { init(); } }); </script> <form> <input type="text" id="myTextfield" placeholder="Type your name" /> <input type="button" id="myButton" value="Go" /> </form>
4.(不明):スクリプトエラー
このエラーは、クロスドメイン制限ポリシーに違反したときに、キャッチされていないJavaScriptエラーがドメインの境界を越えると発生します。 たとえば、JSコードがCDNリソースでホストされている場合、スクリプトエラーに関するメッセージがキャッチされていないエラー(つまり、
try-catch
インターセプトされず
window.onerror
ハンドラーに到達しなかったエラー)に関するメッセージに表示されますが、役に立たないこのエラー情報を排除する目的で。 これは、ソースが異なるドメインであり、通常の条件下では情報の交換が禁止されているコードフラグメント間のデータ転送を防ぐことを目的としたブラウザベースのセキュリティメカニズムの1つです。
このエラーを確認するのに役立つ一連の手順を次に示します。
1.
Access-Control-Allow-Origin
ヘッダーを送信します 。
Access-Control-Allow-Origin
ヘッダーを
*
設定すると、リソースに任意のドメインからアクセスできることを示します。
必要に応じて、アスタリスク記号を特定のドメインに置き換えることができます(例
: Access-Control-Allow-Origin: www.example.com
。 ただし、複数のドメインのサポートは少し複雑です。 キャッシュの問題の可能性があるため、CDNを使用する場合、そのようなサポートは提供するのに費やす労力に値しない場合があります。 詳細はこちらをご覧ください 。
さまざまな環境でこのヘッダーを設定する例を次に示します。
アパッチ
JavaScriptファイルのロード元のフォルダーで、次の内容の
.htaccess
ファイルを作成します。
Header add Access-Control-Allow-Origin "*"
Nginx
add_header
ディレクティブを
location
ブロックに追加します。これは、JSファイルを管理する役割を果たします。
location ~ ^/assets/ { add_header Access-Control-Allow-Origin *; }
HAProxy
JSファイルのサポートを担当するシステムの設定に次の設定を追加します。
rspadd Access-Control-Allow-Origin:\ *
2.
<script>
で
crossorigin="anonymous"
設定します。
Access-Control-Allow-Origin
crossorigin="anonymous"
設定されている各スクリプトのHTMLファイルで、
<script>
で
crossorigin="anonymous"
設定します。
crossorigin
プロパティを
<script>
追加する前に、スクリプトファイルのヘッダーが送信されていることを確認してください。 Firefoxでは、
crossorigin
属性
crossorigin
存在するが、
Access-Control-Allow-Origin
ヘッダーが存在しない場合、スクリプトは実行されません。
5. TypeError:オブジェクトはプロパティをサポートしていません
このエラーは、未定義のメソッドを呼び出そうとするとIEで発生します。 IE開発者コンソールでこのエラーを確認できます。
エラーオブジェクトはプロパティをサポートしていません
このエラーは、Chromeで発生する
"TypeError: 'undefined' is not a function"
エラーと同等です。 同じ論理エラーについて話しているという事実に注意を喚起します。この論理エラーは、ブラウザごとに異なる方法で報告されます。
これは、JavaScript名前空間を利用するWebアプリケーションの一般的なIEの問題です。 このエラーが発生した場合、99.9%のケースで、その原因はIEが現在のネームスペースにあるメソッドを
this
にバインドできないことです。 たとえば、
isAwesome
メソッドを持つ
Rollbar
オブジェクトがあるとします。 通常、このオブジェクト内では、
isAwesome
ように
isAwesome
メソッドを
isAwesome
ことができます。
this.isAwesome();
Chrome、Firefox、Operaは通常このコマンドを受け入れます。 IEはそれを理解していません。 したがって、何よりも、このような構成を使用する場合は、メソッド名の前に、それが定義されているオブジェクト(名前空間)の名前を常に付ける必要があります。
Rollbar.isAwesome();
6. TypeError: 'undefined'は関数ではありません
未定義の関数を呼び出そうとすると、Chromeでこのエラーが発生します。 このエラーは、Chromeデベロッパーツールコンソールと同様のFirefoxコンソールで確認できます。
エラーTypeError: 'undefined'は関数ではありません
JavaScriptプログラミングのアプローチとデザインパターンは常に複雑になっているため、コールバック関数とクロージャー関数内で、
this
使用して独自のメソッドとプロパティへの参照が使用されるスコープが出現する状況が増加しています。混乱とエラーのかなり一般的な原因です。
次の例を考えてみましょう。
function testFunction() { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard(); // "this"? }, 0); };
上記のコードを実行すると、
"Uncaught TypeError: undefined is not a function."
エラーが発生
"Uncaught TypeError: undefined is not a function."
このエラーが発生する理由は、
setTimeout()
を呼び出すときに
setTimeout()
実際に
window.setTimeout()
呼び出すためです。 その結果、
setTimeout()
渡される匿名関数は、
clearBoard()
メソッドを持たない
window
オブジェクトのコンテキストで定義されます。
古いバージョンのブラウザと互換性があるこの問題を解決するための従来のアプローチは、これへのリンクを特定の変数に保存することで、クロージャーからアクセスできます。 たとえば、次のようになります。
function testFunction () { this.clearLocalStorage(); var self = this; // 'this' , ! this.timer = setTimeout(function(){ self.clearBoard(); }, 0); };
最新のブラウザでは、
bind()
メソッドを使用して必要なリンクを渡すことができます。
function testFunction () { this.clearLocalStorage(); this.timer = setTimeout(this.reset.bind(this), 0); // 'this' }; function testFunction(){ this.clearBoard(); // 'this'! };
7.キャッチされないRangeError:最大呼び出しスタック
このエラーには、Chromeなど、いくつかの理由があります。 それらの1つは、再帰関数の無限の呼び出しです。 Chromeデベロッパーコンソールでこのエラーが表示されるのは次のとおりです。
エラー最大コールスタックサイズを超えました
これは、関数が特定の許容範囲外の値を渡すときにも発生する可能性があります。 多くの関数は、特定の範囲の数字のみを受け入れます。 たとえば、関数
Number.toExponential(digits)
および
Number.toFixed(digits)
は、
Number.toFixed(digits)
digits
で表される引数
digits
取り、関数
Number.toPrecision(digits)
は
Number.toPrecision(digits)
数値を取ります。これらの状況を見てみましょう。その他のいくつかの機能はエラーにつながります。
var a = new Array(4294967295); //OK var b = new Array(-1); // ! var num = 2.555555; document.writeln(num.toExponential(4)); //OK document.writeln(num.toExponential(-2)); //! num = 2.9999; document.writeln(num.toFixed(2)); //OK document.writeln(num.toFixed(25)); // ! num = 2.3456; document.writeln(num.toPrecision(1)); //OK document.writeln(num.toPrecision(22)); // !
8. TypeError:プロパティ 'length'を読み取れません
undefined
変数の
length
プロパティを読み取ろうとすると、Chromeでこのエラーが発生します。 Chromeデベロッパーコンソールでこのエラーをご覧ください。
エラープロパティ 'length'を読み取れません
通常、
length
プロパティを参照すると、配列の長さがわかりますが、配列が初期化されていない場合、または変数の名前がスコープに隠されている場合、この変数にアクセスしようとする場所からアクセスできない場合、上記のエラーが発生する可能性があります このエラーの本質をよりよく理解するために、次の例を検討してください。
var testArray= ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
関数がパラメーターで宣言されると、これらのパラメーターはそのローカル変数になります。 この例では、関数を囲むスコープに
testArray
変数がある場合でも、同じ名前のパラメーターがこの変数を非表示にし、関数のローカル変数として認識されることを意味します。
この問題を解決するには、この場合、次の2つの方法のいずれかを使用できます。
- 関数の宣言中に指定されたパラメーターを削除します(例からわかるように、外部で宣言された配列の関数を操作したいので、ここで関数パラメーターなしで実行できます)。
var testArray = ["Test"]; /* : testArray */ function testFunction(/* */) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
- 以前に宣言された配列を渡す関数呼び出し:
var testArray = ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction(testArray);
9.キャッチされないTypeError:プロパティを設定できません
未定義の変数にアクセスしようとすると、実際には
undefined
型の値を操作しますが、この型はプロパティの読み取りまたは書き込みをサポートしていません。 この場合、アプリケーションは次のエラーを返します。
"Uncaught TypeError cannot set property of undefined."
Chromeブラウザでご覧ください。
エラープロパティを設定できません
test
オブジェクトが存在しない場合、エラー
"Uncaught TypeError cannot set property of undefined."
10. ReferenceError:イベントは定義されていません
このエラーは、未定義の変数、または現在のスコープ外の変数にアクセスしようとしたときに発生します。 Chromeコンソールでご覧ください。
エラーReferenceError:fooが定義されていません
イベント処理システムの使用中にこのエラーが発生した場合は、パラメーターとして渡されたイベントオブジェクトを使用していることを確認してください。 IEなどの古いブラウザーは、イベントへのグローバルアクセスを提供しますが、これはすべてのブラウザーに共通するわけではありません。 jQueryのようなライブラリは、この状況を修正しようとしています。 いずれの場合も、イベント処理関数に渡されるイベントオブジェクトを正確に使用することをお勧めします。
function myFunction(event) { event = event.which || event.keyCode; if(event.keyCode===13){ alert(event.keyCode); } }
まとめ
エラーストーリーから新しい何かを学び、将来の間違いを避けるのに役立つことを願っています。長い間悩まされてきた質問の答えを見つけるのにすでに役立っているかもしれません。
親愛なる読者! 本番環境ではどのようなJSエラーに遭遇しましたか?