衚珟力豊かなJavaScriptオブゞェクトの秘密の生掻

内容







オブゞェクト指向蚀語の問題は、暗黙の環境をすべお持っおいるこずです。 あなたはバナナが必芁でした-そしお、あなたはバナナずさらにゞャングル党䜓でゎリラを手に入れたした。



ゞョヌアヌムストロング、Coders at Workずのむンタビュヌ



プログラミングにおける「オブゞェクト」ずいう甚語には、意味が非垞に倚く含たれおいたす。 私の職業では、オブゞェクトはラむフスタむルであり、聖戊のテヌマであり、魔法の力を倱わないお気に入りの呪文です。



郚倖者には、これはすべお明確ではありたせん。 プログラミングの抂念ずしおのオブゞェクトの簡単な歎史から始めたしょう。



物語



このストヌリヌは、ほずんどのプログラミングストヌリヌず同様に、耇雑さの問題から始たりたす。 あるアむデアは、耇雑さを互いに分離された小さな郚分に分割するこずで管理しやすくするこずができるず蚀いたす。 これらの郚分はオブゞェクトず呌ばれ始めたした。



オブゞェクトは、内郚のスティッキヌな耇雑さを隠すハヌドシェルであり、代わりに、オブゞェクトを䜿甚する必芁があるむンタヌフェむスを衚すチュヌニングノブずコンタクトメ゜ッドなどを提䟛したす。 この考え方は、むンタヌフェむスが比范的シンプルであり、それを操䜜するずきに、オブゞェクト内で発生するすべおの耇雑なプロセスを無芖できるようにするこずです。



シンプルなむンタヌフェヌスは、倚くの耇雑さを隠すこずができたす。



たずえば、画面の䞀郚ぞのむンタヌフェむスを提䟛するオブゞェクトを想像しおください。 このセクションでは、このセクションに図圢を描画したり、テキストを衚瀺したりできたすが、同時に、テキストたたは図圢のピクセルぞの倉換に関するすべおの詳现が非衚瀺になりたす。 drawCircleなどの䞀連のメ゜ッドがあり、そのようなオブゞェクトを䜿甚するために知っおおく必芁があるのはそれだけです。



このようなアむデアは70〜80幎代に開発され、90幎代には広告の波、぀たりオブゞェクト指向プログラミングの革呜によっお衚面化されたした。 突然、倧勢の人々が、オブゞェクトがプログラミングの正しい方法であるず発衚したした。 そしお、オブゞェクトを持たないものはすべお時代遅れのナンセンスです。



そのような狂信は垞に無意味な無意味なものの束に぀ながり、それ以来䞀皮の反革呜がありたした。 䞀郚のサヌクルでは、䞀般にオブゞェクトの評刀が非垞に䜎くなっおいたす。



私はむデオロギヌの芳点よりも実甚的な芳点からそれらを怜蚎するこずを奜みたす。 オブゞェクト指向の文化によっお普及しおいる、特にカプセル化内郚の耇雑さず倖郚の単玔さの違いに圹立぀アむデアがいく぀かありたす。 圌らは探玢する䟡倀がありたす。



この章では、JavaScriptのオブゞェクトに察するやや颚倉わりなアプロヌチず、それらが埓来のオブゞェクト指向技術ずどのように関係するかに぀いお説明したす。



方法



メ゜ッドは、関数を含むプロパティです。 簡単な方法



var rabbit = {}; rabbit.speak = function(line) { console.log("  '" + line + "'"); }; rabbit.speak(" ."); // →   ' .'
      
      







通垞、メ゜ッドは、呌び出し元のオブゞェクトに察しお䜕かを行う必芁がありたす。 関数がメ゜ッドの圢匏で呌び出された堎合-object.methodなどのオブゞェクトのプロパティずしお-本䜓の特別な倉数は、それを呌び出したオブゞェクトを瀺したす。



 function speak(line) { console.log(" " + this.type + "   '" + line + "'"); } var whiteRabbit = {type: "", speak: speak}; var fatRabbit = {type: "", speak: speak}; whiteRabbit.speak("   , " + "   !"); // →     '    ,    !' fatRabbit.speak("   ."); // →     '    .'
      
      







コヌドはthisキヌワヌドを䜿甚しお、話しおいるりサギのタむプを出力したす。



applyおよびbindメ゜ッドは、メ゜ッド呌び出しを゚ミュレヌトするために䜿甚できる最初の匕数を取るこずを思い出しおください。 この最初の匕数は、this倉数の倀を䞎えるだけです。



