演算子?。、?? および|>:今後お楽しみいただけるJavaScript機能

今日公開している資料の著者であるJustin Fullerは、近い将来JavaScriptに登場することが期待される3つの新機能を検討することを提案しています。 最初に、JS開発プロセスについて説明し、その後、これらの機能の概要を示し、その使用例を示します。 オペレーターについて?.



??



および|>







ECMAScriptおよびJavaScript開発について



ECMA TC39ワーキンググループの機能、およびJavaScriptを改善するための提案を選択および処理する方法に既に精通している場合は、このセクションをスキップしてもかまいません。 あなたがこれについて学ぶことに興味がある人の一人である場合-TC39の機能の概要を以下に示します。



JavaScriptは、 ECMAScriptと呼ばれる標準の実装です。これは、Webブラウザーの初期に登場した言語実装を標準化するために作成されました。



ECMAScript標準には8つのエディションと7つのリリースがあります(標準の第4版は出ていませんが、第3版はすぐに第5版になります)。 JavaScriptエンジンの開発者は、標準のリリース後に言語革新の実装を開始します。 ここでは、すべてのエンジンがすべての機能を実装しているわけではなく、一部のエンジンは他のエンジンよりもイノベーションを導入するのに時間がかかることがわかります。 この状況は理想的ではありませんが、それでも標準が完全に欠けているよりはましです。



申し出



この言語の新機能は、最初はいわゆるセンテンスを表します。センテンスは、標準に含まれる前に承認プロセスを経ます。 提案が有用であり、すでに存在するすべてのものと下位互換性があると判明した場合、次の標準の版に含まれます。



提案を考えるためのプロセスはこのドキュメントで説明された5つのステップから成ります 。 当初、提案はドラフト状態(ストローマン)であり、 ステージ0と同じです。 このステップでは、提案はまだ技術委員会に提出されていないか、まだ却下されていませんが、次の承認段階への移行を許可する基準を満たしていません。 以下で説明するこれらの機能は、すでにステージ0に合格しています。



JSのイノベーションを実稼働で使用しないようにすることをお勧めします。ステージ0にあることを説明する提案。より安定した調整ステージへの移行を待つことをお勧めします。 この推奨事項の目的は、提案が拒否された場合や、大幅に変更された場合に問題を回避できるようにすることです。



試験システム



プログラミング言語の新機能について説明する資料には、多くの場合、コンテキストから取り出されたコードのスニペットが含まれています。 これらの機能は、教育用アプリケーションの作成に使用される場合があります。 ただし、ここではどちらも行いません。 私はTDDの大ファンなので、新しいテクノロジーを学ぶ最良の方法はテストすることだと思います。



ここで、JS Newkirkがトレーニングテストと呼ぶJSの記述された機能の開発に使用します。 このようなテストは、特定の言語で記述されたコードに関するステートメントに基づいていません。 これらは、言語自体に関するステートメントの分析に基づいています。 同じアプローチは、サードパーティのAPIを学習するとき、および言語機能を習得するときに役立ちます。



トランスピレーター



トランスパイラーに慣れていない場合は、まだ実装されていない言語機能をどのように使用するかについて質問があります。 JavaScriptは常に進化しており、一般的なエンジンのプログラマにとって興味深い新機能を実装するには時間がかかることに注意してください。 その結果、JSエコシステムにはトランスピレーターのようなものがあります。



たとえば、標準にまだ含まれておらず、人気のあるエンジンによって実装されていない最新の機能を使用して、JSで記述されたコードを既存のJavaScriptランタイムが理解できるJSコードに変換できます。 これにより、たとえばコード内でステージ0の文を使用することも可能になります。コードがトランスパイラーで処理された後の動作は、たとえば最新のブラウザーやNode.jsで実行できます。 これは、ランタイム環境で、サポートするJSバージョンの1つで記述されたコードのように見えるように新しいコードを変換することによって行われます。



最も人気のあるJavaScriptトランスパイラーの1つはBabelであり、すぐにその使用方法について説明します。



作業環境の準備



