js関数の複数の書き方

14820 ワード

原文のリンク:https://zhuanlan.zhihu.com/p/27091503 著者:Liubara
JavaScriptコードと付き合ったことがあるなら、関数の定義と呼び出しについてよく知っているはずですが、どれぐらいの関数を定義する方法がありますか?Test 262(ブラウザJavaScript標準テスト)を作成して維持するために、これは非常に一般的な挑戦であり、特に新しい特性が現れ、既存の関数文法に関連したり、既存の関数のAPIを拡張したりしたとき.新しいまたは提案された文法およびAPIが有効であり、言語の各々の既存の変形体に対して有効であることを確認する必要がある.
本論文の内容はJavaScriptに既に存在している関数文法フォーマットの概要を説明するものである.これらの形式で生成されたオブジェクトは「呼び出し可能」ではないので、この文書では「呼び出し可能」な関数オブジェクトを生成するフォーマットにのみ注目します.また、この記事はパラメータリスト(デフォルトのパラメータ、構成、またはコンマを含む)も含まれていません.これらの話題はもう一つの文章を書くのに十分です.
古い方法
関数宣言と表現
ご存知のように、最も広く使われているのも一番早い関数定義形式であり、関数宣言と関数式である.前者は最初に設計された一部(1995)であり、第一のバージョンの規範(1997年)に登場し、後者は第三のバージョン(1999年)に導入された.これらの仕様から三つの異なる定義形式を抽出できます.
//     
function BindingIdentifier() {}

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

//        
(function() {});
なお、匿名関数表現には「名前」が残っているかもしれません.この記事でWhat’s in a Function Name?の中でよく説明されています.
Functionコンストラクタ
言語の「関数API」について議論しているうちに、Functionコンストラクタについて議論し始めました.最初の言語設計を考慮すると、関数宣言の構文形式は、関数コンストラクタのAPIの「文字」形式として解釈され得る.Functionコンストラクタは定義関数のための方法を提供しています.関数パラメータと関数本体をN文字列パラメータで指定します.(以下の例のように)最後の文字列パラメータは常に関数本体です.ほとんどの場合、このような形式は適切ではないので、その使用は非常に少ないです.しかし、最初のバージョンのECMAScript以来、ずっとJavaScriptに存在しています.
new Function('x', 'y', 'return x * y;');
新しい方法
ES 2015が発売されてから、いくつかの新しい文法形式が導入されました.これらの形の変化は大きいです!
not-so-anonymous関数宣言
これは新しい匿名関数ステートメントの形式です.ES Modulesを使ったことがあるなら、このような文法を明確にすべきです.匿名関数の表現とよく似ているように見えるかもしれませんが、実際には「default」というデフォルトの名前があります.
// not-so-anonymous     
export default function() {}
ちなみに、この名前自体は有効な識別子ではなく、匿名関数に結び付けられていません.
メソッドの定義
以下の例については,属性の値として匿名関数式と命名関数式が定義されていることがすぐにわかるはずである.これらは異なる文法形式ではないことに注意してください.これらは、以前に議論された関数式の例であり、初期オブジェクトで作成されたものです.この形式は最初にES 3に導入された.
let object = {
  propertyName: function() {},
};
let object = {
  // (BindingIdentifier          )
  propertyName: function BindingIdentifier() {},
};
ES 5にはアクセス器の属性定義が導入されている.
let object = {
  get propertyName() {},
  set propertyName(value) {},
};
ES 2015からJavaScriptは、テキスト属性名と属性名の計算形式、およびアクセス形式を含む簡単な文法を定義する方法を提供する.
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) {}
};
矢印関数
ES 2015の最も論争的な関数の一つとして、矢印関数はすでに周知のようになりました.矢印関数文法は、関数宣言のための2つの異なるフォーマットを提供しています.(矢印の後に「\」の括弧がない場合は、値式)と関数体(コードに0から複数の語句が含まれている場合は関数体)この構文は、単一のパラメータを記述する際に、括弧を使わないことも可能ですが、0つ以上のパラメータは括弧を入れる必要があります.これらの構文構造は、矢印関数が複数の表記形式を持つことを可能にします.
//           
(() => 2 ** 2);

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

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

