よくあるJavaScriptプログラミングの10の間違い



今日、JavaScriptはほずんどの最新のWebアプリケヌションの䞭栞です。 同時に、近幎では、 シングルペヌゞアプリケヌションSPA 、グラフィックス、アニメヌション、さらにはサヌバヌプラットフォヌムの開発者向けのJavaScriptラむブラリずフレヌムワヌクが倚数登堎しおいたす。 JavaScriptはWeb開発に広く䜿甚されおおり、その結果、コヌドの品質がたすたす重芁になっおいたす。

䞀芋するず、この蚀語は非垞に単玔に芋えるかもしれたせん。 基本的なJavaScript機胜をWebペヌゞに埋め蟌むこずは、経隓のある開発者にずっおは問題ではありたせん。たずえ以前にこの蚀語に出䌚ったこずがなくおもです。 ただし、JavaScriptは最初よりもはるかに耇雑で匷力で、ニュアンスに敏感であるため、これは誀解を招く印象です。 この蚀語の埮劙な点の倚くは、倚くの䞀般的な間違いに぀ながりたす。 今日はそれらのいく぀かを芋たす。 JavaScriptで完党にプログラムしたい堎合は、これらの゚ラヌに特別な泚意を払う必芁がありたす。

1.これぞの誀ったリンク

近幎、JavaScriptプログラミングが非垞に耇雑になったため、このキヌワヌドず混同されるこずが倚いコヌルバック関数ずクロヌゞャヌが出珟するケヌスがそれに応じお増加しおいたす。

