javascript ES 6にletコマンドの使用紹介が追加されました.

10858 ワード

letコマンド
基本的な使い方
ES 6は、変数を宣言するためにletコマンドを追加しました.その使い方はvarに似ていますが、宣言された変数はletコマンドがあるコードブロック内でのみ有効です.

{
 let a = 10;
 var b = 1;
}

a // ReferenceError: a is not defined.
b // 1
上のコードはコードブロックの中で、それぞれletとvarで2つの変数を宣言しています.その後、コードブロックの外でこれらの2つの変数を呼び出した結果、let宣言の変数がエラーとなり、var宣言の変数が正しい値に戻りました.これはlet宣言の変数がそのコードブロックだけで有効であることを示しています.
forループのカウンターは、letコマンドを使うのに適しています.

for (let i = 0; i < 10; i++) {}

console.log(i);
//ReferenceError: i is not defined
上のコードの中で、カウンタiはfor循環体だけで有効です.循環体の外で引用するとエラーが発生します.
下のコードはvarを使うと最後に10が出力されます.

var a = [];
for (var i = 0; i < 10; i++) {
 a[i] = function () {
  console.log(i);
 };
}
a[6](); // 10
上のコードの中で、変数iはvarで宣言されています.全体の範囲で有効です.したがって、サイクルごとに新しいi値が古い値をカバーし、最後の出力が最後のラウンドのiの値になります.
letを使用すると、宣言した変数はブロックレベルのスコープ内でのみ有効で、最後に出力されるのは6です.

var a = [];
for (let i = 0; i < 10; i++) {
 a[i] = function () {
  console.log(i);
 };
}
a[6](); // 6
上記のコードの中で、変数iはletで宣言されています.現在のiはサイクルのみで有効ですので、サイクル毎のiは実は新しい変数なので、最後に出力されるのは6です.
変数の昇格は存在しません.
letはvarのように「変数アップ」現象が発生しません.したがって、変数は宣言後に必ず使用されます.そうでないとエラーが発生します.

console.log(foo); //   undefined
console.log(bar); //   ReferenceError

var foo = 2;
let bar = 2;
上記のコードの中で、変数fooはvarコマンドで宣言しています.すなわち、スクリプトが起動すると変数fooは既に存在しますが、値がないので、undefinedを出力します.変数barはletコマンドで宣言します.変数のアップグレードは発生しません.これは声明の前に変数barが存在しないという意味です.これを使うとエラーが発生します.
一時死地
ブロックレベルのスコープ内にletコマンドが存在する限り、その宣言した変数は「バインディング」(binding)という領域になり、外部の影響を受けなくなる.

var tmp = 123;

if (true) {
 tmp = 'abc'; // ReferenceError
 let tmp;
}
上のコードには、グローバル変数tmpがありますが、ブロックレベルのスコープ内のletはまた、局所変数tmpを宣言しています.このブロックレベルのスコープを結合させるため、letが変数を宣言する前に、tmp割当値に対してエラーが発生します.
ES 6は、ブロック内にletとconstコマンドが存在する場合、このブロックがこれらのコマンドに対して宣言する変数は、最初から閉鎖作用領域を形成することを明確に規定している.宣言する前にこれらの変数を使うと、エラーが発生します.
つまり、コードブロック内では、letコマンドを使用して変数を宣言する前に、この変数は使用できません.これは文法的には「一時的なデッドゾーン」と呼ばれています.

if (true) {
 // TDZ  
 tmp = 'abc'; // ReferenceError
 console.log(tmp); // ReferenceError

 let tmp; // TDZ  
 console.log(tmp); // undefined

 tmp = 123;
 console.log(tmp); // 123
}
上のコードは、letコマンドが変数tmpを宣言する前に、変数tmpの「デッドゾーン」に属します.
「一時死区」もtypeofが100%安全な操作ではないことを意味します.

typeof x; // ReferenceError
let x;
上記のコードの中で、変数xはlet命令声明を使用していますので、声明の前にxの「デッドゾーン」に属しています.この変数を使うとエラーが発生します.そのため、typeofを実行するとReferenceErrが投げ出されます.
比較として、もし変数が宣言されていないなら、typeofを使うと、逆にエラーが発生しません.

typeof undeclared_variable // "undefined"
上のコードの中で、undeclared_variableは存在しない変数名です.結果として「undefined」に戻ります.ですから、letがない前に、typeof演算子は100%安全です.いつまでも間違いないです.今はこの点が成立しない.このような設計は皆さんに良いプログラミング習慣を身につけるために、変数は必ず声明の後で使います.でないと、エラーを報告します.
いくつかの「デッドゾーン」は比較的に隠れています.見つけにくいです.

function bar(x = y, y = 2) {
 return [x, y];
}

