あまり知られていないJavaScript機能

JavaScriptは、習熟を達成するのが最も難しいプログラミングにおいて、初心者にとって最も単純な言語と呼ばれることがよくあります。 翻訳した資料の著者は、この声明に同意せざるを得ないと語っています。 問題は、JSは非常に古く、非常に柔軟な言語であるということです。 神秘的な構文構造と、まだサポートされている古い機能がいっぱいです。



画像



今日は、あまり知られていないJavaScriptの機能と、実用的なアプリケーションのオプションについて説明します。



JavaScriptは常に新しいものです



私は長年にわたってJavaScriptを使用してきましたが、存在するとは思わなかった何かに常に出会っています。 ここでは、言語の同様のあまり知られていない機能をリストしようとしました。 厳格モードでは、それらの一部は機能しませんが、通常モードでは完全に正しいJSコードサンプルです。 読者にこれらすべてを役立てるように助言することを想定していないことに注意してください。 私がお話しすることはあなたにとって非常に興味深いように思えますが、チームで作業している場合、これらすべてを使い始めて、穏やかに言えば同僚を驚かせることができます。



ここで説明するコードはここにあります。



変数、クロージャ、プロキシオブジェクト、プロトタイプの継承、非同期/待機、ジェネレータなどを上げるようなものは含めなかったことに注意してください。 言語のこれらの機能は、理解しにくいことに起因する可能性がありますが、あまり知られていません。



ボイド演算子



JavaScriptには単項void



演算子があります。 あなたはそれをvoid(0)



またはvoid 0



として見つけたかもしれません。 その唯一の目的は、右側の式を計算し、 undefined



を返すことです。 ここでは0



であるが、これは単に慣例であるために使用されるが、これは必要ではない0



ここでは、有効な式を使用できる。 確かに、この演算子はいずれの場合でもundefined



返します。



 //  void void 0                  // undefined void (0)                // undefined void 'abc'              // undefined void {}                 // undefined void (1 === 1)          // undefined void (1 !== 1)          // undefined void anyfunction()      // undefined
      
      





標準値undefined



使用できるのに、なぜundefined



を返すのに役立つ言語に特別なキーワードを追加するのですか? そうではありません、いくつかの冗長性がありますか?



結局のところ、ほとんどのブラウザーでES5標準が登場する前に、 undefined



標準値に新しい値を割り当てることができました。 次のコマンドを正常に実行できたとします: undefined = "abc"



。 結果として、 undefined



値はundefined



値ではない場合があります。 当時、 void



使用することで、実際のundefined



使用に対する信頼を確保することができました。



コンストラクターを呼び出すときの括弧はオプションです



クラス名の後に追加され、コンストラクターを呼び出す括弧は完全にオプションです(コンストラクターが引数を渡す必要がない限り)。



次の例では、括弧の有無はプログラムの正しい動作に影響しません。



 //     const date = new Date() const month = new Date().getMonth() const myInstance = new MyClass() //     const date = new Date const month = (new Date).getMonth() const myInstance = new MyClass
      
      





ブラケットはIIFEでは使用できません



IIFE構文は常に奇妙に思えました。 なぜこれらすべてのブラケットがあるのですか?



判明したように、括弧は、一部のコードが関数式であり、関数を宣言する誤った試みではないことをJavaScriptパーサーに伝えるためにのみ必要です。 この事実を知ることで、IIFEで囲まれた角かっこを削除すると同時に作業コードを記述する方法がたくさんあることを理解できます。



 // IIFE (function () { console.log('Normal IIFE called') })() // Normal IIFE called void function () { console.log('Cool IIFE called') }() // Cool IIFE called
      
      





ここで、 void



演算子は、それに続くコードが関数式であることをパーサーに伝えます。 これにより、関数宣言を囲む角括弧を取り除くことができます。 ところで、ここでは、任意の単項演算子( void



+



!



-



など)を使用できます。コードは引き続き機能します。 それは素晴らしいことではないですか?



ただし、注意深い読者であれば、単項演算子がIIFEから返される結果に影響するのではないかと思うかもしれません。 実際、そのとおりです。 しかし、良いことは、IIFE実行の結果が必要な場合(たとえば、変数に保存する場合)、IIFEを囲む括弧は必要ないということです。 以下に例を示します。



 // IIFE,    let result = (function () { // ... -  return 'Victor Sully' })() console.log(result) // Victor Sully let result1 = function () { // ... -  return 'Nathan Drake' }() console.log(result1) // Nathan Drake
      
      