自分で説明するすべてを繰り返したい場合は、npmプロジェクトをセットアップし、必要な依存関係をインストールすることでこれを行うことができます。 現在、 Node.jsNPMがインストールされていると想定しています。



実験の準備をするには、これらの実験用に予約されたディレクトリにある次のコマンドを実行します。



 npm init -f && npm i ava@1.0.0-beta.3 @babel/preset-env@7.0.0-beta.42 @babel/preset-stage-0@7.0.0-beta.42 @babel/register@7.0.0-beta.42 @babel/polyfill@7.0.0-beta.42 @babel/plugin-transform-runtime@7.0.0-beta.42 @babel/runtime@7.0.0-beta.42 --save-dev
      
      





次に、 package.json



ファイルに次を追加します。



 "scripts": { "test": "ava" }, "ava": {    "require": [        "@babel/register",   "@babel/polyfill"   ] }
      
      





次に、次の内容の.babelrc



ファイルを作成します。



 {  "presets": [      ["@babel/preset-env", {          "targets": {              "node": "current"          }   }],     "@babel/preset-stage-0"  ],  "plugins": [      "@babel/plugin-transform-runtime" ] }
      
      





これで、JSの新機能を調査するテストを作成する準備が整いました。



1.オペレーター?



JavaScriptアプリケーションを作成するとき、私たちは常にオブジェクトを操作します。 ただし、これらのオブジェクトには、期待どおりの構造がない場合があります。 たとえば、データを持つオブジェクト。 同様のオブジェクトは、たとえば、データベースへのクエリの結果として、または特定のAPIにアクセスすることで取得できます。



 const data = { user: {   address: {     street: 'Pennsylvania Avenue',   }, }, };
      
      





このオブジェクトは、登録されたユーザーを記述します。 同様のオブジェクトがありますが、この場合、彼が説明したユーザーは登録を完了していません。



 const data = { user: {}, };
      
      





この「不完全な」オブジェクトにネストされたaddress



オブジェクトのaddress



プロパティにアクセスしようとして、必要なすべてのデータを含むオブジェクトのように見えると、このエラーが発生する可能性があります。



 console.log(data.user.address.street); // Uncaught TypeError: Cannot read property 'street' of undefined
      
      





これを避けるために、現在の状況では、 street



プロパティにアクセスするために、次の構造を使用する必要があります。



 const street = data && data.user && data.user.address && data.user.address.street; console.log(street); // undefined
      
      





私は、これらのすべてが、第一に見た目が悪く、第二に、書くのが難しい、そして第三に、そのような構造が長すぎることが判明したと信じています。



そのような状況では、オプションのシーケンスまたはオプションのチェーン(オプションのチェーン)は、ドット( ?.



)の付いた疑問符のように見える演算子によって表され、非常に適切であることが判明します。



 console.log(data.user?.address?.street); // undefined
      
      





見た目が良く、そのような構造の構築は簡単です。 あなたはこの声明に同意すると確信しています。 この新しい機会の有用性を確信して、私たちはそれを探求します。 optional-chaining.test.js



ファイルにコードを配置して、テストを作成します。 このセクションでは、このファイルに新しいテストを徐々に追加します。



 import test from 'ava'; const valid = { user: {   address: {     street: 'main street',   }, }, }; function getAddress(data) { return data?.user?.address?.street; } test('Optional Chaining returns real values', (t) => { const result = getAddress(valid); t.is(result, 'main street'); });
      
      





このテストにより、 ?.



演算子を検証できます?.



、オブジェクトが期待どおりに見える場合、ポイントを介してオブジェクトのプロパティにアクセスする方法とまったく同じように機能します。 次に、オブジェクトが私たちが考えているものではない状況で、この演算子の動作をチェックします。



 test('Optional chaining returns undefined for nullish properties.', (t) => { t.is(getAddress(), undefined); t.is(getAddress(null), undefined); t.is(getAddress({}), undefined); });
      
      





