JavaScriptおよびTypeScriptでのメタプログラミング

プロローグ



メタプログラミングの技術と基礎を説明するいくつかのミニ像をあなたの裁判所に提出したいと思います。 主にJavaScriptまたはTypeScriptでの特定のテクニックの使用について書いています。

これは、シリーズの最初の記事です(できれば最後ではありません)。







メタプログラミングとは



メタプログラミングは、コンピュータープログラムが他のプログラムをデータとして扱うことができるプログラミング手法です。 つまり、プログラムは、他のプログラムを読み取り、生成、分析、または変換するように設計でき、実行中にプログラム自体を変更することもできます。 場合によっては、 これによりプログラマーはコードの行数を最小限に抑えてソリューションを表現できるため、開発時間を短縮できます。

わかりにくい説明ですが、メタプログラミングの主な利点は理解できます。







...これにより、プログラマーはコードの行数を最小限に抑えてソリューションを実装できるため、開発時間を短縮できます。







実際、メタプログラミングには多くの顔と装いがあります。 また、「メタプログラミングが終了し、プログラミング自体が開始する場所」について長い間議論できます







私自身は、次のルールを受け入れました。







  1. メタプログラミングはビジネスロジックを処理せず、それを変更せず、いかなる方法でもそれに影響しません。
  2. すべてのメタプログラミングコードを削除しても、プログラムに(根本的に)影響を与えることはありません。


JavaScriptでは、メタプログラミングは比較的新しいトレンドであり、その基本は記述子です。







JavaScript記述子



記述子は、オブジェクト内の特定のプロパティまたはメソッドの一種の記述(メタ情報)です。







このオブジェクト( 記述子 )を理解して適切に操作することで、オブジェクトのメソッドやプロパティを作成および変更するだけでなく、それ以上のことが可能になります。

また、 記述子は、デコレータを使用した作業を理解するのに役立ちます(詳細については次の記事で説明します)。







明確にするために、オブジェクトがアパートの説明であると想像してください。

アパートのオブジェクトについて説明します。







let apt = { floor: 12, number: '12B', size: 3400, bedRooms: 3.4, bathRooms: 2, price: 400000, amenities: {...} };
      
      





変更可能なプロパティと変更できないプロパティを判断しましょう。







たとえば、アパートの床や合計サイズを変更することはできませんが、部屋やバスルームの数はかなり可能です。

したがって、次の要件があります: aptオブジェクトでは、プロパティを変更できないようにします: floorおよびsize







この問題を解決するには、これら各プロパティの記述子が必要です。 記述子を取得するには、クラスObjectに属する静的メソッドgetOwnPropertyDescriptorを使用します。







 let descriptor = Object.getOwnPropertyDescriptor(todoObject, 'floor'); console.log(descriptor); // Output { value: 12, writable: true, enumerable: true, configurable: true }
      
      





順番に分析しましょう:

値:any-実際には、ある時点でフロアプロパティに割り当てられた値と同じ

writable:boolean- を変更するかどうかを決定します

enumerable:boolean- フロアプロパティを一覧表示できるかどうかを決定します(詳細は後述)。

configurable:boolean- 記述子オブジェクトに変更を加える機能を定義します。







フロアプロパティが変更される可能性を防ぐには、初期化後に、 writableの値をfalseに変更する必要があります

記述子のプロパティを変更するには、オブジェクト自体、プロパティの名前、およびdescriptorを受け入れる静的メソッドdefinePropertyがあります。







 Object.defineProperty(apt, 'floor', {writable: false});
      
      





この例では、 記述子オブジェクト全体を渡すのではなく、値がfalseの 書き込み可能なプロパティを1つだけ渡します

次に、floorプロパティの値を変更してみましょう。







 apt.floor = 44; console.log(apt.floor); // output 12
      
      





値は変更されていません。「use strict」を使用すると、エラーメッセージが表示されます。







オブジェクトのプロパティ 'floor'の読み取り専用に割り当てることができません...

そして今、値を変更することはできません。 ただし、 書き込み可能-> trueを返してから、 floorプロパティを変更できます。 これを回避するには、 descriptorで 構成可能プロパティーの値をfalseに変更する必要があります







 Object.defineProperty(apt, 'floor', {writable: false, configurable: false});
      
      





記述子のプロパティの値を変更しようとすると...







 Object.defineProperty(apt, 'floor', {writable: true, configurable: true});
      
      





応答として、次のものを取得します。







TypeError:プロパティを再定義できません:floor

つまり、 floorの値もその記述子も変更することはできません。


要約する



オブジェクトのプロパティ値を変更しないようにするには、このプロパティの構成を登録する必要があります: {writable:false、configurable:false}







これは、プロパティの初期化中に行うことができます。







 Object.defineProperty(apt, 'floor', {value: 12, writable: false, configurable: false});
      
      





または後。







 Object.defineProperty(apt, 'floor', {writable: false, configurable: false});
      
      





最後に、クラスを使用した例を考えます。







 class Apartment { constructor(apt) { this.apt = apt; } getFloor() { return this.apt.floor } } let apt = { floor: 12, number: '12B', size: 3400, bedRooms: 3.4, bathRooms: 2, price: 400000, amenities: {...} };
      
      





getFloorメソッドを変更します。







 Apartment.prototype.getFloor = () => { return 44 }; let myApt = new Apartment(apt); console.log(myApt); // output will be changed. 44
      
      





次に、 getFloor()メソッドの記述子を変更します







 Object.defineProperty(Apartment.prototype, 'getFloor', {writable: false, configurable: false}); Apartment.prototype.getFloor = () => { return 44 }; let myApt = new Apartment(apt); console.log(myApt); // output will be original. 12
      
      





この記事が、 記述子とは何か、どのように使用できるかについてもう少し光を当てることを願っています。







上記のすべてが完全に真実であるとか、唯一の正しいとは限りません。



All Articles