最初のIIFEを囲む括弧は、その動作に影響を与えることなく、コードの可読性を向上させるだけです。



IIFEの理解を深めるには、 この資料をご覧ください。



建設



JavaScriptには、式ブロックをサポートするwith



コンストラクトがあることをご存知ですか? 次のようになります。



 with (object)  statement //       with (object) {  statement  statement  ... }
      
      





with



コンストラクトは、コマンドの実行時に使用されるスコープチェーンに渡されたオブジェクトのすべてのプロパティを追加します。



 //    with const person = { firstname: 'Nathan', lastname: 'Drake', age: 29 } with (person) { console.log(`${firstname} ${lastname} is ${age} years old`) } // Nathan Drake is 29 years old
      
      





with



は素晴らしいツールのように思えるかもしれません。 JSのオブジェクトの破壊に関する新機能よりも優れているようですが、実際はそうではありません。



with



構造は非推奨であり、使用を推奨しません。 厳格モードでは、その使用は禁止されています。 ブロックをwith



すると、パフォーマンスとセキュリティの問題が発生することwith



わかりました。



関数コンストラクタ



function



キーワードの使用は、新しい関数を定義する唯一の方法ではありません。 Function



コンストラクターとnew



演算子を使用して、関数を動的に定義できます。 外観は次のとおりです。



 //  Function const multiply = new Function('x', 'y', 'return x*y') multiply(2,3) // 6
      
      





コンストラクタに渡される最後の引数は、関数コードを含む文字列です。 他の2つの引数は関数パラメーターです。



Function



コンストラクターはJavaScriptのすべてのコンストラクターの「親」であることに注意してください。 Object



コンストラクターでもFunction



コンストラクターです。 また、ネイティブのFunction



コンストラクターもFunction



です。 その結果、JSオブジェクトに対して十分な回数行われたobject.constructor.constructor...



という形式の呼び出しは、結果としてFunction



コンストラクターを返します。



機能のプロパティ



JavaScriptの関数がファーストクラスオブジェクトであることは誰もが知っています。 したがって、関数に新しいプロパティを追加することを妨げるものは誰もいません。 これは完全に正常ですが、これはほとんど使用されません。



これはいつ必要になりますか?



実際、この機能が役立つ場合がいくつかあります。 それらを考慮してください。



▍カスタム機能



greet()



関数があるとします。 使用する地域設定に応じて、さまざまなウェルカムメッセージを表示する必要があります。 これらの設定は、関数の外部の変数に保存できます。 さらに、関数には、これらの設定、特にユーザーの言語設定を定義するプロパティが含まれている場合があります。 2番目のアプローチを使用します。



 //  ,   function greet () { if (greet.locale === 'fr') {   console.log('Bonjour!') } else if (greet.locale === 'es') {   console.log('Hola!') } else {   console.log('Hello!') } } greet() // Hello! greet.locale = 'fr' greet() // Bonjour!
      
      





Static静的変数を持つ関数



同様の例をもう1つ示します。 順序付けられた数値のシーケンスを生成する特定のジェネレーターを実装する必要があるとします。 通常、このような状況では、最後に生成された数値に関する情報を保存するために、クラスまたはIIFEの静的カウンター変数が使用されます。 このアプローチでは、カウンターへのアクセスを制限し、追加の変数でグローバルスコープの汚染を防ぎます。



しかし、柔軟性が必要な場合、グローバルスコープを詰まらせるのではなく、そのようなカウンターの値を読み取ったり変更する必要がある場合はどうでしょうか。



もちろん、対応する変数と、それを操作できるメソッドを使用してクラスを作成できます。 または、そのようなことを気にせず、関数のプロパティを使用することはできません。



 //  ,   function generateNumber () { if (!generateNumber.counter) {   generateNumber.counter = 0 } return ++generateNumber.counter } console.log(generateNumber()) // 1 console.log(generateNumber()) // 2 console.log('current counter value: ', generateNumber.counter) // current counter value: 2 generateNumber.counter = 10 console.log('current counter value: ', generateNumber.counter) // current counter value: 10 console.log(generateNumber()) // 11
      
      





引数オブジェクトのプロパティ



ほとんどの人は、関数にはarguments



オブジェクトがあることを知っていると思います。 これは、すべての関数内でアクセス可能な配列のようなオブジェクトです(独自のarguments



オブジェクトを持たない矢印関数を除く)。 関数が呼び出されたときに関数に渡される引数のリストが含まれています。 さらに、いくつかの興味深いプロパティがあります。