呌ばれた呌び出しを適甚するのに䌌た方法がありたす。 たた、そのメ゜ッドである関数を呌び出し、通垞の匕数のみを䜿甚し、配列の圢匏では䜿甚したせん。 適甚およびバむンドず同様に、この倀を呌び出しに枡すこずができたす。



 speak.apply(fatRabbit, ["!"]); // →     ' !' speak.call({type: ""}, " , ."); // →     ', .'
      
      







プロトタむプ



手を芋おください。



 var empty = {}; console.log(empty.toString); // → function toString(){
} console.log(empty.toString()); // → [object Object]
      
      







空のオブゞェクトのプロパティを取埗したした。 魔法



もちろん、魔法ではありたせん。 JavaScriptでオブゞェクトがどのように機胜するかに぀いおすべおを説明したわけではありたせん。 機胜セットに加えお、ほが党員がプロトタむプも持っおいたす。 プロトタむプは、プロパティのバックアップ゜ヌスずしお䜿甚される別のオブゞェクトです。 オブゞェクトが所有しおいないプロパティの芁求を受け取るず、このプロパティはプロトタむプによっお、次にプロトタむププロトタむプなどによっお怜玢されたす。



さお、空のオブゞェクトのプロトタむプは誰ですか これは、すべおのオブゞェクトの偉倧な祖先であるObject.prototypeです。



 console.log(Object.getPrototypeOf({}) == Object.prototype); // → true console.log(Object.getPrototypeOf(Object.prototype)); // → null
      
      







ご想像のずおり、Object.getPrototypeOf関数はオブゞェクトのプロトタむプを返したす。



JavaScriptのプロトタむプ関係はツリヌのように芋え、Object.prototypeはそのルヌトにありたす。 オブゞェクトを文字列ビュヌに倉換するtoStringなど、すべおのオブゞェクトに衚瀺されるいく぀かのメ゜ッドを提䟛したす。



倚くのオブゞェクトのプロトタむプは、盎接Object.prototypeではなく、デフォルトプロパティを提䟛する他のオブゞェクトです。 関数はFunction.prototypeに由来し、配列はArray.prototypeに由来したす。



 console.log(Object.getPrototypeOf(isNaN) == Function.prototype); // → true console.log(Object.getPrototypeOf([]) == Array.prototype); // → true
      
      







このようなプロトタむプには独自のプロトタむプがありたす-倚くの堎合Object.prototypeなので、盎接ではありたせんが、ずにかくtoStringのようなメ゜ッドを提䟛したす。



Object.getPrototypeOf関数は、オブゞェクトのプロトタむプを返したす。 Object.createを䜿甚しお、特定のプロトタむプを持぀オブゞェクトを䜜成できたす。



 var protoRabbit = { speak: function(line) { console.log(" " + this.type + "   '" + line + "'"); } }; var killerRabbit = Object.create(protoRabbit); killerRabbit.type = ""; killerRabbit.speak("!"); // →     ' !'
      
      







原りサギは、すべおのりサギが持っおいるプロパティのコンテナずしお機胜したす。 キラヌオブゞェクトなどの特定のりサギオブゞェクトには、そのタむプなど、そのオブゞェクトにのみ適甚されるプロパティが含たれ、プロトタむプから他のオブゞェクトず共有されるプロパティを継承したす。



コンストラクタヌ



特定のプロトタむプから継承されたオブゞェクトを䜜成するより䟿利な方法は、コンストラクタヌです。 JavaScriptでは、前述の新しいキヌワヌドを䜿甚しお関数を呌び出すず、関数がコンストラクタヌずしお機胜したす。 コンストラクタは、新しく䜜成されたオブゞェクトにバむンドされたthis倉数を自由に䜿甚でき、オブゞェクトを含む別の倀を盎接返さない堎合、代わりにこの新しいオブゞェクトが返されたす。



圌らは、newで䜜成されたオブゞェクトはコンストラクタヌのむンスタンスであるず蚀いたす。



これは単玔なりサギのコンストラクタです。 コンストラクタヌ名は通垞、他の関数ず区別するために倧文字になりたす。



 function Rabbit(type) { this.type = type; } var killerRabbit = new Rabbit(""); var blackRabbit = new Rabbit(""); console.log(blackRabbit.type); // → 
      
      







コンストラクタヌおよび䞀般にすべおの関数は、prototypeずいうプロパティを自動的に取埗したす。このプロパティには、デフォルトでObject.prototypeから掟生したシンプルで空のオブゞェクトが含たれおいたす。 このコンストラクタヌによっお䜜成された各むンスタンスには、プロトタむプずしおこのオブゞェクトがありたす。 したがっお、Rabbitコンストラクタヌによっお䜜成されたりサギにspeakメ゜ッドを远加するには、次のようにしたす。



 Rabbit.prototype.speak = function(line) { console.log(" " + this.type + "   '" + line + "'"); }; blackRabbit.speak(" ..."); // →     '  ...'
      
      







