ES6以降。 第2章:構文。 パート1



JSアプリケーションの作成経験がある場合は、おそらくその構文に精通しているでしょう。 それは非常に珍しく、いくつかの癖がありますが、一般的に考え抜かれ、理解可能で、他の言語と多くの類似点があります。

ES6では、いくつかの新しい構文形式を追加します。 この章では、それらの多くを調べて、私たちの武器庫の新機能を見つけます。

注:この本の執筆時点では、ES6のほとんどの新機能は、人気のあるブラウザー(Firefox、Chromeなど)と多くの興味深い環境の両方で既に実装されています。 しかし、残念なことに、すべてのブラウザーまたは環境がES6で動作できるわけではありません。 前の章で述べたように、トランスパイリングがすべてです。 このアプローチを使用すると、このマニュアルの例を実行できます。 これを行うには、自由に使えるツールがいくつかあります-ES6Fiddlehttp://www.es6fiddle.net /)は、 Babelの ES6とREPL( http://babeljs.io/repl/を試すための優れた使いやすいプラットフォームです)

ブロックスコープ


JavaScriptの可視性の基本単位は関数であることはご存知でしょう。 スコープでブロックを実装する必要がある場合、IIFE(すぐに呼び出される関数の式)を使用するのが最も適切な方法です。次に例を示します。
var a = 2; (function IIFE(){ var a = 3; console.log( a ); // 3 })(); console.log( a ); // 2 

Letステートメント


ただし、現在では、あらゆる種類のブロックにスコープを実装でき、これはブロックスコープと呼ばれます。 新しいスコープを作成するために必要なのは、長い間誰もが知っている中括弧{ .. }ペア{ .. } 。 内部(またはグローバル)スコープの変数を宣言するために使用したvar演算子を使用する代わりに、 letを使用してブロックを宣言できます。
 var a = 2; { let a = 3; console.log( a ); // 3 } console.log( a ); // 2 

もちろん、これはJSにとっては珍しいことですが、他の言語で作業した開発者にとっては、このパターンは長い間知られています。

これは、 { ... }を使用してブロックの可視性を作成する最良の方法{ ... } 。 ブロックの先頭にlet宣言を配置することをお勧めします。複数の宣言がある場合は、 letステートメントを1つだけ使用letください。

様式的には、これらの変数の対象となるブロックを明示的に強調するために、開始{と同じ行でアナウンスすることをお勧めします。
 { let a = 2, b, c; // .. } 

今ではかなり奇妙に見え、ES6に関する文献のほとんどの推奨事項と一致していませんが、私には狂気の理由があります。 (perevに注意してください:少し低めに学んでください)。

letステートメントには別の推奨形式があり、 letブロックと呼ばれます。
 //      ES6 let (a = 2, b, c) { // .. } 

これは、「 明示的ブロックスコープ 」と呼ぶものの優れた例ですが、 let varと同様に、スコープのブロック全体に拡張されるため、 明示的ではありません

残念ながら、 let (..) { .. } form let (..) { .. }はES6では受け入れられませんでした。 おそらく、今後のES6の更新でこのフォームが表示されますが、現時点では最初の例が最適です。

letステートメントの暗黙的な性質を強調するために、次の例を見てみましょう。
 let a = 2; if (a > 1) { let b = a * 3; console.log( b ); // 6 for (let i = a; i <= b; i++) { let j = i + 10 console.log( j ); } // 12 13 14 15 16 let c = a + b; console.log( c ); // 8 } 

そして、コードを再検討することなく、少しクイズを教えてください: ifブロックに存在する変数とforブロックのみに存在する変数はどれですか?

回答: ifブロックにbブロックスコープの変数ab含まれ、ブロックスコープfor変数ijます。

変数i ifブロックのスコープに追加されなかったのは驚くことではありませんか?

この例では、変数c宣言が非常に低いことにも危険があります。
従来のvarステートメントの種類は、変数をスコープに「 アタッチ 」し、関数が実行される前に入力されたときに、どこで宣言されたかに関係なく変数を初期化します。 この現象は隆起として知られています。 letステートメントは、実行前に変数をスコープにアタッチしますが、関数に入るときに変数を初期化しません。宣言/初期化される前にそのような変数にアクセスしようとすると、エラーになります。

実例:
 { console.log( a ); // undefined console.log( b ); // ReferenceError! var a; let b; } 

注: 変数が宣言または初期化される前の以前の変数呼び出しによって引き起こされるReferenceErrorは、技術的にはTDZ(一時デッドゾーン)エラーと呼ばれます。

「初期化されていない」とは、明示的に値を割り当てる必要があるという意味ではないという事実に注意を喚起したいと思います。 let b; -結構です。 宣言中に値が割り当てられなかった変数は、デフォルトでundefinedに初期化されると想定されています。 したがってlet b; let b = undefined;と同じlet b = undefined; 。 しかし、私は繰り返します-前に、式let b; 失敗します。この変数を使用してください、できません。

別の予期しないひねりは、TDZ変数を使用したtypeofの動作です。
 { if (typeof a === "undefined") { console.log( "cool" ); } if (typeof b === "undefined") { // ReferenceError! // .. } // .. let b; } 

変数宣言さいないため、存在をテストする唯一の安全な方法はtypeof演算子を使用することです。 しかし、変数b場合はそうではありません。変数bは例外をスローします。これは、 letステートメントを使用してはるかに低く宣言されているletです。

推奨するだけではありませんが、新しいletステートメントを使用するすべての広告はブロックの最上部に配置することを強くお勧めします。 これにより、リファクタリングの頭痛から解放され、不可解なエラーが排除され、コードがより理解しやすく、予測可能になります。

注: letステートメントとブロックスコープの詳細については、第3章で説明します。

させる+


letステートメントのもう1つの重要な機能は、 forループでの動作です。
例を考えてみましょう:
 var funcs = []; for (let i = 0; i < 5; i++) { funcs.push( function(){ console.log( i ); } ); } funcs[3](); // 3 

forヘッダーのlet i式は、ループ全体ではなく、個々の反復ごとに新しい変数を宣言します。 同じことがループ内のクロージャーにも当てはまります。 実際、コードは期待どおりに動作します。

let i var i代わりにvar iを使用しようとすると、結果として3ではなく例5が得られます。この場合、クロージャはすべての反復に共通するためです。

少量の水を追加すると、同じことを行う次の例を取得できます。
 var funcs = []; for (var i = 0; i < 5; i++) { let j = i; funcs.push( function(){ console.log( j ); } ); } funcs[3](); // 3 

この例では、反復ごとに新しい変数jを明示的に作成します。 私は最初のオプションを好みます。もちろん、これはそれほど明確ではありませんが、放棄するほどではありません。

定数ステートメント


考慮すべき別のステートメントがあります。これもブロックスコープconst関連しています。

定数。これは、初期初期化後に読み取り専用になる変数です。
例:
 { const a = 2; console.log( a ); // 2 a = 3; // TypeError! } 

宣言中に既に値が割り当てられている変数の値を変更することはできません。 定数を宣言するときは、変数の値を明示的に指定する必要があります 。 デフォルトでは、定数は値undefined取得しません。したがって、そのような値が必要な場合は、手動で割り当てる必要がありますconst a = undefined;

定数には割り当てに制限がありますが、変数の値に関連する制限はありません。 たとえば、定数が複雑な値である場合、引き続き変更できます。
 { const a = [1,2,3]; a.push( 4 ); console.log( a ); // [1,2,3,4] a = 42; // TypeError! } 

したがって、定数kindは定数参照を保持しますが、それが指す配列はまだ動的です。

注:リンクを削除できないため、定数にオブジェクトまたは配列を割り当てることは、値がレキシカルスコープを離れるまでガベージコレクターによって値を削除できないことを意味します。 もちろん、これは望ましい動作かもしれませんが、注意してください。

以前は、変数を意図したリテラルで大文字を使用していたため、そのような変数の値を変更することはお勧めできません。 これで、不要な変更に対して警告する定数を使用できます。

噂によると、定数を使用すると、値が変更されないことがJSエンジンに明らかになるため、この演算子はletまたはvarよりも最適化されたソリューションになります。

これについての空想にもかかわらず、定数を本当に使用する必要がある場合を理解することははるかに重要です。 単純な変数には定数を使用しないでください。そうしないと、誤解を招く可能性があります。

機能のブロック範囲


ES6以降、ブロック内で宣言された関数にはスコープ(ブロック)が含まれることが理解されています。 ES6より前は、さまざまな特定の実装とは異なり、仕様はこれについて沈黙していました。 しかし、今では仕様は真実です。

例を考えてみましょう:
 { foo(); // ! function foo() { // .. } } foo(); // ReferenceError 

この例では、関数はブロックスコープで宣言されているため、外部からアクセスできません。 また、興味深い事実に注目する価値があります。このような変数が初期化される前に操作できないlet宣言とは対照的に、この場合はTDZエラーはありません。

関数のブロックスコープは、以下の例のように古い動作に依存してコードを記述している場合、問題になる可能性があります。
 if (something) { function foo() { console.log( "1" ); } } else { function foo() { console.log( "2" ); } } foo(); // ?? 

ES6以前の環境では、 foo()関数を呼び出した結果は"2" になります。これは、 something 変数の値に関係なく 作業関数を上げることで2番目だけになるためです。

ES6では、最後の行は例外ReferenceErrorスローします。

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


All Articles