そして、配列要素にアクセスするために使用される場合のオプションのシーケンスの仕組みは次のとおりです。



 const valid = { user: {   address: {     street: 'main street',     neighbors: [       'john doe',       'jane doe',     ],   }, }, }; function getNeighbor(data, number) { return data?.user?.address?.neighbors?.[number]; } test('Optional chaining works for array properties', (t) => { t.is(getNeighbor(valid, 0), 'john doe'); }); test('Optional chaining returns undefined for invalid array properties', (t) => { t.is(getNeighbor({}, 0), undefined); });
      
      





オブジェクトに機能が実装されているかどうかわからないことがあります。 たとえば、ブラウザで作業する場合、この状況は一般的です。 古いブラウザには、特定の機能の実装が含まれていない場合があります。 オペレーターのおかげ?.



関心のある機能がオブジェクトに実装されているかどうかを確認できます。 方法は次のとおりです。



 const data = { user: {   address: {     street: 'main street',     neighbors: [       'john doe',       'jane doe',     ],   },   getNeighbors() {     return data.user.address.neighbors;   } }, }; function getNeighbors(data) { return data?.user?.getNeighbors?.(); } test('Optional chaining also works with functions', (t) => { const neighbors = getNeighbors(data); t.is(neighbors.length, 2); t.is(neighbors[0], 'john doe'); }); test('Optional chaining returns undefined if a function does not exist', (t) => { const neighbors = getNeighbors({}); t.is(neighbors, undefined); });
      
      





チェーンに何か問題がある場合、式は実行されません。 このメカニズムの内部実装について話すと、式は次のように変換されます。



 value == null ? value[some expression here]: undefined;
      
      





その結果、 ?.



値がundefined



またはnull



として表される場合、何も行われません。 次のテストを使用して、このルールの動作を確認できます。



 let neighborCount = 0; function getNextNeighbor(neighbors) { return neighbors?.[++neighborCount]; } test('It short circuits expressions', (t) => { const neighbors = getNeighbors(data); t.is(getNextNeighbor(neighbors), 'jane doe'); t.is(getNextNeighbor(undefined), undefined); t.is(neighborCount, 1); });
      
      





ご覧のとおり、オプションのシーケンスにより、 if



構文、 lodash



などのlodash



パーティライブラリ、および&&



を使用する不器用なlodash



の必要性が減少します。



▍演算子?..とパフォーマンス



オプションのシーケンスを使用すると、システムに余分な負荷がかかることにお気づきでしょう。 事は、演算子が使用されるたびに?.



、システムは追加のチェックを強制されます。 オペレーター乱用?.



プログラムのパフォーマンスに大きく影響する可能性があります。



この機能を何らかの検証システムで使用することをお勧めします。検証システムを使用すると、オブジェクトをどこかから受け取ったとき、またはオブジェクトを作成したときに分析できます。 これにより、設計の必要性が減り?.



パフォーマンスへの影響を制限します。



2.演算子??



JavaScriptでの作業中に発生する可能性のある一般的な操作を次に示します。 通常、これらは単一の式のように見えますが、その意味は次のとおりです。



  1. undefined



    およびnull



    値を確認しnull



  2. デフォルト値を指定します。
  3. 0



    false



    、および''



    の出現によってデフォルト値が使用されないようにします。


同様の式の例を次に示します。



 value != null ? value : 'default value';
      
      





また、この式の非識字バージョンを見つけることができます。



 value || 'default value'
      
      





2番目の例の問題は、上記リストの3番目の項目がここで実行されないことです。 数値0



の出現、値false



または空の文字列は値false



として認識されfalse



が、これは必要なものではありません。 そのため、 null



undefined



チェックを明示的に行う必要があります。



 value != null
      
      





この式はこれに類似しています:



 value !== null && value !== undefined
      
      





このような状況では、2つの疑問符( ??



)のように見える「 null



値を持つユニオン」( nullish coalescence )と呼ばれる新しい演算子が役立ちます。 同様の状況で、次の構成を使用できるようになります。



 value ?? 'default value';
      
      





これにより、 false



として認識される式に値が表示されたときに誤ってデフォルト値を使用することから保護されfalse



が、同時に、3項演算子に頼らずにフォームと!= null