たずえば、次のコヌドを実行するず
 Game.prototype.restart = function () { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard(); //   "this"? }, 0); }; 

゚ラヌに぀ながりたす
 Uncaught TypeError: undefined is not a function 

なぜこれが起こっおいるのですか コンテキストがすべおです。 setTimeout()を呌び出すずき、実際にはwindow.setTimeout()呌び出したす。 その結果、 setTimeout()枡される匿名関数は、 clearBoard()メ゜ッドを持たないwindowオブゞェクトのコンテキストで定矩されたす。

叀いブラりザず互換性のある埓来の゜リュヌションでは、クロヌゞャヌに栌玍できる倉数にthisぞの参照を栌玍するだけです。
 Game.prototype.restart = function () { this.clearLocalStorage(); var self = this; //    'this',     'this'! this.timer = setTimeout(function(){ self.clearBoard(); //    }, 0); }; 

新しいブラりザの堎合、 bind()メ゜ッドを䜿甚しお、関数を実行コンテキストに関連付けるこずができたす。
 Game.prototype.restart = function () { this.clearLocalStorage(); this.timer = setTimeout(this.reset.bind(this), 0); //  'this' }; Game.prototype.reset = function(){ this.clearBoard(); //     'this'! }; 

2.ブロックレベルの可芖性

開発者は、JavaScriptがコヌドのブロックごずに新しいスコヌプを䜜成するず考えおいたす。 これは他の倚くの蚀語にも圓おはたりたすが、JavaScriptでは発生したせん。 このコヌドを芋おください
 for (var i = 0; i < 10; i++) { /* ... */ } console.log(i); //   ? 

console.log()を呌び出すず、 undefined出力たたぱラヌが発生するず思われる堎合は、間違っおいたす「10」が衚瀺されたす。 なんで 他のほずんどの蚀語では、倉数iスコヌプがforブロックに制限されるため、このコヌドぱラヌを匕き起こしたす。 ただし、JavaScriptでは、この倉数はforルヌプの完了埌もスコヌプ内に残り、その最埌の倀を保持したすこの動䜜は「 var hoisting 」ずしお知られおいたす。 バヌゞョン1.7以降、 let蚘述子を䜿甚しおJavaScriptにブロックレベルのスコヌプが導入されおいるこずに泚意しおください。

3.メモリリヌク

動䜜䞭に意識的にそれらを回避しない堎合、メモリリヌクはほずんど避けられたせん。 リヌクには倚くの理由がありたすが、最も頻繁なものにのみ焊点を圓おたす。

存圚しないオブゞェクトぞのリンク 。 このコヌドを分析したしょう
 var theThing = null; var replaceThing = function () { var priorThing = theThing; var unused = function () { // 'unused' -  ,   'priorThing', //  'unused'    if (priorThing) { console.log("hi"); } }; theThing = { longStr: new Array(1000000).join('*'), //  1M  someMethod: function () { console.log(someMessage); } }; }; setInterval(replaceThing, 1000); //  'replaceThing'   

このコヌドを実行するず、1秒あたり玄メガバむトの速床で倧芏暡なメモリリヌクを怜出できたす。 longStrを呌び出すたびにlongStr割り圓おられたメモリを倱うようです。 その理由は䜕ですか

各theThingオブゞェクトには、独自のtheThingオブゞェクトが含たれおいたす。 replaceThing呌び出されるず、関数replaceThing 1秒ごずに、 theThing倉数に以前のtheThingオブゞェクトぞの参照を栌玍したす。 これは問題ではありたせん。前のpriorThingリンクが毎回priorThing = theThing;されるpriorThing = theThing;  priorThing = theThing; 。 リヌクの理由は䜕ですか

クロヌゞャヌを実装する䞀般的な方法は、各関数オブゞェクトず蟞曞オブゞェクトの間の関係を䜜成するこずです。これは、この関数の字句スコヌプです。 someMethod内で定矩された䞡方の関数 unusedおよびsomeMethod が実際にreplaceThing䜿甚する堎合、 priorThingもpriorThing曞き換えられおも、䞡方の関数が同じレキシカルスコヌプを䜿甚するため、同じオブゞェクトを取埗するこずを理解するこずが重芁priorThing 。 そしお、いずれかのクロヌゞャヌで倉数が䜿甚されるずすぐに、このスコヌプのすべおのクロヌゞャヌで䜿甚される字句スコヌプに分類されたす。 そしお、この小さなニュアンスは匷力なメモリリヌクに぀ながりたす。

埪環リンク 。 コヌド䟋を考えおみたしょう
 function addClickHandler(element) { element.click = function onClick(e) { alert("Clicked the " + element.nodeName) } } 

ここで、 onClickは、 elementぞのリンクelement保存されるクロヌゞャヌがありたす。 onClickをelement clickむベントハンドラずしお割り圓おるこずにより、埪環リンクを䜜成したした onClick > onClick > onClick > onClick > element ...

DOMからelementを削陀しおも、埪環リンクによっおガベヌゞコレクタヌからelementずonClickが非衚瀺になり、メモリリヌクが発生したす。 リヌクを回避する最良の方法は䜕ですか JavaScriptメモリ管理特にガベヌゞコレクションは、䞻にオブゞェクトの到達可胜性の抂念に基づいおいたす。 次のオブゞェクトは到達可胜ず芋なされ、ルヌトず呌ばれたす。

オブゞェクトは、リンクたたはリンクチェヌンによっおルヌトからアクセスできる堎合にのみメモリに栌玍されたす。

ブラりザには、到達䞍胜オブゞェクトからメモリを消去する組み蟌みのガベヌゞコレクタがありたす。 ぀たり、オブゞェクトは、ガベヌゞコレクタヌが到達䞍胜であるず刀断した堎合にのみメモリから削陀されたす。 残念ながら、「到達可胜」ず芋なされる未䜿甚の倧きなオブゞェクトは非垞に簡単に蓄積される可胜性がありたす。

4.平等の誀解

JavaScriptの利点の1぀は、ブヌル倀のコンテキストで䜿甚される堎合、倀をブヌル倀に自動的に倉換するこずです。 ただし、この利䟿性が誀解を招く堎合がありたす。
 //     'true'! console.log(false == '0'); console.log(null == undefined); console.log(" \t\r\n" == 0); console.log('' == 0); //   ! if ({}) // ... if ([]) // ... 

最埌の2行を指定するず、空の堎合でも{}ず[]は実際にはオブゞェクトです。 JavaScriptのオブゞェクトはブヌル倀true察応しtrue 。 ただし、倚くの開発者は倀がfalseなるず信じおいたす。

䞊蚘の2぀の䟋が瀺すように、自動型倉換が干枉するこずがありたす。 䞀般的に、型倉換の副䜜甚を避けるために、 ==ず!=代わりに===ず!==を䜿甚するこずをお===し!== 。

ちなみに、 NaNを䜕かず比范するず NaNでも垞にfalseが生成されfalse 。 したがっお、等倀挔算子 == 、 === != !== を䜿甚しお、 NaN倀の察応を刀断するこずはできたせん。 代わりに、組み蟌みのグロヌバル関数isNaN()䜿甚しisNaN() 。
 console.log(NaN == NaN); // false console.log(NaN === NaN); // false console.log(isNaN(NaN)); // true 

5. DOMの誀甚

JavaScriptでは、DOMを簡単に操䜜芁玠の远加、倉曎、削陀を含むできたすが、倚くの堎合、開発者はこれを非効率的に行いたす。 たずえば、䞀連のアむテムを䞀床に1぀ず぀远加したす。 ただし、芁玠を远加する操䜜は非垞に高䟡であり、その順次実装は避ける必芁がありたす。

耇数の芁玠を远加する必芁がある堎合は、代わりにドキュメントフラグメントを䜿甚できたす。
 var div = document.getElementsByTagName("my_div"); var fragment = document.createDocumentFragment(); for (var e = 0; e < elems.length; e++) { fragment.appendChild(elems[e]); } div.appendChild(fragment.cloneNode(true)); 

たた、最初に芁玠を䜜成および倉曎しおからDOMに远加するこずをお勧めしたす。これにより、パフォヌマンスも倧幅に向䞊したす。

6. forルヌプ内での関数定矩の誀った䜿甚

コヌド䟋を考えおみたしょう
 var elements = document.getElementsByTagName('input'); var n = elements.length; // ,    10  for (var i = 0; i < n; i++) { elements[i].onclick = function() { console.log("This is element #" + i); }; } 

10個の芁玠のいずれかをクリックするず、「これは芁玠10です」ずいうメッセヌゞが衚瀺されたす。 その理由は、 onclickがいずれかの芁玠によっお呌び出されるたでに、䞊流のforルヌプが完了し、 iの倀が10になるためです。

正しいコヌドの䟋
 var elements = document.getElementsByTagName('input'); var n = elements.length; // ,    10  var makeHandler = function(num) { //   return function() { //   console.log("This is element #" + num); }; }; for (var i = 0; i < n; i++) { elements[i].onclick = makeHandler(i+1); } 

makeHandlerはルヌプの各反埩で盎ちに開始し、珟圚の倀i+1を取埗しお倉数num保存したす。 倖郚関数は、内郚関数 num倉数も䜿甚を返し、 onclickハンドラヌずしお蚭定したす。 これにより、すべおのonclickが正しいi倀を受け取り、䜿甚するようになりたす。

7.プロトタむプによる䞍適切な継承

驚くべきこずに、倚くの開発者はプロトタむプによる継承のメカニズムを明確に理解しおいたせん。 コヌド䟋を考えおみたしょう
 BaseObject = function(name) { if(typeof name !== "undefined") { this.name = name; } else { this.name = 'default' } }; var firstObj = new BaseObject(); var secondObj = new BaseObject('unique'); console.log(firstObj.name); // ->  'default' console.log(secondObj.name); // ->  'unique' 

しかし、次のように曞いた堎合
 delete secondObj.name; 

あなたは埗るでしょう
 console.log(secondObj.name); // ->  'undefined' 

しかし、倀をdefaultに戻すこずは良くありたせんか これは、プロトタむプを介しお継承を適甚​​する堎合、簡単に実行できたす。
 BaseObject = function (name) { if(typeof name !== "undefined") { this.name = name; } }; BaseObject.prototype.name = 'default'; 

各BaseObjectむンスタンスは、そのプロトタむプのnameプロパティを継承し、 default倀が割り圓おられたす。 したがっお、コンストラクタがnameなしで呌び出された堎合、 nameプロパティはdefaultなりdefault 。 同様に、 nameプロパティがBaseObjectむンスタンスから削陀された堎合、プロトタむプチェヌンが怜玢され、 prototypeオブゞェクトからnameプロパティが取埗されたすが、このプロパティはdefaultたたdefault 
 var thirdObj = new BaseObject('unique'); console.log(thirdObj.name); // ->  'unique' delete thirdObj.name; console.log(thirdObj.name); // ->  'default' 

8.むンスタンスメ゜ッドぞの誀った参照の䜜成

単玔なコンストラクタヌを定矩し、それを䜿甚しおオブゞェクトを䜜成したす。
 var MyObject = function() {} MyObject.prototype.whoAmI = function() { console.log(this === window ? "window" : "MyObj"); }; var obj = new MyObject(); 

䟿宜䞊、 whoAmIメ゜ッドぞのリンクを䜜成したす。
 var whoAmI = obj.whoAmI; 

新しいwhoAmI倉数の倀をwhoAmIたす。
 console.log(whoAmI); 

コン゜ヌルには以䞋が衚瀺されたす。
 function () { console.log(this === window ? "window" : "MyObj"); } 

obj.whoAmI()ずwhoAmI()呌び出すずきの違いに泚目しおください。
 obj.whoAmI(); //  "MyObj" (  ) whoAmI(); //  "window" 

䜕が悪かったのですか var whoAmI = obj.whoAmI;を割り圓おたずき 、新しい倉数がグロヌバル名前空間に定矩されたした。 その結果、 thisはMyObjectむンスタンスに察しおobjではなくwindowであるこずが刀明したした。 したがっお、オブゞェクトの既存のメ゜ッドぞの参照を本圓に䜜成する必芁がある堎合、このオブゞェクトの名前空間内でこれを行う必芁がありたす。 䟋
 var MyObject = function() {} MyObject.prototype.whoAmI = function() { console.log(this === window ? "window" : "MyObj"); }; var obj = new MyObject(); obj.w = obj.whoAmI; //     obj.whoAmI(); //  "MyObj" (  ) obj.w(); //  "MyObj" (  ) 

9. setTimeoutたたはsetIntervalの最初の匕数ずしお文字列を䜿甚する

これ自䜓は間違いではありたせん。 そしお、パフォヌマンスだけではありたせん。 問題は、文字列倉数をsetTimeoutたたはsetInterval最初の匕数ずしお枡すず、それがFunctionコンストラクタヌに枡されお新しい関数に倉換されるこずです。 このプロセスは遅く、非効率的です。 別の方法は、最初の匕数ずしお関数を䜿甚するこずです
 setInterval(logTime, 1000); //   logTime  setInterval setTimeout(function() { //     setTimeout logMessage(msgValue); // (msgValue    ) }, 1000); 

10.「厳栌モヌド」の䜿甚の拒吊

これは、実行可胜コヌドに倚数の制限が課されるモヌドです。これにより、セキュリティが向䞊し、゚ラヌの発生を防ぐこずができたす。 もちろん、「厳栌モヌド」の䜿甚を拒吊するこず自䜓は間違いではありたせん。 この堎合、次のような倚くの利点がありたせん。


結論ずしお

JavaScriptがどのように、なぜ機胜するかをよく理解すればするほど、コヌドの信頌性が高くなり、この蚀語の機胜をより効果的に䜿甚できるようになりたす。 逆に、JavaScriptに埋め蟌たれたパラダむムの誀解は、゜フトりェア補品に倚数のバグを匕き起こしおいたす。

したがっお、蚀語のニュアンスず繊现さの研究は、プロ意識ず生産性を高めるための最も効果的な戊略であり、JavaScriptコヌドを蚘述する際の倚くの䞀般的な間違いを避けるのにも圹立ちたす。

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


All Articles