JavaScript:多くの顔

JavaScriptの開発に携わっている場合は、どのプラットフォームについて話をしていても、関数の価値を理解できることを意味します。 それらが配置される方法、プログラマーに与える機会は、それらを真に普遍的で不可欠なツールにします。 そう思うと、開発者Test262標準のECMAScriptとの互換性を確保するためのJavaScriptエンジンをテストするために設計された公式のテストスイートを、 - 。



この記事では、関数定義の構文形式の概要を説明します。 特に、JSが登場した日からJSに何が存在していたか、何年にもわたって開発されてきたもの、そして今後何が期待されるかについてお話します。

従来のアプローチ


▍関数宣言と関数式


さらに、JSで関数を定義するための最も有名で広く使用されている方法は、最も古いものです。 これは、関数宣言(関数宣言)および関数式(関数式)です。 第一の方法は、1995年言語と元のバージョンの一部であったとに反映されたの最初のバージョンは1997年に、仕様。 第二は、に提示されている第三版 、1999年に、。

関数を定義するこれらの方法をよく見ると、それらを使用するための3つのオプションがあります。

//   function BindingIdentifier() {} 

 //    // (BindingIdentifier     ) (function BindingIdentifier() {}); 

 //    (function() {}); 

ただし、ここでは、匿名の関数式がまだ「名前」を持っている可能性があることを考慮する価値があります。 ここに関数名に関するいくつかの良いものがあります。

▍関数コンストラクタ


JavaScriptで「API関数」について話す場合は、コンストラクターFunctionこの会話を開始します。 言語設計への初期アプローチを考慮すると、他の構成要素との類推により、上記の関数宣言は、 FunctionコンストラクターAPIの「リテラル」として解釈できます。

Functionコンストラクターは、文字列引数を使用してパラメーターと関数の本体を指定することにより、関数を定義するためのツールを提供します。 これらの引数の最後は、関数の本体です。

 new Function('x', 'y', 'return x ** y;'); 

コンストラクターを使用して関数を定義する場合、動的コード実行に頼らざるを得ないという事実に注意することが重要です。
実際には、 Functionコンストラクターはごくまれにしか使用されませんが、EcmaScriptの初版以来、言語に存在していました。 ほとんどの場合、はるかに便利な代替手段があります。

新しいアプローチ


ES2015標準では、関数を定義するためのいくつかの新しい構文形式が導入されました。 それらには膨大な数のオプションがあります。

suchそのような匿名関数宣言ではない


ESモジュールの経験がある場合は、新しい形式の匿名関数宣言に精通している必要があります。 これは匿名の関数式に非常に似ていますが、このような関数には実際には"*default*" という名前が関連付けられてい "*default*" 。 その結果、関数はそれほど匿名ではありません。

 //        export default function() {} 

ちなみに、このような「名前」は有効な識別子ではなく、ここではバインディングは作成されません。

objectオブジェクトメソッドを定義するためのメソッド


