情報アイランド

「情報を制する者は世界を制す」をモットーに様々な情報を提供することを目指すブログです。現在はプログラミング関連情報が多めですが、投資関連情報も取り扱っていきたいです。

ECMAScript 6の新機能(9)varとlet/const

var

ECMAScript 5では変数を宣言する際にvarを使用しました。

varにより宣言された変数のスコープはその変数を含む関数全体となり、巻き上げ(ホイスティング)が行われました(巻き上げとは変数がそのスコープの最も上で宣言されたものとして扱われることです)。

C:\work\node>node
> function f () {
... console.log(i);
... if (1 === 1) {
..... var i = 100;
..... }
... console.log(i);
... }
undefined
> f()
undefined
100
undefined

let

ECMAScript 6で変数を宣言する際にletを使用することができるようになりました。

letにより宣言された変数のスコープはその変数を含む関数全体ではなくその変数を含むブロックとなり、巻き上げも行われません。

C:\work\node>node
> function f () {
... console.log(i);
... let i = 100;
... console.log(i);
... }
undefined
> f()
ReferenceError: i is not defined
    at f (repl:2:13)
    at repl:1:1
    at REPLServer.defaultEval (repl.js:252:27)
    at bound (domain.js:287:14)
    at REPLServer.runBound [as eval] (domain.js:300:12)
    at REPLServer.<anonymous> (repl.js:417:12)
    at emitOne (events.js:82:20)
    at REPLServer.emit (events.js:169:7)
    at REPLServer.Interface._onLine (readline.js:210:10)
    at REPLServer.Interface._line (readline.js:549:8)
C:\work\node>node
> function f () {
... if (1 === 1) {
..... let i = 100;
..... }
... console.log(i);
... }
undefined
> f()
ReferenceError: i is not defined
    at f (repl:5:13)
    at repl:1:1
    at REPLServer.defaultEval (repl.js:252:27)
    at bound (domain.js:287:14)
    at REPLServer.runBound [as eval] (domain.js:300:12)
    at REPLServer.<anonymous> (repl.js:417:12)
    at emitOne (events.js:82:20)
    at REPLServer.emit (events.js:169:7)
    at REPLServer.Interface._onLine (readline.js:210:10)
    at REPLServer.Interface._line (readline.js:549:8)

なお、巻き上げが行われないということはコード上変数が宣言されるより上でその変数を使用できないという訳ではないことに注意してください。

たとえば、下のようなことは可能です。

C:\work\node>node
> function f () {
... let fin = function () {
..... console.log(i);
..... };
... let i = 100;
... fin();
... }
undefined
> f()
100
undefined

上では変数iを宣言する前に変数iを内部で使用する関数finを定義していますが、関数finを呼び出しているのは変数iを宣言した後であるためエラーとはなりません。

逆に、変数iを宣言する前に関数finを呼び出した場合にはエラーとなります。

C:\work\node>node
> function f () {
... let fin = function () {
..... console.log(i);
..... }
... fin();
... let i = 100;
... fin();
... }
undefined
> f()
ReferenceError: i is not defined
    at fin (repl:3:13)
    at f (repl:5:1)
    at repl:1:1
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)

const

ECMAScript 6で変数を宣言する際にconstも使用することができるようになりました。

constにより宣言された変数のスコープはletにより宣言されたものと同じくその変数を含む関数全体ではなくその変数を含むブロックとなり、巻き上げも行われません。

しかし、letの場合とは異なり、constにより宣言された変数はイミュータブル(不変)になります。

すなわち、一度変数の値を定義してしまうともう値の変更を行うことはできません。

そのため、constの場合、変数の宣言のみを行うことはできず、変数の宣言と同時に必ず定義も行わなければなりません。

C:\work\node>node
> const i
const i
SyntaxError: Missing initializer in const declaration
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)
> const i = 100
undefined
> i = 200
TypeError: Assignment to constant variable.
    at repl:1:3
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)
C:\work\node>node
> function f () {
... console.log(i);
... const i = 100;
... console.log(i);
... }
undefined
> f()
ReferenceError: i is not defined
    at f (repl:2:13)
    at repl:1:1
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
C:\work\node>node
> function f () {
function f () {
... if (1 === 1) {
..... const i = 100;
..... }
... console.log(i);
... }
undefined
> f()
ReferenceError: i is not defined
    at f (repl:5:13)
    at repl:1:1
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)

なお、varの場合と同様ですが、巻き上げが行われないということはコード上変数が宣言されるより上でその変数を使用できないという訳ではないことに注意してください。

また、constにより定義された変数は値の変更を行うことができないというだけで値の内容を変更することはできることに注意してください。

C:\work\node>node
> const o = {}
const o = {}
undefined
> o.i = 100
100
> o.i
100
> const a = [100, 101]
undefined
> a[0]
100
> a[0] = 1001
1001
> a[0]
1001

ループ

for系のループのループ変数をvarletconstにより宣言できるかです。

for文

for文の場合、varletは使用できますが、constは使用できません。

C:\work\node>node
> for (var i = 0; i < 10; i++) {
... console.log(i);
... }
0
1
2
3
4
5
6
7
8
9
undefined
> for (let i = 0; i < 10; i++) {
... console.log(i);
... }
0
1
2
3
4
5
6
7
8
9
undefined
> for (const i = 0; i < 10; i++) {
... console.log(i);
... }
0
TypeError: Assignment to constant variable.
    at repl:1:28
    at REPLServer.defaultEval (repl.js:272:27)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)