例を考えてみましょう。



 //  callee  caller  arguments const myFunction = function () { console.log('Current function: ', arguments.callee.name) console.log('Invoked by function: ', arguments.callee.caller.name) } void function main () { myFunction() } () // Current function: myFunction // Invoked by function: main
      
      





ES5標準では、strictモードでのcallee



caller



プロパティの使用は禁止されていますが、これらは、たとえばライブラリなど、多くのJavaScriptでコンパイルされたプログラムテキストに広く見られます。 したがって、それらについて知ることは有用です。



タグ付きテンプレートリテラル



確かに、JavaScriptプログラミングに関係があるなら、 テンプレートリテラルのことを聞いたことがあるでしょう。 テンプレートリテラルは、ES6標準の多くの優れた革新の1つです。 ただし、タグ付きテンプレートリテラルについて知っていますか?



 //    `Hello ${username}!` //    myTag`Hello ${username}!`
      
      





タグ付きテンプレートリテラルを使用すると、開発者はテンプレートリテラルを文字列に変換する方法を制御できます。 これは、特別なタグを使用して行われます。 タグは、文字列パターンによって解釈される文字列と値の配列を受け取るパーサー関数の名前です。 タグ関数を使用する場合、完成した文字列を返すことが期待されます。



次の例では、タグhighlight



はテンプレートリテラルのデータを解釈し、このデータを完成した行に埋め込み、HTML <mark>



タグに配置して、そのようなテキストがWebページに表示されるときにそれらを選択します。



 //    function highlight(strings, ...values) { //  i -      let result = '' strings.forEach((str, i) => {   result += str   if (values[i]) {     result += `<mark>${values[i]}</mark>`   } }) return result } const author = 'Henry Avery' const statement = `I am a man of fortune & I must seek my fortune` const quote = highlight`${author} once said, ${statement}` // <mark>Henry Avery</mark> once said, <mark>I am a man of fortune // & I must seek my fortune</mark>
      
      





この機能を使用する興味深い方法は、多くのライブラリにあります。 以下に例を示します。





ES5のゲッターとセッター



JavaScriptオブジェクトは、ほとんどの場合、非常に単純です。 user



オブジェクトがあり、 user.age



コンストラクトを使用してそのage



プロパティにアクセスしようとしているとします。 このアプローチでは、このプロパティが定義されている場合、その値を取得し、定義されていない場合、 undefined



を取得します。 すべてが非常に簡単です。



しかし、プロパティの操作はそれほど原始的である必要はありません。 JSオブジェクトは、ゲッターとセッターの概念を実装します。 オブジェクトのいくつかのプロパティの値を直接返す代わりに、必要と考えるものを返す独自のゲッター関数を作成できます。 同じことは、セッター関数を使用してプロパティに新しい値を書き込む場合にも当てはまります。



ゲッターとセッターを使用すると、プロパティを操作するための高度なスキームを実装できます。 プロパティの読み取りまたは書き込み時には、仮想フィールドの概念を使用でき、フィールドの値を確認できます。書き込みまたは読み取り時には、いくつかの有用な副作用が発生する可能性があります。



 //    const user = { firstName: 'Nathan', lastName: 'Drake', // fullname -    get fullName() {   return this.firstName + ' ' + this.lastName }, //      set age(value) {   if (isNaN(value)) throw Error('Age has to be a number')   this._age = Number(value) }, get age() {   return this._age } } console.log(user.fullName) // Nathan Drake user.firstName = 'Francis' console.log(user.fullName) // Francis Drake user.age = '29' console.log(user.age) // 29 // user.age = 'invalid text' // Error: Age has to be a number
      
      





ゲッターとセッターはES5標準の革新ではありません。 彼らは常に言語に存在していました。 ES5では、便利な構文ツールのみが追加されて機能します。 ゲッターとセッターの詳細については、 こちらをご覧ください



ゲッターの使用例の中で、人気のあるColors Node.jsライブラリに注目できます。



このライブラリは、Stringクラス拡張し、それに多くのgetterメソッドを追加します。 これにより、文字列を「色付き」バージョンに変換して、この文字列をログに使用できるようになります。 これは、文字列プロパティを操作することで実行されます



コンマ演算子



JSにはコンマ演算子があります。 コンマで区切られた複数の式を1行に記述し、最後の式を評価した結果を返すことができます。 そのようなデザインは次のようになります。



 let result = expression1, expression2,... expressionN
      
      





ここでは、すべての式の値が計算され、その後expressionNの値が変数result