関数を表すオブジェクトのプロパティでは、名前付きおよび匿名の関数式を簡単に認識できます。 これらの構造は、関数定義の特別な構文形式ではないことに注意してください。 これらは、前述の関数式であり、オブジェクトの初期化子で使用されます。 これらの設計はES3で導入されました。

 let object = { propertyName: function() {}, }; let object = { // (BindingIdentifier     ) propertyName: function BindingIdentifier() {}, }; 

ES5で表示されるアクセサプロパティの宣言は次のとおりです。

 let object = { get propertyName() {}, set propertyName(value) {}, }; 

ES2015では、オブジェクトメソッドを定義するための短縮構文が導入されました。これは、通常のプロパティ名の形式でも、名前の文字列表現を囲む角括弧でも使用できます。 アクセサプロパティについても同じことが言えます。

 let object = { propertyName() {}, ["computedName"]() {}, get ["computedAccessorName"]() {}, set ["computedAccessorName"](value) {}, }; 

同様のアプローチを使用して、クラス宣言(クラス宣言)およびクラス式(クラス式)でプロトタイプメソッドを定義できます。

 //   class C { methodName() {} ["computedName"]() {} get ["computedAccessorName"]() {} set ["computedAccessorName"](value) {} } 

 //   let C = class { methodName() {} ["computedName"]() {} get ["computedAccessorName"]() {} set ["computedAccessorName"](value) {} }; 

同じことが静的クラスメソッドにも当てはまります。

 //   class C { static methodName() {} static ["computedName"]() {} static get ["computedAccessorName"]() {} static set ["computedAccessorName"](value) {} } 

 //   let C = class { static methodName() {} static ["computedName"]() {} static get ["computedAccessorName"]() {} static set ["computedAccessorName"](value) {} }; 

▍矢印機能


ES2015に登場した矢印関数は多くのノイズを発生させましたが、最終的には幅広い人気と人気を獲得しました。 矢印関数には2つの形式があります。 1つ目はコンサイスボディで、矢印の後に中括弧は含まれません。割り当て式のみがあります。 2番目はブロック形式です。 ここでは、矢印の後に、関数の本体が中括弧で囲まれています。これは空にすることも、特定の数の式を含めることもできます。

矢印関数に引数がない場合、または複数ある場合は、矢印の前にあるものを括弧で囲む必要があります。 そのような関数に引数が1つしかない場合、括弧の使用はオプションです。

実際には、上記は矢印関数を決定するための多くのオプションの存在を意味します:

 //      (() => 2 ** 2); 

 //       (x => x ** 2); 

 //        (x => { return x ** 2; }); 

 //         ((x, y) => x ** y); 

例の最後の部分は、括弧内の矢印関数パラメーターのセット(カバーされているパラメーター)を示しています。 このアプローチにより、パラメータのリストを操作できるようになります。たとえば、破壊パターンを使用できます。

 ({ x }) => x 

既に説明したように、カバーされていないパラメーターを使用すると、引数が1つだけの矢印関数を指定できます。 この単一の引数の前に、 awaitまたはyield —キーワードを使用できますyield —矢印関数が非同期関数またはジェネレーター内で定義されている場合、この構文の可能性はそこで終わります。

矢印関数は、オブジェクト初期化子で、およびプロパティを設定するときに使用できます。 ここでは、矢印関数式が使用されます。

 let foo = x => x ** 2; 

 let object = { propertyName: x => x ** 2 }; 

▍ジェネレーター


ジェネレータには特別な構文があります。 矢印関数とゲッターとセッターの宣言を除いて、関数の定義にアスタリスクを追加することにあります。 結果は、関数とメソッド、関数式、さらにはコンストラクターの宣言です。 次の例でこれをすべて見てみましょう。

 //   function *BindingIdentifer() {} 

 //        export default function *() {} 

 //   // (BindingIdentifier      ) (function *BindingIdentifier() {}); 

 //    (function *() {}); 

 //   let object = { *methodName() {}, *["computedName"]() {}, }; 

 //      class C { *methodName() {} *["computedName"]() {} } 

 //       class C { static *methodName() {} static *["computedName"]() {} } 

 //      let C = class { *methodName() {} *["computedName"]() {} }; 

 //       let C = class { static *methodName() {} static *["computedName"]() {} }; 

ES2017


synchronous非同期関数


2017年6月にES2017標準が公開され、数年の開発の後、Async Functionsが導入されました。 標準は文字通り「印刷所を去った」という事実にもかかわらず、多くの開発者はすでにBabelのおかげで非同期関数を使用しています。

非同期関数を使用すると、非同期操作を簡単に記述することができます。 それらの使用のおかげで、コードはクリーンで一貫性があります。 非同期関数を呼び出すと、promiseが返されます。promiseは、非同期関数が作業の結果を返した後に解決されます。 awaitキーワードを含む式が非同期関数で見つかった場合、作業を一時停止し、式が完了するのを待つことができます。たとえば、結果を返します。

非同期関数の構文は、すでに検討されているものと特に違いはありません。 主な機能はasyncプレフィックスです:

 //    async function BindingIdentifier() { /**/ } 

 //          export default async function() { /**/ } 

 //     // (BindingIdentifier     ) (async function BindingIdentifier() {}); 

 //     (async function() {}); 

 //   let object = { async methodName() {}, async ["computedName"]() {}, }; 

 //      class C { async methodName() {} async ["computedName"]() {} } 

 //       class C { static async methodName() {} static async ["computedName"]() {} } 

 //      let C = class { async methodName() {} async ["computedName"]() {} }; 

 //       let C = class { static async methodName() {} static async ["computedName"]() {} }; 

synchronous非同期矢印関数


asyncおよびawaitキーワードは、従来の関数宣言および関数式だけでなく使用できます。 それらは矢印関数と互換性があります:

 //       (async x => x ** 2); 

 //    ,      (async x => { return x ** 2; }); 

 //         (async (x, y) => x ** y); 

 //    ,      (async (x, y) => { return x ** y; }); 

未来を見る


synchronous非同期ジェネレーター


JavaScript仕様の将来のバージョンでは、 asyncおよびawaitキーワードの使用がジェネレーターに拡張される予定です。 ここでこの機能性の実装の進歩を観察できます 。 おそらくご想像のとおり、宣言と式を通じて、ジェネレーターを定義するasync/awaitasync/awaitキーワードと既存のフォームの組み合わせについて話します。

非同期ジェネレーターは、呼び出されると、メソッドnext()がオブジェクトによって解決されるプロミスを返すイテレーターを返します。これは、イテレーターが通常返すものです。 通常の状況では、このメソッドを呼び出すと結果が直接返されます。

非同期ジェネレーターは、通常のジェネレーター機能が既に利用可能な場所にあります。

 //    async function *BindingIdentifier() { /**/ } 

 //         export default async function *() {} 

 //    // (BindingIdentifier      ) (async function *BindingIdentifier() {}); 

 //    (async function *() {}); 

 //   let object = { async *propertyName() {}, async *["computedName"]() {}, }; 

 //       class C { async *propertyName() {} async *["computedName"]() {} } 

 //       let C = class { async *propertyName() {} async *["computedName"]() {} }; //       class C { static async *propertyName() {} static async *["computedName"]() {} } 

 //       let C = class { static async *propertyName() {} static async *["computedName"]() {} }; 

結果。 JavaScriptエンジン、テスト、機能について


アイデアから作業コードまでの関数を定義する新しい方法の道を想像してください。 簡略化された形式では、このように見えます。 最初に、アイデアは標準への提案になり、次に標準に入り、次にJSエンジンでの実装が続き、次にプログラマと実用的なアプリケーションによって研究されます。

このプロセスに対するTest262の作業者の貢献は、標準を理解し、既存の言語構成を考慮して新しい言語構成をテストするテストを準備することです。 このアプローチは、テストを作成するための多くの作業を意味します。これは、人に横たわるのは非合理的です。 たとえば、デフォルトの引数のチェックは、すべての形式の関数で実行する必要があります;そのようなことでは、たとえば、関数を宣言する単純な形式だけに制限することはできません。 これらすべてがテストを作成するためのツールの開発につながりました。これにより、テストできるほとんどすべてのものがテストの対象になると言えます。

現在、プロジェクトには、さまざまなテストスクリプトとテンプレートで構成されるソースコード付きファイルのセットが含まれています

たとえば、 ここでは、 arguments関数のプロパティがどのようにチェックされるかを見ることができます、 ここでは -関数のさまざまな形式のテスト。 もちろん、Test262にはさらに多くの機能があります。 言う、 ここある -破壊に関連するテスト。 テストに取り組む過程で、かなり大きなプル要求が受信され、 エラーが検出されて修正されます。 これにより、Test262の品質が常に向上します。これは、JSエンジンがEcmaScript仕様に準拠しているかどうかをより適切にチェックすることを意味します。 これはJavaScript業界に直接影響を与えます。 より多くのソフトウェア構成が識別され、テストでカバーされるほど、エンジンの開発者が新しい機能をより簡単に実装できるようになり、安定性と信頼性が高まり、結果としてJavaScriptプログラムが機能します。

親愛なる読者! JavaScriptで最も頻繁に使用する関数定義の方法は何ですか?

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


All Articles