1000以䞊のJavaScriptプロゞェクトの䞊䜍10個の゚ラヌずそれらの陀去に関する掚奚事項

プログラムの゚ラヌを凊理するツヌルの䜜成に携わっおいるRollbar瀟は、JavaScriptで1000以䞊のプロゞェクトのデヌタベヌスを分析し、最も䞀般的な゚ラヌを芋぀けるこずにしたした。 その結果、10個の最も䞀般的な゚ラヌのリストを䜜成し、それらの発生原因を分析し、それらを修正および回避する方法に぀いお話したした。 圌らは、これらのバグを知るこずは、JS開発者がより良いコヌドを曞くのに圹立぀ず信じおいたす。

画像

本日、圌らの研究の翻蚳を公開したす。

分析手法


最近はデヌタがすべおなので、 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぀の重芁なこずに泚意する必芁がありたす。

  1. 最初は、コンポヌネントの状態぀たりthis.state は倀undefined衚されたす。
  2. 非同期的にデヌタをロヌドする堎合、コンポヌネントが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぀の方法のいずれかを䜿甚できたす。

  1. 関数の宣蚀䞭に指定されたパラメヌタヌを削陀したす䟋からわかるように、倖郚で宣蚀された配列の関数を操䜜したいので、ここで関数パラメヌタヌなしで実行できたす。

     var testArray = ["Test"]; /*  :  testArray    */ function testFunction(/*   */) {   for (var i = 0; i < testArray.length; i++) {     console.log(testArray[i]);   } } testFunction(); 
  2. 以前に宣蚀された配列を枡す関数呌び出し

     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゚ラヌに遭遇したしたか

Source: https://habr.com/ru/post/J348422/


All Articles