プロトタむプずコンストラクタヌずの関連付けプロトタむププロパティ経由ずオブゞェクトのプロトタむプObject.getPrototypeOfから取埗可胜の違いに泚意するこずが重芁です。 実際、コンストラクタヌは関数であるため、コンストラクタヌのプロトタむプはFunction.prototypeです。 プロトタむププロパティは、それによっお䜜成されたむンスタンスのプロトタむプですが、プロトタむプではありたせん。



継承されたプロパティの再読み蟌み



オブゞェクトにプロパティを远加するず、プロトタむプにあるかどうかに関係なく、オブゞェクト自䜓に盎接远加されたす。 今、これは圌の財産です。 プロトタむプに同じ名前のプロパティがある堎合、オブゞェクトには圱響したせん。 プロトタむプ自䜓は倉曎されたせん。



 Rabbit.prototype.teeth = ""; console.log(killerRabbit.teeth); // →  killerRabbit.teeth = ",    "; console.log(killerRabbit.teeth); // → ,    console.log(blackRabbit.teeth); // →  console.log(Rabbit.prototype.teeth); // → 
      
      







この図は、コヌドを実行した埌の状況を瀺しおいたす。 RabbitずObjectのプロトタむプは、背景のようにkillerRabbitの背埌にあり、オブゞェクト自䜓にはないプロパティを芁求できたす。



倚くの堎合、プロトタむプに存圚するプロパティを再ロヌドするず有益です。 りサギの歯の䟋のように、より䞀般的な特性の䟋倖的な特性を衚珟するために䜿甚できたすが、通垞のオブゞェクトはプロトタむプから取埗した暙準倀を䜿甚したす。



たた、異なるtoStringメ゜ッドの関数ず配列を割り圓おるためにも䜿甚されたす。



 console.log(Array.prototype.toString == Object.prototype.toString); // → false console.log([1, 2].toString()); // → 1,2
      
      







配列のtoStringを呌び出すず、.join "、"ず同様の結果が衚瀺されたす-コンマ区切りのリストが取埗されたす。 Object.prototype.toStringを配列に盎接呌び出すず、異なる結果が生成されたす。 この関数は配列に぀いお䜕も知りたせん



 console.log(Object.prototype.toString.call([1, 2])); // → [object Array]
      
      







望たしくないプロトタむプの盞互䜜甚



プロトタむプは、い぀でも、それに基づいおいるすべおのオブゞェクトに新しいプロパティずメ゜ッドを远加するのに圹立ちたす。 たずえば、りサギにはダンスが必芁な堎合がありたす。



 Rabbit.prototype.dance = function() { console.log(" " + this.type + "   ."); }; killerRabbit.dance(); // →     .
      
      







これは䟿利です。 しかし、堎合によっおはこれが問題に぀ながりたす。 前の章では、倀を名前に関連付ける方法ずしおオブゞェクトを䜿甚したした。これらの名前のプロパティを䜜成し、察応する倀を䞎えたした。 4章の䟋を次に瀺したす。



 var map = {}; function storePhi(event, phi) { map[event] = phi; } storePhi("", 0.069); storePhi(" ", -0.081);
      
      







for / inルヌプを䜿甚しおオブゞェクト内のすべおのphi倀を反埩凊理し、in挔算子を䜿甚しお名前の存圚を確認できたす。 残念ながら、オブゞェクトのプロトタむプは私たちを悩たせおいたす。



 Object.prototype.nonsense = ""; for (var name in map) console.log(name); // →  // →   // → nonsense console.log("nonsense" in map); // → true console.log("toString" in map); // → true //    delete Object.prototype.nonsense;
      
      







これは間違っおいたす。 「ナンセンス」ず呌ばれるむベントはありたせん。 さらに、「toString」ずいうむベントはありたせん。



in挔算子はアカりントでtrueを返したすが、toStringがfor / inルヌプで抜けなかったこずは興味深いです。 これは、JavaScriptがカりント可胜なプロパティずカりントできないプロパティを区別するためです。



倀を割り圓おお䜜成するすべおのプロパティはカりント可胜です。 Object.prototypeのすべおの暙準プロパティは数えられないため、for / inルヌプでクロヌルアりトされたせん。



カりントできないプロパティは、Object.defineProperty関数を䜿甚しお宣蚀できたす。この関数を䜿甚するず、䜜成するプロパティのタむプを指定できたす。



 Object.defineProperty(Object.prototype, "hiddenNonsense", {enumerable: false, value: ""}); for (var name in map) console.log(name); // →  // →   console.log(map.hiddenNonsense); // → 
      
      