bar(); //   
上のコードの中で、bar関数を呼び出してエラーが発生したのは、パラメータxのデフォルト値が別のパラメータyに等しいためであり、yはまだ宣言されていません.デッドゾーンに属しています.yのデフォルト値がxであれば、エラーは発生しません.この時点でxはすでに宣言されています.

function bar(x = 2, y = x) {
 return [x, y];
}
bar(); // [2, 2]
ES 6は、一時的なデッドエリアとlet、constステートメントに変数のアップグレードがないことを規定しています.主に運行時のエラーを減らすために、変数宣言前にこの変数を使用することを防止して、予想外の行動を招いています.このようなエラーはES 5でよく見られます.このような規定があるので、このような間違いを避けるのは容易です.
つまり、一時的なデッドゾーンの本質は、現在のスコープに入ると、使用する変数が既に存在しますが、取得できないのは、宣言変数の行のコードが現れるまで、その変数を取得して使用することができます.
繰り返し宣言は許可されません.
letは同じスコープ内で同じ変数を繰り返し宣言することができません.

//   
function () {
 let a = 10;
 var a = 1;
}

//   
function () {
 let a = 10;
 let a = 1;
}
したがって、パラメータは関数内で再宣言できません.

function func(arg) {
 let arg; //   
}

function func(arg) {
 {
  let arg; //    
 }
}
ブロックレベルのスコープ
なぜブロックレベルのスコープが必要ですか?
ES 5はグローバルスコープと関数スコープのみで、ブロックレベルのスコープがないため、多くの不合理なシーンをもたらします.
第一のシーンでは、内層変数が外層変数をカバーする場合があります.

var tmp = new Date();

function f() {
 console.log(tmp);
 if (false) {
  var tmp = "hello world";
 }
}

f(); // undefined
上のコードでは、関数fが実行された後、出力結果はundefinedであり、その理由は変数が向上し、内部層のtmp変数が外層のtmp変数をカバーしているためです.
第二のシーンでは、計数するための循環変数が大域変数として漏洩されます.

var s = 'hello';

for (var i = 0; i < s.length; i++) {
 console.log(s[i]);
}

console.log(i); // 5
上記のコードでは変数iはループ制御のみに使用されますが、ループ終了後は消えず、グローバル変数に漏れました.
ES 6のブロックレベルのスコープ
letは実際にJavaScriptのためにブロックレベルのスコープを追加しました.

function f1() {
 let n = 5;
 if (true) {
  let n = 10;
 }
 console.log(n); // 5
}
上の関数には二つのコードブロックがあります.変数nを宣言して、実行後に5を出力します.これは外層コードブロックが内層コードブロックの影響を受けないことを表しています.var定義変数nを使うと最後に出力される値は10です.
ES 6は、ブロックレベルのスコープの任意のネストを許可する.

{{{{{let insane = 'Hello World'}}}}};
上のコードは5層のブロックレベル作用領域を使用しています.外層作用領域は内層作用領域の変数を読み取ることができません.

{{{{
 {let insane = 'Hello World'}
 console.log(insane); //   
}}}};
内層作用領域は外層作用領域の同名変数を定義することができる.

{{{{
 let insane = 'Hello World';
 {let insane = 'Hello World'}
}}}};
ブロックレベルのスコープの出現は、実際には、幅広いアプリケーションを得るための即実行関数式(IIIIFE)を必要としなくする.

// IIFE   
(function () {
 var tmp = ...;
 ...
}());

//        
{
 let tmp = ...;
 ...
}
ブロックレベルのスコープと関数宣言
関数がブロックレベルのスコープ内で宣言できるかどうかは、かなり紛らわしい問題です.
ES 5は、関数が最上位のスコープと関数スコープの中でしか宣言できないと規定しています.ブロックレベルのスコープ宣言ではできません.

//    
if (true) {
 function f() {}
}

//    
try {
 function f() {}
} catch(e) {
}
上のコードの2つの関数宣言は、ES 5の規定によって不法です.
ただし、ブラウザはこの規定を遵守していません.従来のコードに対応するために、ブロックレベルのスコープ内の宣言関数をサポートするために、上記の2つの状況は実際に実行できます.エラーは発生しません.ただし、「厳格モード」ではエラーが発生します.

// ES5    
'use strict';
if (true) {
 function f() {}
}
//   
ES 6はブロックレベルのスコープを導入し、ブロックレベルのスコープ内の宣言関数を明確に許可する.

// ES6    
'use strict';
if (true) {
 function f() {}
}
//    
ES 6は、ブロックレベルのスコープのうち、関数宣言文の挙動はletに似ており、ブロックレベルのスコープ以外では参照できないと規定している.

function f() { console.log('I am outside!'); }
(function () {
 if (false) {
  //         f
  function f() { console.log('I am inside!'); }
 }

 f();
}());
上のコードはES 5で動作します.if内で宣言された関数fが関数ヘッドにアップグレードされるため、実際に実行されているコードは以下の通りです.

