let と const コマンド
let コマンド
let
で宣言された変数は、宣言されたコードブロック内でのみ存在します。
{
let a = 10;
var b = 11;
}
console.log(a); //ReferenceError: a is not defined
console.log(b); //11
ご覧の通り、コードブロックの外からは let
で宣言された変数にアクセスすることはできません。つまり、let
で宣言された変数はブロックスコープに基づいています。これにより、いくつかの変数宣言の問題を回避できます。
var arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {}
console.log(i); //ReferenceError: a is not defined
上記のコードでは、for
ループの外からは変数 i
にアクセスできないことがわかります。
一方、以下のコードは var
で宣言されており、最終的に 10
を出力します。
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
しかし let
を使用すると 6
が出力されます。以前はこれを実現するために、即時実行関数が必要だったかもしれません。
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
さらに、let
は var
のように「巻き上げ(hoisting)」現象は発生しません。
function a(){
console.log(b) //ReferenceError
let b = 2;
}
function c(){
console.log(d); //undefined
var d = 2;
ブロックスコープ内では、let
で宣言された変数はその領域に固定され、外部の影響を受けません。
var temp = 123;
let temp1 = 222;
var temp2 = 555;
if (true) {
let temp = 333;
let temp1 = 444;
console.log(temp); //333
console.log(temp1); //444
console.log(temp2); //ReferenceError
let temp2 = 555;
}
let
は、一つのスコープ内で同じ変数を再宣言することを許可しません。
function(){
let a = 10;
var a = 1;
}
function(){
let a = 11;
let a = 212;
}
const コマンド
const
は let
と似ていますが、const
で宣言された変数は定数であり、一度宣言されると変更できません。
const a = 1;
a; //1
a = 3;
a; //1
const a = 12;
a; //1
注意すべき点として、const
コマンドは変数が存在するアドレスを指すだけなので、オブジェクトを定数として宣言する際には注意が必要です。
const foo = {};
foo.prop = 1;
foo.prop; //1
const a = [];
a.push("Hello"); // 可执行
a.length = 0; // 可执行
a = ["Dave"]; // 报错
上記のコードでは、定数
foo
はアドレスを格納しており、このアドレスはオブジェクトを指しています。不変なのはこのアドレスだけであり、foo
を別のアドレスに再割り当てすることはできません。しかし、オブジェクト自体は可変であるため、新しいプロパティを追加することは可能です。
つまり、オブジェクトや配列を定数として宣言した場合、それ自体を再割り当てすることはできませんが、メソッドを追加することは可能です。なぜなら、不変なのはそのアドレス自体だからです。
グローバルオブジェクトのプロパティ
ES6 の規定では、
var
コマンドとfunction
コマンドで宣言されたグローバル変数はグローバルオブジェクトのプロパティに属します。一方、let
コマンド、const
コマンド、class
コマンドで宣言されたグローバル変数はグローバルオブジェクトのプロパティには属しません。
var a = 1;
// 如果在node环境,可以写成global.a
// 或者采用通用方法,写成this.a
window.a; // 1
let b = 1;
window.b; // undefined
変数の分割代入
ES6 では、特定のパターンに従って配列やオブジェクトから値を取り出し、変数に代入することが許可されており、これを「分割代入(destructuring assignment)」と呼びます。この点は Python によく似ていると感じます。
配列の分割代入
var [a, b] = [1, 2];
console.log(a, b);
//1 2
let [c, d] = [3, 4];
console.log(c, d);
//3 4
実際には、これは「パターンマッチング」に属します。つまり、等号の両側のパターンが同じであれば、左側の変数には対応する値が割り当てられます。
var [a, b, [c, d]] = [1, 2, [3, 4]];
//a = 1,b = 2,c = 3,d = 4
var [aa, bb, cc] = [, , 3];
//aa = undefined,bb = undefined,cc = 3;
let [one, ...more] = [1, 2, 3, 4, 5, 6];
//one = 1,more = [2,3,4,5,6]
上記のコードからわかるように、構造が同じであれば、左側の対応する位置の変数には右側の対応する値が代入されます。対応する値がない場合は undefined
になります。
以下のケースは分割代入が失敗する例であり、いずれも TypeError
が発生し、変数の値は undefined
になります。
上記のいくつかのケースはすべて分割代入が失敗する例であり、
foo
の値はundefined
になります。これは、プリミティブ型の値が自動的にオブジェクトに変換されるためです(例えば、数値1
はnew Number(1)
に変換される)、その結果foo
がundefined
を取得することになります。
var [foo] = [];
var [foo] = 1;
var [foo] = false;
var [foo] = NaN;
var [bar, foo] = [1];
let [foo] = undefined;
let [foo] = null;
分割代入ではデフォルト値を指定できます。配列のメンバーが厳密に undefined
と等しい場合に、デフォルト値が有効になります。
var [foo = true] = [];
foo[(x, (y = "b"))] = // true
["a"][(x, (y = "b"))] = // x='a', y='b'
["a", undefined][(x, (y = "b"))] =
// x='a', y='b'
[1, null]; // x= 1,y=null
オブジェクトの分割代入
分割代入はオブジェクトにも同様に適用されます。配列との違いは、配列の要素の値はその位置によって決まるのに対し、オブジェクトのプロパティには順序がないため、正しい値を取得するには変数名がプロパティ名と同じである必要がある点です。
var { foo, bar } = { foo: "aaa", bar: "bbb" };
foo; // "aaa"
bar; // "bbb"
//注意两者区别
var { foo, bar } = { bar: "aaa", foo: "bbb" };
foo; // "bbb"
bar; // "aaa"
var { fos } = { foo: "aaa" };
fos; // undefined
上記のコードからわかるように、代入はプロパティ名のみに関係し、順序には関係ありません。プロパティ名が一致しない場合、値は undefined
になります。
変数名とプロパティ名が一致しない状況で代入を行う必要がある場合は、以下の方法を使用します。
var { foo: abs } = { foo: "sss" };
abs; // 'sss'
同様に、ネストされた構造を持つオブジェクトも分割代入できます。
let obj = {
p: ["Hello", { y: "World" }],
};
let {
p: [x, { y }],
} = obj;
console.log(x, y);
//Hello World
配列と同様に、オブジェクトの分割代入でもデフォルト値を指定できます。代入対象のオブジェクトに対応するプロパティが厳密に undefined
と等しい場合に、デフォルト値が適用されます。
var { a = 3 } = {};
a; //3
var { b = 4, y } = { y: 4 };
(b, y); //4,4
オブジェクトの分割代入は、私たちに大きな利便性をもたらします。
var obj = {
method1: function () {},
method2: function () {},
};
var { method1, method2 } = obj;
//以前的写法
var method1 = obj.method1;
var method2 = obj.method2;
文字列の分割代入
文字列も同様に分割代入が可能です。分割代入を行う際、文字列は配列ライクなオブジェクトに変換されます。
let [f, g, h] = "length";
console.log(f, g, h);
//l,e,n
let { length: len } = "length";
console.log(len);
//6
関数引数の分割代入
関数引数は配列ライクなオブジェクトであり、同様に分割代入が可能です。
function a([x, y]) {
return x + y;
}
add([1, 2]); //3
同様にデフォルト値を設定することもできます。
function move({x=0,y=0}={}){
"use strict";
console.log([x,y]);
}
move({x:3,y:4}); //[3,4]
move({x:3}); //[3,0]
move({}); //[0,0]
move(); //[0,0]
function remove({x,y}={x:0,y:0}){
"use strict";
console.log(x,y);
}
remove({x:1,y:2}); //1,2
remove({x,y}); //Hello World
remove({}); //undefined undefined
remove(); // 0 0
関数引数の分割代入方法は、引数の型に基づいて自動的に選択されます。配列であれば配列の分割代入、オブジェクトであればオブジェクトの分割代入が適用されます。
function fc([x,y,z]){
"use strict";
console.log(x,y,z);
}
fc([1,2,3]); //1,2,3
function nf({x,y,z}){
"use strict";
console.log(x,y,z);
}
nf({y:1,z:2,x:3}); //3,1,2
用途
JSON データの抽出
var jsonDate = {
id: 42,
status: "notOK",
data: [12, 32],
};
let { id, status, data } = jsonDate;
console.log(id, status, data); //42 'notOK' [12,32]
Map 構造のイテレーション
var map = new Map();
map.set("first", "HELLO");
map.set("second", "WORLD");
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is HELLO
// second is WORLD
for (let key of map) {
console.log(key);
}
//['first','HELLO']
//[ 'second', 'WORLD' ]
著作権
記事内のすべてのコードは以下から引用または派生しています:
阮一峰 - ECMAScript 6 入門, 本記事も同様に表示-非営利ライセンスに従います。
この記事は 2015年8月21日 に公開され、2015年8月21日 に最終更新されました。3698 日が経過しており、内容が古くなっている可能性があります。