ます。



for



ループで既にコンマ演算子を使用している可能性があります。



 for (var a = 0, b = 10; a <= 10; a++, b--)
      
      





同じ行に複数の式を記述する必要がある場合に、このステートメントが役立つことがあります。



 function getNextValue() {   return counter++, console.log(counter), counter }
      
      





小さな矢印関数を設計するときに役立ちます。



 const getSquare = x => (console.log (x), x * x)
      
      





プラス演算子



文字列をすばやく数値に変換する必要がある場合は、プラス演算子が役立ちます。 彼はさまざまな数字を扱うことができますが、それは一見、ポジティブな数字だけではありません。 負、8進数、16進数、および指数表記の数値について説明しています。 さらに、 Date



オブジェクトとMoment.jsライブラリオブジェクトをタイムスタンプに変換できます。



 //  "" +'9.11'          // 9.11 +'-4'            // -4 +'0xFF'          // 255 +true            // 1 +'123e-5'        // 0.00123 +false           // 0 +null            // 0 +'Infinity'      // Infinity +'1,234'         // NaN +new Date      // 1542975502981 ( ) +momentObject    // 1542975502981 ( )
      
      





二重感嘆符



「二重感嘆符演算子」(Bang BangまたはDouble Bang)と呼ばれることもあるのは、実際には演算子ではないことに注意してください。 これは、論理否定演算子、または感嘆符が2回繰り返されているように見える論理否定演算子です。 二重感嘆符は、式をブール値に変換できるので便利です。 JSの観点から式がtrueの場合-二重感嘆符で処理した後、 true



が返されます。 それ以外の場合、 false



が返されます。



 //     !!null            // false !!undefined       // false !!false           // false !!true            // true !!""              // false !!"string"        // true !!0               // false !!1               // true !!{}              // true !![]              // true
      
      





ビットごとの否定演算子



それに直面しましょう:誰もビットごとの演算子を気にしません。 私はそれらの使用について話していません。 ただし、ビットごとの否定演算子は多くの状況で使用できます。



この演算子を数値に適用すると、数値N



から-(N+1)



ように変換されます。 N



-1



場合、このような式は0



-1



ます。



この機能はindexOf()



メソッドで使用でき、配列または文字列内の要素の存在を確認するために使用されます。これは、このメソッドは要素を検出せずに-1



返すためです。



 //      indexOf let username = "Nathan Drake" if (~username.indexOf("Drake")) { console.log('Access denied') } else { console.log('Access granted') }
      
      





ES6およびES7標準では、それぞれ文字列と配列に対して、include includes()



メソッドが登場していることに注意してください。 ビットごとの否定演算子とindexOf()



を使用するよりも、要素の存在を判断する方がはるかに便利です。



名前付きブロック



JavaScriptにはラベルの概念があり、これを使用して名前(ラベル)をループに割り当てることができます。 その後、これらのラベルを使用して、 break



ステートメントまたはcontinue



ステートメントを適用break



ときに適切なループを参照できます。 ラベルは通常のコードブロックにも割り当てることができます。



ラベル付きループは、ネストされたループで作業するときに役立ちます。 ただし、コードをブロック単位で整理したり、コードを中断できるブロックを作成する場合にも便利です。



 //    declarationBlock: { //       //     var i, j } forLoop1: //     - "forLoop1" for (i = 0; i < 3; i++) {       forLoop2: //     -  "forLoop2"  for (j = 0; j < 3; j++) {       if (i === 1 && j === 1) {        continue forLoop1     }     console.log('i = ' + i + ', j = ' + j)  } } /* i = 0, j = 0 i = 0, j = 1 i = 0, j = 2 i = 1, j = 0 i = 2, j = 0 i = 2, j = 1 i = 2, j = 2 */ //      loopBlock4: { console.log('I will print') break loopBlock4 console.log('I will not print') } // I will print
      
      





他の言語とは異なり、JSにはgoto



がありません。 その結果、ラベルはbreak



ステートメントとcontinue



ステートメントでのみ使用されます。



まとめ



この記事では、少なくとも他の誰かのコードで何か珍しいことに出会う準備をするために、JSプログラマーにとってその知識が役立つJavaScriptのあまり知られていない機能について話しました。 「unknown JS」のトピックが興味深い場合は、 この出版物をご覧ください。



親愛なる読者! JSのあまり知られていない機能をいくつか知っていて、それらの実用的なアプリケーションのオプションを見つけたら-それらについて教えてください。






All Articles