// ES5  
function f() { console.log('I am outside!'); }
(function () {
 function f() { console.log('I am inside!'); }
 if (false) {
 }
 f();
}());
ES 6の運行結果は全く違っています.「I am outside!」を得ることができます.ブロックレベルのスコープ内宣言の関数はletに似ていますので、スコープ以外に影響はありません.実際に実行されるコードは以下の通りです.

// ES6  
function f() { console.log('I am outside!'); }
(function () {
 f();
}());
明らかに、このような行為の違いは古いコードに大きな影響を与えます.そのために発生した非互換性の問題を軽減するために、ES 6は付録Bに規定されています.ブラウザの実現は上記の規定を守らなくてもいいです.自分の行動方式があります.
ブロックレベルのスコープ内で関数を宣言することができます.関数宣言はvarに似ています.グローバルスコープまたは関数スコープの頭にアップグレードされます.また、関数宣言は、あるブロックレベルのスコープの頭にもアップグレードされます.上の3つの規則はES 6のブラウザだけに有効です.他の環境の実装は守られていませんか?それともブロックレベルのスコープの関数宣言をlとしてください.セット処理
前のコードはChrome環境下で実行されます.

// ES6      
function f() { console.log('I am outside!'); }
(function () {
 if (false) {
  //         f
  function f() { console.log('I am inside!'); }
 }

 f();
}());
// Uncaught TypeError: f is not a function
上のコードが間違っていたのは、実際に実行されているのは下のコードです.

// ES6      
function f() { console.log('I am outside!'); }
(function () {
 var f = undefined;
 if (false) {
  function f() { console.log('I am inside!'); }
 }

 f();
}());
// Uncaught TypeError: f is not a function
環境による挙動の違いを考慮して、ブロックレベルのスコープ内で関数を宣言することを避けるべきです.必要であれば、関数ステートメントの代わりに関数式を作成するべきです.

//       
{
 let a = 'secret';
 function f() {
  return a;
 }
}

//      
{
 let a = 'secret';
 let f = function () {
  return a;
 };
}
また、もう一つの注意点があります.ES 6のブロックレベルのスコープは、ステートメント関数のルールを許可しています.大きな括弧だけを使用して成立します.大きな括弧を使用していないと、エラーが発生します.

//    
'use strict';
if (true) {
 function f() {}
}

//   
'use strict';
if (true)
 function f() {}
ド式
本質的には、ブロックレベルのスコープは、ステートメントであり、複数の動作を一緒にカプセル化し、リターン値がない.

{
 let t = f();
 t = t * t + 1;
}
上のコードでは、ブロックレベルのスコープは2つのステートメントをカプセル化しています.しかし、ブロックレベルのスコープ以外では、tの値は得られません.なぜなら、ブロックレベルのスコープは戻りません.tがグローバル変数でない限り、値を返しません.
ブロックレベルのスコープを式に変えるという提案があります.つまり、値を返すことができます.ブロックレベルのスコープの前にdoを加えて、do表現にします.

let x = do {
 let t = f();
 t * t + 1;
};
上のコードでは、変数xはブロックレベルのスコープ全体の戻り値を得ます.
JavaScript ES 6のletとvarの比較
javascript 1.7にletのキーワードが追加されていると聞きましたが、それは宣言後、「ローカル変数」に似ています.しかし、キーワードvarとの具体的な違いはまだ分かりません.
回答:違いは作用域にあり、varキーワードの作用域は一番近い関数作用域である(関数体の外部であれば大域作用域である)、letキーワードの作用域は最も近いブロック作用域である(ブロックであれば例外なく大域作用域である).これは関数のスコープよりも小さいです.同様に、varのように、let宣言を使用する変数も、その宣言された場所の前に見られます.
以下はデモの例です.
グローバル(Global)
それらは関数体の外では平等である.

let me = 'go'; //globally scoped 
var i = 'able'; //globally scoped 
関数(Function)は、以下のように見えても、平等です.

function ingWithinEstablishedParameters() { 
  let terOfRecommendation = 'awesome worker!'; //function block scoped 
  var sityCheerleading = 'go!'; //function block scoped 
}; 
ブロック(Block)は異なる点であり、letはforサイクルの中だけで、varは関数全体で見られます.

function allyIlliterate() { 
  //tuce is *not* visible out here 
 
  for( let tuce = 0; tuce < 5; tuce++ ) { 
    //tuce is only visible in here (and in the for() parentheses) 
  }; 
 
  //tuce is *not* visible out here 
}; 
 
function byE40() { 
  //nish *is* visible out here 
 
  for( var nish = 0; nish < 5; nish++ ) { 
    //nish is visible to the whole function 
  }; 
 
  //nish *is* visible out here 
};