これで、この演算子に慣れてきたので、実際にテストするためのテストを作成できます。 これらのテストはnullish-coalescing.test.js







 import test from 'ava'; test('Nullish coalescing defaults null', (t) => { t.is(null ?? 'default', 'default'); }); test('Nullish coalescing defaults undefined', (t) => { t.is(undefined ?? 'default', 'default'); }); test('Nullish coalescing defaults void 0', (t) => { t.is(void 0 ?? 'default', 'default'); }); test('Nullish coalescing does not default 0', (t) => { t.is(0 ?? 'default', 0); }); test('Nullish coalescing does not default empty strings', (t) => { t.is('' ?? 'default', ''); }); test('Nullish coalescing does not default false', (t) => { t.is(false ?? 'default', false); });
      
      





これらのテストから、 null



undefined



およびvoid 0



デフォルト値が使用されていることがわかりnull



undefined



に変換されます)。 同時に、式に現れる値が0



、空の文字列、 false



など、 false



として認識される場合、デフォルト値は適用されません。



3.演算子|>



関数型プログラミングでは、 compositionのようなものがあります。 これはアクションであり、複数の関数呼び出しのチェーンです。 各関数は、前の関数の出力を入力として受け入れます。 通常のJavaScriptを使用して作成したコンポジションの例を次に示します。



 function doubleSay (str) { return str + ", " + str; } function capitalize (str) { return str[0].toUpperCase() + str.substring(1); } function exclaim (str) { return str + '!'; } let result = exclaim(capitalize(doubleSay("hello"))); result //=> "Hello, hello!"
      
      





これは非常に普及しているため、関数の構成の手段は、 lodashramdaなどの関数型プログラミングをサポートする多くのライブラリに存在します。



垂直バーと大なり記号( |>)



組み合わせのように見える新しいパイプライン演算子のおかげで、サードパーティライブラリの使用を放棄し、上記の例を次のように書き換えることができます。



 let result = "hello" |> doubleSay |> capitalize |> exclaim; result //=> "Hello, hello!"
      
      





この演算子の目的は、関数呼び出しチェーンの可読性を改善することです。 さらに、将来、この演算子は機能の部分的な適用の構築に適用されるでしょう。 これで、次のように実行できます。



 let result = 1 |> (_ => Math.max(0, _)); result //=> 1 let result = -5 |> (_ => Math.max(0, _)); result //=> 0
      
      





基本事項を扱ったので、 pipeline-operator.test.js



ファイルにテストを配置します。



 import test from 'ava'; function doubleSay (str) { return str + ", " + str; } function capitalize (str) { return str[0].toUpperCase() + str.substring(1); } function exclaim (str) { return str + '!'; } test('Simple pipeline usage', (t) => { let result = "hello"   |> doubleSay   |> capitalize   |> exclaim; t.is(result, 'Hello, hello!'); }); test('Partial application pipeline', (t) => { let result = -5   |> (_ => Math.max(0, _)); t.is(result, 0); }); test('Async pipeline', async (t) => { const asyncAdd = (number) => Promise.resolve(number + 5); const subtractOne = (num1) => num1 - 1; const result = 10   |> asyncAdd   |> (async (num) => subtractOne(await num)); t.is(await result, 14); });
      
      





これらのテストを分析すると、パイプラインでasync



キーワードを使用して宣言された非同期関数を使用する場合、 await



キーワードを使用しawait



値が表示されるのを待つ必要があることがわかります。 ここでのポイントは、値がPromise



オブジェクトになることです。 フォームの構築をサポートすることを目的とした変更に関するいくつかの提案があります|> await asyncFunction



、まだ実装されておらず、将来の運命に関する決定はまだ行われていません。



まとめ



この資料専用のJSの期待される機能をお楽しみください。 この記事で行ったテストを含むリポジトリを次に示します。 そして、ここでは、利便性のために、ここで考慮される革新へのリンク: オペレーター? 演算子??演算子|>



親愛なる読者! 話し合ったJSの新機能についてどう思いますか?






All Articles