//               
((x, y) => x ** y);
上記の最後の形態では、パラメータは括弧内にラッピングされているので、まとめられたパラメータリストとして記述されています.これは、パラメータリストまたは特殊な構成モードをマークするシンタックスを提供します.=>x.包括されていない形式、つまり丸括弧がない形、つまり矢印関数は一つの識別子名だけをパラメータとして表現することができます.矢印関数が非同期関数または生成器で定義される場合、この識別子名はawaitまたはyeildをプレフィックスとして定義する必要がありますが、これは矢印関数で得られる最大の程度のものではありません.パラメータリストをかっこで囲む場合.
let foo = x => x ** 2;

let object = {
  propertyName: x => x ** 2
};
生成器
生成器には矢印関数とsetter/getterメソッドを定義する際には追加できない特殊な文法があります.他のすべての文法形式に追加することができます.関数宣言、表現、定義、さらには構成関数を生成することができます.
//      
function *BindingIdentifer() {}

//     not-so-anonymous      
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"]() {}
};
ES 2017
非同期関数
数年間の発展を経て、非同期関数は2017年6月にES 2017のEcmaScript言語規範の第8版に導入されます.それでも、多くの開発者がこの特性を使用しています.これはBabelの早期実現サポートのおかげです.Async関数文法は、非同期動作を説明するために、清潔で統一的な方法を提供します.呼び出し時、Aync関数オブジェクトはPromに戻ります.iseオブジェクトは、このオブジェクトを非同期関数で返したときに解析されます.await表現が含まれている場合、非同期関数は関数の実行を一時停止し、非同期関数の戻り値として使用することができます.その文法は他の形で知られているものと同じです.
//       
async function BindingIdentifier() { /**/ }

// not-so-anonymous       
export default async function() { /**/ }

//          
// (BindingIdentifier is not accessible outside of this function)
(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"]() {}
};
非同期矢印関数
asyncとawaitは普通の声明と表現形式に限定されません.矢印関数にも使えます.
//           
(async x => x ** 2);

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

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

//              
(async (x, y) => { return x ** y; });
更新し続けるES 2017
非同期生成器Aync Generators
次のES 017では、asyncとawaitのキーワードは新しい非同期ジェネレータ形式をサポートするために拡張されます.この特性の進展はproposal’s github repositoryによって追跡できます.async、awaitと既存のジェネレータステートメントとジェネレータ表現文法の組み合わせだと推測できます.呼び出し時に、非同期ジェネレータは、そのnext(next)に戻ります.この方法はPromiseオブジェクトに戻り、直接にディエゼルオブジェクトに戻るのではなく、ディエゼルオブジェクトを解析します.多くの場所で非同期ジェネレータを見つけることができます.これはジェネレータ関数で見られているかもしれません.
//        
async function *BindingIdentifier() { /**/ }

// not-so-anonymous        
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"]() {}
};
複雑な挑戦
各関数構文フォーマットは学習および使用に挑戦するだけでなく、JS実行時間およびTest 262の実装および維持にも挑戦するものであり、新しい構文形式を導入すると、Test 262はすべての関連する構文ルールとともに新しい形式をテストしなければならない.例えば、標準パラメータ構文の試験形式は簡単な関数宣言形式に限定され、他の形式において仮定される.文法も正常に機能するのは賢明ではありません.文法規則ごとにテストを経て、これらのテストタスクを個人に割り当てることは不合理です.したがって、テスト生成ツールの設計と実現につながっています.テスト生成ツールは上書きが可能であることを保証します.より詳細な方法です.このプロジェクトは、異なるテストケースとテンプレートからなる一連のソースファイルを含んでいます.例えば、各関数形式のパラメータをどうやって調べますか?あるいは関数形式のテスト、さらには範囲を超えた関数形式が含まれています.これらの関数形式では、バインディングを解除したり、値を分解したりすることが適用されます.密集と長時間の時間がかかるかもしれませんが、の送信要求ですが、カバー率は常に向上し、常に新しいエラーが発見される可能性があります.
なぜすべての関数の書式を知ることが重要ですか?
テストをTest 262で作成する必要がない場合は、すべての関数のフォームを計算し、一覧表示することは重要ではないかもしれません.ここには多くのフォーマットのテンプレートが記載されています.新しいテストは、既存のテンプレートを起点として簡単に使用することができます.EcmaScript仕様の良好なテストがTest 262の主要な任務であることを確認します.これは、すべてのJavaScript実行時間に直接的な影響を与えます.のフォーマットが多くなればなるほど、カバー率が全面的になります.これは新しい機能をよりシームレスに集積することを助けます.お使いのプラットフォームが何であろうと.