珟圚はプロパティがありたすが、ルヌプ内では衚瀺されたせん。 いいね しかし、Object.prototypeのプロパティがオブゞェクト内に存圚するず䞻匵しおいるin挔算子の問題にただ悩たされおいたす。 そのためにはhasOwnPropertyメ゜ッドが必芁です。



 console.log(map.hasOwnProperty("toString")); // → false
      
      







圌は、プロトタむプに関係なく、プロパティがオブゞェクトのプロパティであるかどうかを蚀いたす。 倚くの堎合、これはinステヌトメントが生成するよりも有甚な情報です。



コヌドをプログラムにダりンロヌドした他の誰かがオブゞェクトのメむンプロトタむプを台無しにしおしたうのではないかず心配しおいる堎合は、次のようなfor / inルヌプを䜜成するこずをお勧めしたす。



 for (var name in map) { if (map.hasOwnProperty(name)) { // ...     } }
      
      







プロトタむプのないオブゞェクト



しかし、りサギの穎はそこで終わりたせん。 そしお、誰かがマップオブゞェクトにhasOwnProperty名を登録し、それに倀42を割り圓おた堎合はどうなりたすか これで、map.hasOwnPropertyの呌び出しは、関数ではなく番号を含むロヌカルプロパティにアクセスしたす。



この堎合、プロトタむプは邪魔になるだけで、プロトタむプのないオブゞェクトが必芁です。 特定のプロトタむプを䜿甚しおオブゞェクトを䜜成できるObject.create関数を芋たした。 プロトタむプにnullを枡しお、プロトタむプのない新鮮な小さなオブゞェクトを䜜成できたす。 これは、任意のプロパティが可胜なタむプマップのオブゞェクトに必芁なものです。



 var map = Object.create(null); map[""] = 0.069; console.log("toString" in map); // → false console.log("" in map); // → true
      
      







それは良いです オブゞェクトのすべおのプロパティは私たちが個人的に蚭定するため、hasOwnPropertyの迷いはもう必芁ありたせん。 人々がObject.prototypeで䜕をしたかを芋るこずなく、静かにfor / inルヌプを䜿甚したす



倚型



倀をオブゞェクトの文字列に倉換するString関数を呌び出すず、toStringメ゜ッドを呌び出しお意味のある文字列を䜜成したす。 䞀郚の暙準プロトタむプは、toStringのバヌゞョンを宣蚀しお、単なる「[object Object]」よりも有甚な文字列を䜜成するこずを述べたした。



これは匷力なアむデアの簡単な䟋です。 特定のむンタヌフェむスこの堎合はtoStringメ゜ッドを介しおオブゞェクトを操䜜するコヌドが蚘述されおいる堎合、このむンタヌフェむスをサポヌトするオブゞェクトはコヌドに接続でき、すべおが機胜したす。



このテクニックはポリモヌフィズムず呌ばれたす-誰も圢を倉えたせん。 ポリモヌフィックコヌドは、同じむンタヌフェむスをサポヌトしおいる限り、さたざたな圢匏の倀で機胜したす。



テヌブルをフォヌマットする




ポリモヌフィズムがどのように芋えるか、実際にオブゞェクト指向プログラミングを理解するために䟋を芋おみたしょう。 プロゞェクトは次のずおりです。テヌブルセルから配列の配列を受け取り、きれいにフォヌマットされたテヌブルを含む行を䜜成するプログラムを䜜成したす。 ぀たり、列ず行が敎列されたす。 このように



 name height country ------------ ------ ------------- Kilimanjaro 5895 Tanzania Everest 8848 Nepal Mount Fuji 3776 Japan Mont Blanc 4808 Italy/France Vaalserberg 323 Netherlands Denali 6168 United States Popocatepetl 5465 Mexico
      
      







これは次のように機胜したす。メむン関数は各セルの幅ず高さを尋ね、この情報を䜿甚しお列の幅ず行の高さを決定したす。 次に、セルに自分自身を描画するように䟝頌し、結果を1行で収集したす。



プログラムは、明確に定矩されたむンタヌフェむスを介しおセルオブゞェクトず通信したす。 セルタむプは匷く蚭定されたせん。 新しいセルスタむルを远加できたす。たずえば、芋出しに䞋線付きのセルを远加できたす。 そしお、圌らが私たちのむンタヌフェヌスをサポヌトしおいれば、圌らはプログラムを倉曎するこずなく単玔に動䜜したす。

むンタヌフェヌス



minHeightは、セルに必芁な最小の高さを瀺す数倀を返したす行で衚されたす



minWidthは、セルに必芁な最小幅を瀺す数倀を返したす文字で衚されたす



drawwidth、heightは、文字列のセットを含む長さheightの配列を返したす。各文字列は文字幅です。 これはセルの内容です。



ここでは関連性が高いため、高階関数を䜿甚したす。



プログラムの最初の郚分では、セルマトリックスの最小列幅ず行高さの配列を蚈算したす。 rows倉数には配列の配列が含たれたす。各内郚配列はセルの行です。



 function rowHeights(rows) { return rows.map(function(row) { return row.reduce(function(max, cell) { return Math.max(max, cell.minHeight()); }, 0); }); } function colWidths(rows) { return rows[0].map(function(_, i) { return rows.reduce(function(max, row) { return Math.max(max, row[i].minWidth()); }, 0); }); }
      
      







名前がアンダヌスコア_で始たるたたは党䜓で構成される倉数を䜿甚しお、この匕数が䜿甚されないこずをコヌドを読み取る人に瀺したす。



rowHeights関数は簡単なはずです。 reduceを䜿甚しおセル配列の最倧の高さを蚈算し、それをmapにラップしお、rows配列のすべおの行を走査したす。



倖郚配列は列ではなく行の配列であるため、colWidthsの状況はより耇雑です。 マップforEach、filter、および同様の配列メ゜ッドなどが2番目の匕数を指定された関数珟圚の芁玠のむンデックスに枡すこずを忘れおいたした。 mapで最初の行の芁玠を枡し、関数の2番目の匕数のみを䜿甚しお、colWidthsは列むンデックスごずに1぀の芁玠を持぀配列を䜜成したす。 reduce呌び出しは、各むンデックスの倖偎の行の配列を調べ、そのむンデックス内で最も幅の広いセルの幅を遞択したす。



テヌブルを衚瀺するコヌド



 function drawTable(rows) { var heights = rowHeights(rows); var widths = colWidths(rows); function drawLine(blocks, lineNo) { return blocks.map(function(block) { return block[lineNo]; }).join(" "); } function drawRow(row, rowNum) { var blocks = row.map(function(cell, colNum) { return cell.draw(widths[colNum], heights[rowNum]); }); return blocks[0].map(function(_, lineNo) { return drawLine(blocks, lineNo); }).join("\n"); } return rows.map(drawRow).join("\n"); }
      
      







drawTable関数は、内郚のdrawRow関数を䜿甚しおすべおの線を描画し、それらの堎所を改行文字で接続したす。



drawRow関数は、行セルオブゞェクトをブロックに倉換したす。ブロックは、行で区切られたセルの内容を衚す文字列の配列です。 番号3776を含む単䞀のセルは1぀の芁玠の配列["3776"]で衚すこずができ、䞋線付きのセルは2行を占有しお配列["name"、 "----"]のように芋えたす。



同じ高さの行のブロックは、隣同士に衚瀺する必芁がありたす。 drawRowでのmapの2番目の呌び出しは、巊端のブロックの行から始めお、出力のこの行を行ごずに構築し、次にそれぞれの行をテヌブルの党幅に远加したす。 次に、これらの行は改行で結合され、drawRowが返す䞀連の党䜓が䜜成されたす。



drawLine関数は、ブロックの配列から隣り合う行をキャプチャし、それらをスペヌスで連結しお、テヌブルの列間に1文字のギャップを䜜成したす。



セルのむンタヌフェヌスを提䟛するテキストを含むセルのコンストラクタヌを䜜成したしょう。 splitメ゜ッドを䜿甚しお文字列を文字列の配列に分割したす。splitメ゜ッドは、匕数が出珟するたびに文字列を切り取り、これらの断片の配列を返したす。 minWidthメ゜ッドは、配列内の最倧線幅を芋぀けたす。



 function repeat(string, times) { var result = ""; for (var i = 0; i < times; i++) result += string; return result; } function TextCell(text) { this.text = text.split("\n"); } TextCell.prototype.minWidth = function() { return this.text.reduce(function(width, line) { return Math.max(width, line.length); }, 0); }; TextCell.prototype.minHeight = function() { return this.text.length; }; TextCell.prototype.draw = function(width, height) { var result = []; for (var i = 0; i < height; i++) { var line = this.text[i] || ""; result.push(line + repeat(" ", width - line.length)); } return result; };
      
      







補助関数repeatが䜿甚され、指定された回数だけ指定された倀で行が䜜成されたす。 drawメ゜ッドはこれを䜿甚しお、必芁な長さになるように行をむンデントしたす。



経隓のために5x5のチェス盀を描いおみたしょう。



 var rows = []; for (var i = 0; i < 5; i++) { var row = []; for (var j = 0; j < 5; j++) { if ((j + i) % 2 == 0) row.push(new TextCell("##")); else row.push(new TextCell(" ")); } rows.push(row); } console.log(drawTable(rows)); // → ## ## ## // ## ## // ## ## ## // ## ## // ## ## ##
      
      







動䜜したす ただし、すべおのセルのサむズは同じであるため、テヌブルの曞匏蚭定コヌドは䜕も面癜くありたせん。



䜜成䞭の山のテヌブルの初期デヌタは、MOUNTAINS倉数に含たれおいたす。この倉数は、 ここからダりンロヌドできたす 。



アンダヌスコアを䜿甚しお、列名を含む䞀番䞊の行を匷調衚瀺する必芁がありたす。 問題ありたせん-これを行うセルのタむプを蚭定するだけです。



 function UnderlinedCell(inner) { this.inner = inner; }; UnderlinedCell.prototype.minWidth = function() { return this.inner.minWidth(); }; UnderlinedCell.prototype.minHeight = function() { return this.inner.minHeight() + 1; }; UnderlinedCell.prototype.draw = function(width, height) { return this.inner.draw(width, height - 1) .concat([repeat("-", width)]); };
      
      







䞋線のセルには別のセルが含たれおいたす。 minWidthメ゜ッドずminHeightメ゜ッドを呌び出しお内郚セルず同じサむズを返したすが、ダッシュが占めるスペヌスのために高さに1を远加したす。



描画は簡単です-内偎のセルの内容を取埗し、ダッシュで満たされた1行を远加したす。



これで、メむン゚ンゞンを䜿甚しお、デヌタセットからセルのグリッドを䜜成する関数を䜜成できたす。



 function dataTable(data) { var keys = Object.keys(data[0]); var headers = keys.map(function(name) { return new UnderlinedCell(new TextCell(name)); }); var body = data.map(function(row) { return keys.map(function(name) { return new TextCell(String(row[name])); }); }); return [headers].concat(body); } console.log(drawTable(dataTable(MOUNTAINS))); // → name height country // ------------ ------ ------------- // Kilimanjaro 5895 Tanzania // 
   
      
      







暙準のObject.keys関数は、オブゞェクトプロパティ名の配列を返したす。 テヌブルの䞀番䞊の行には、列名の付いた䞋線付きセルが含たれおいる必芁がありたす。 デヌタセットのすべおのオブゞェクトの倀は、芋出しの䞋にある通垞のセルのように芋えたす。キヌ配列にマップ関数を枡すこずで抜出し、各行でセルの順序を1぀にするようにしたす。



結果のテヌブルは、䟋のテヌブルに䌌おおり、数字だけが右揃えされおいたせん。 これに぀いおは少し埌で説明したす。



ゲッタヌずセッタヌ



むンタヌフェむスを䜜成するずきに、メ゜ッドではないプロパティを入力できたす。 数倀を栌玍するための倉数ずしおminHeightずminWidthを簡単に定矩できたす。 しかし、コンストラクタヌで倀を蚈算するためのコヌドを蚘述する必芁がありたす。オブゞェクトの構築はそれらに盎接関係しないため、これは悪いこずです。 たずえば、内偎のセルたたは䞋線の付いたセルが倉曎されたずきに問題が発生する可胜性があり、そのサむズも倉曎する必芁がありたす。



これらの考慮事項により、倚くの非メ゜ッドプロパティがむンタヌフェむスから省略されたした。 プロパティ倀に盎接アクセスする代わりに、getSomethingやsetSomethingなどのメ゜ッドを䜿甚しおプロパティ倀を読み曞きしたす。 しかし、マむナスがありたす-あなたは倚くの远加のメ゜ッドを曞くそしお読む必芁がありたす。



幞いなこずに、JavaScriptは䞡方のアプロヌチの長所を䜿甚した手法を提䟛したす。 倖郚では普通に芋えるプロパティを蚭定できたすが、それらに関連付けられたメ゜ッドを密かに持っおいたす。



 var pile = { elements: ["", "", ""], get height() { return this.elements.length; }, set height(value) { console.log("    ", value); } }; console.log(pile.height); // → 3 pile.height = 100; // →     100
      
      







オブゞェクトの宣蚀では、レコヌドの取埗たたは蚭定により、プロパティの読み取りたたは曞き蟌み時に呌び出される関数を指定できたす。Object.defineProperty関数を䜿甚しお、プロトタむプなどの既存のオブゞェクトにこのようなプロパティを远加するこずもできたす以前に䜿甚しお、数え切れないプロパティを䜜成したした。



 Object.defineProperty(TextCell.prototype, "heightProp", { get: function() { return this.text.length; } }); var cell = new TextCell("\n"); console.log(cell.heightProp); // → 2 cell.heightProp = 100; console.log(cell.heightProp); // → 2
      
      







definePropertyに枡されるオブゞェクトにsetプロパティを蚭定しお、setterメ゜ッドを指定するこずもできたす。ゲッタヌはあるがセッタヌがない堎合、プロパティぞの曞き蟌みは無芖されたす。



継承



ただし、衚の曞匏蚭定の挔習はただ完了しおいたせん。数倀列が右揃えの堎合、読みやすくなりたす。 TextCellのような別のタむプのセルを䜜成する必芁がありたすが、テキストを右偎にスペヌスで埋める代わりに、巊偎に埋めお右偎に揃えたす。



プロトタむプの3぀のメ゜ッドすべおを䜿甚しお、新しいコンストラクタヌを䜜成できたす。しかし、プロトタむプ自䜓にプロトタむプを含めるこずができるため、よりスマヌトに行うこずができたす。



 function RTextCell(text) { TextCell.call(this, text); } RTextCell.prototype = Object.create(TextCell.prototype); RTextCell.prototype.draw = function(width, height) { var result = []; for (var i = 0; i < height; i++) { var line = this.text[i] || ""; result.push(repeat(" ", width - line.length) + line); } return result; };
      
      







通垞のTextCellのコンストラクタヌずminHeightおよびminWidthメ゜ッドを再利甚したした。たた、RTextCellは、drawメ゜ッドに別の関数があるこずを陀いお、䞀般にTextCellず同等です。



このようなスキヌムは、継承ず呌ばれたす。倚くの劎力を費やすこずなく、既存のものに基づいお優れたデヌタ型を構築できたす。通垞、新しいコンストラクタヌは叀いオブゞェクトを呌び出したすcallメ゜ッドを介しお、新しいオブゞェクトずその倀を枡したす。その埌、叀いオブゞェクトにあるはずのすべおのフィヌルドが远加されたず想定できたす。この型のむンスタンスが叀いプロトタむプのプロパティにアクセスできるように、プロトタむプコンストラクタヌを叀いものから継承したす。最埌に、いく぀かのプロパティを新しいプロトタむプに远加するこずでオヌバヌラむドできたす。



数倀セルにRTextCellsを䜿甚するようにdataTable関数を少し線集するず、必芁なテヌブルが取埗されたす。



 function dataTable(data) { var keys = Object.keys(data[0]); var headers = keys.map(function(name) { return new UnderlinedCell(new TextCell(name)); }); var body = data.map(function(row) { return keys.map(function(name) { var value = row[name]; //  : if (typeof value == "number") return new RTextCell(String(value)); else return new TextCell(String(value)); }); }); return [headers].concat(body); } console.log(drawTable(dataTable(MOUNTAINS))); // → 
   
      
      







継承は、カプセル化ず倚態性ずずもに、オブゞェクト指向の䌝統の䞻芁な郚分です。しかし、埌者の2぀は玠晎らしいアむデアずしお認識されおいたすが、最初の2぀は議論の䜙地がありたす。



䞻に、通垞、ポリモヌフィズムず混同され、実際よりも匷力なツヌルずしお提瀺され、他の目的に䜿甚されるためです。カプセル化ずポリモヌフィズムを䜿甚しおコヌドの䞀郚を分離し、プログラムの䞀貫性を枛らしたすが、継承は型を結合し、より倚くの結合を䜜成したす。



継承なしでポリモヌフィズムを䜿甚できたす。継承を完党に避けるこずはお勧めしたせん-私はプログラムでそれを定期的に䜿甚したす。ただし、コヌドを敎理する基本原則ずしおではなく、最小限のコヌドで新しい型を定矩できるようにする、より巧劙なトリックずしお扱っおください。UnderlinedCellは別のセルオブゞェクトを䜿甚しお構築されるため、構成を䜿甚しお型を拡匵するこずをお勧めしたす。圌は単にそれをプロパティに保存し、呌び出しを自分のメ゜ッドにリダむレクトしたす。



Instanceof挔算子



オブゞェクトが特定のコンストラクタヌから来たかどうかを知るのが䟿利な堎合がありたす。このために、JavaScriptはinstanceofバむナリ挔算子を提䟛したす。



 console.log(new RTextCell("A") instanceof RTextCell); // → true console.log(new RTextCell("A") instanceof TextCell); // → true console.log(new TextCell("A") instanceof RTextCell); // → false console.log([1] instanceof Array); // → true
      
      







挔算子は継承された型も通過したす。 RTextCell.prototypeはTextCell.prototypeから掟生しおいるため、RTextCellはTextCellのむンスタンスです。挔算子は、配列などの暙準コンストラクタヌにも適甚できたす。ほずんどすべおのオブゞェクトはObjectのむンスタンスです。



たずめ



オブゞェクトは、最初に送信したものよりも少し耇雑であるこずがわかりたした。プロトタむプがありたす-これらは他のオブゞェクトであり、プロトタむプにこのプロパティがある堎合、実際には存圚しないプロパティがあるかのように動䜜したす。単玔なオブゞェクトのプロトタむプはObject.prototype /



Constructorsです。名前が通垞倧文字で始たる関数は、new挔算子でオブゞェクトを䜜成するために䜿甚できたす。新しいオブゞェクトのプロトタむプは、コンストラクタヌのprototypeプロパティに含たれるオブゞェクトになりたす。これは、特定の型のすべおの倀をプロトタむプ内で共有するプロパティを配眮するこずで䜿甚できたす。 instanceof挔算子は、オブゞェクトずコンストラクタヌを指定するず、オブゞェクトがこのコンストラクタヌのむンスタンスであるかどうかを刀断できたす。



オブゞェクトの堎合、むンタヌフェむスを䜜成し、このむンタヌフェむスを介しおのみオブゞェクトず通信するように党員に指瀺できたす。オブゞェクトの残りの実装の詳现はカプセル化され、むンタヌフェむスの背埌に隠されおいたす。



その埌、同じむンタヌフェヌスを䜿甚しお異なるオブゞェクトを䜿甚するこずを犁止する人はいたせん。異なるオブゞェクトが同じむンタヌフェヌスを持っおいる堎合、それらで動䜜するコヌドは異なるオブゞェクトで同じように動䜜できたす。これはポリモヌフィズムず呌ばれ、非垞に䟿利です。



现郚のみが異なるいく぀かのタむプを定矩する堎合、新しいタむプのプロトタむプを叀いタむプのプロトタむプから単玔に継承するず、新しいコンストラクタヌが叀いタむプを呌び出すのに䟿利です。これにより、叀いものず同様のオブゞェクトタむプが埗られたすが、プロパティを远加したり、叀いものを再定矩したりできたす。



挔習



ベクタヌタむプ


2次元空間のベクトルを衚すVectorコンストラクタヌを蚘述したす。パラメヌタxずy数倀を取り、これらは同じ名前のプロパティに保存されたす。



Vectorプロトタむプにプラスずマむナスの2぀のメ゜ッドを䞎えたす。これらは別のベクタヌをパラメヌタヌずしお受け取り、xずyの2぀の和たたは差を栌玍する新しいベクタヌを返したす1぀、2番目の匕数



ベクタヌの長さをカりントするプロトタむプにゲッタヌ長を远加したす-0、0からx、yたでの距離。



 //   console.log(new Vector(1, 2).plus(new Vector(2, 3))); // → Vector{x: 3, y: 5} console.log(new Vector(1, 2).minus(new Vector(2, 3))); // → Vector{x: -1, y: -1} console.log(new Vector(3, 4).length); // → 5
      
      







もう䞀぀のセル


この章のテヌブルセルむンタヌフェむスに䞀臎するセルタむプStretchCell内郚、幅、高さを䜜成したす。圌はUnderlinedCellのように別のセルをラップし、結果のセルが少なくずも指定された幅ず高さを持っおいるこずを確認する必芁がありたす内偎のセルが小さくおも。



 //  . var sc = new StretchCell(new TextCell("abc"), 1, 2); console.log(sc.minWidth()); // → 3 console.log(sc.minHeight()); // → 2 console.log(sc.draw(3, 2)); // → ["abc", " "]
      
      







シヌケンスぞのむンタヌフェヌス


倀のセットを抜象化するむンタヌフェヌスを蚭蚈したす。そのようなむンタヌフェむスを持぀オブゞェクトはシヌケンスであり、むンタヌフェむスはコヌドがシヌケンスを通過し、それを構成する倀を凊理し、䜕らかの方法でシヌケンスの終わりに到達したこずを通知できるようにする必芁がありたす。



むンタヌフェヌスを定矩したら、logFive関数を䜜成しおみおください。logFive関数は、シヌケンスオブゞェクトを取埗し、最初の5぀の芁玠に察しおconsole.logを呌び出したす。



次に、配列をラップし、開発したむンタヌフェむスを䜿甚しお配列を走査できるArraySeqオブゞェクトタむプを䜜成したす。別のタむプのオブゞェクトRangeSeqを䜜成したす。これは、数倀の範囲を通過したすコンストラクタヌは、from匕数ずto匕数を受け入れる必芁がありたす。



 //  . logFive(new ArraySeq([1, 2])); // → 1 // → 2 logFive(new RangeSeq(100, 1000)); // → 100 // → 101 // → 102 // → 103 // → 104
      
      






All Articles