今日は、あまり知られていない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
オブジェクトを持たない矢印関数を除く)。 関数が呼び出されたときに関数に渡される引数のリストが含まれています。 さらに、いくつかの興味深いプロパティがあります。
-
arguments.callee
は、現在の関数へのリンクが含まれています。 -
arguments.caller
は、現在の関数を呼び出した関数への参照が含まれています。
例を考えてみましょう。
// 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>
この機能を使用する興味深い方法は、多くのライブラリにあります。 以下に例を示します。
- スタイル付きコンポーネント -Reactアプリケーションで使用します。
- es2015-i18n-tag-プロジェクトの翻訳および国際化用。
- chalk-マルチカラーのメッセージをコンソールに出力するため。
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のあまり知られていない機能をいくつか知っていて、それらの実用的なアプリケーションのオプションを見つけたら-それらについて教えてください。