for-of文

for-of文の場合、varletconstの全てが使用できます。

C:\work\node>node
> for (var i of [0, 1, 2]) {
... console.log(i);
... }
0
1
2
undefined
> for (let i of [0, 1, 2]) {
... console.log(i);
... }
0
1
2
undefined
> for (const i of [0, 1, 2]) {
... console.log(i);
... }
0
1
2
undefined

for-in文

for-in文の場合、varletconstの全てが使用できます。

C:\work\node>node
> for (var i in [0, 1, 2]) {
... console.log(i);
... }
0
1
2
undefined
> for (let i in [0, 1, 2]) {
... console.log(i);
... }
0
1
2
undefined
> for (const i in [0, 1, 2]) {
... console.log(i);
... }
0
1
2
undefined

外側のループのループ変数を内側の関数で使用する場合

この場合、varletconstでは動作が変わります。

for-of文の場合で説明します。

空の配列を定義し、ループの度にその配列にループ変数を返す関数を格納し、その配列に格納した全ての関数を順番に呼び出し、返り値を標準出力に出力してみます。

var

varの場合、全てのループで2が表示されます。

C:\work\node>node
> function f () {
... const array = [];
... for (var i of [0, 1, 2]) {
..... array.push(function () {
....... return i;
....... });
..... }
... return array;
... }
undefined
> const fs = f()
undefined
> for (var fi of fs) {
... console.log(fi());
... }
2
2
2
undefined

これはvarにより宣言されたループ変数はループ毎に別々に存在しているのではなく全てのループで共通しているからです。

そのため、関数を呼び出した時点でのループ変数の値(この場合は2)が返るということです。

let

letの場合、ループに応じて012が表示されます。

C:\work\node>node
> function f () {
... const array = [];
... for (let i of [0, 1, 2]) {
..... array.push(function () {
....... return i;
....... });
..... }
... return array;
... }
undefined
> const fs = f()
undefined
> for (var fi of fs) {
... console.log(fi());
... }
0
1
2
undefined

これはletにより宣言されたループ変数はループ毎に別々に存在しているからです。

そのため、関数を呼び出した時点でのそれぞれのループ変数の値(この場合は012)が返るということです。

const

constの場合はletと同様の結果となります。

C:\work\node>node
> function f() {
function f() {
... const array = [];
... for (const i of [0, 1, 2]) {
..... array.push(function () {
....... return i;
....... });
..... }
... return array;
... }
undefined
> const fs = f()
undefined
> for (var fi of fs) {
... console.log(fi());
... }
0
1
2
undefined

最近は関数の中で別の関数を作成し、返り値として返すようなコードを書くことも多くなってきました。

そのような場合にはループ変数の宣言方法により動作が変わることに注意しなければなりません。

同名の変数

同じスコープで同名の変数を複数宣言した場合にどのような動作となるかです。

var

varで複数宣言した場合、何もエラーは発生せず、後で宣言した変数の値がその変数の値となります。

C:\work\node>node
> function f () {
... var i = 100;
... var i = 200;
... console.log(i);
... }
undefined
> f()
200
undefined

let

letで複数宣言した場合、エラーが発生します。

C:\work\node>node
> function f () {
... let i = 100;
... let i = 200;
SyntaxError: Identifier 'i' has already been declared
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)

const

constで複数宣言した場合、エラーが発生します。

C:\work\node>node
> function f () {
... const i = 100;
... const i = 200;
SyntaxError: Identifier 'i' has already been declared
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)

varとlet

varで宣言した後letで宣言した場合は勿論、逆の場合もエラーが発生します。

C:\work\node>node
> function f () {
... var i = 100;
... let i = 200;
SyntaxError: Identifier 'i' has already been declared
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)
C:\work\node>node
> function f () {
... let i = 100;
... var i = 200;
SyntaxError: Identifier 'i' has already been declared
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)

関数の引数と同名の場合

関数の引数と同名の変数をletconstで宣言した場合にもエラーとなります。

C:\work\node>node
> function f(arg) {
... let arg = 100;
SyntaxError: Identifier 'arg' has already been declared
    at Object.exports.createScript (vm.js:24:10)
    at REPLServer.defaultEval (repl.js:236:25)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)
    at REPLServer.<anonymous> (repl.js:441:10)
    at emitOne (events.js:101:20)
    at REPLServer.emit (events.js:188:7)
    at REPLServer.Interface._onLine (readline.js:219:10)
    at REPLServer.Interface._line (readline.js:561:8)
    at REPLServer.Interface._ttyWrite (readline.js:838:14)

グローバルスコープ

グローバルスコープで変数を宣言した場合にグローバル変数になるかです。

var

varの場合、グローバル変数となります。

C:\work\node>node
> global.i
undefined
> var i = 100
undefined
> global.i
100

let

letの場合、グローバル変数にはなりません。

C:\work\node>node
> global.i
undefined
> let i = 100
undefined
> global.i
undefined

const

constの場合、グローバル変数にはなりません。

C:\work\node>node
> global.i
undefined
> const i = 100
undefined
> global.i
undefined
pizyumi
プログラミング歴19年のベテランプログラマー。業務システム全般何でも作れます。現在はWeb系の技術を勉強中。
スポンサーリンク

-ecmascript 2015, ecmascript 6, Javascript