変数と宣言 - var、let、constとグローバルオブジェクト
《 初回公開:2022/08/21 , 最終更新:2022/11/17 》
JavaScriptの変数宣言にはvar、let、constとかあって。
letとconstは、ECMAScript2015(ES6)から採用された新しい宣言。
【 目次 】
var 文は関数スコープまたはグローバルスコープの変数を宣言し、任意でそれをある値に初期化します。
let 文はブロックスコープのローカル変数を宣言します。
...
スコープのルール
let で定義された変数は、自身が定義されたブロックと、そこに含まれるサブブロックがスコープになります。
この点において let のふるまいは var にとてもよく似ています。
大きな違いは、var で定義された変数のスコープはそれを含んでいる関数全体になるということです。
定数は、let キーワードを使って定義する変数と同様にブロックスコープを持ちます。
定数の値は、再代入による変更ができず、再宣言もできません。グローバルな定数は var 変数とは異なり、window オブジェクトのプロパティにはなりません。
定数は大文字または小文字で宣言することができますが、すべて大文字で宣言するのが慣例です。
以下のコードは各変数のスコープにおけるvar宣言とlet宣言のふるまいの違いを示している。
var x = "global";
function test() {
var x = "関数内の変数";
{
var x = "ブロック内の変数";
}
console.log(x); // ブロック内の変数
}
test();
console.log(x); // global
上記のコードのvar宣言をlet宣言に置き換えると
let x = "global";
function test() {
let x = "関数内の変数";
{
let x = "ブロック内の変数";
}
console.log(x); // 関数内の変数
}
test();
console.log(x); // global
以下のような変数のスコープがあって
- グローバルスコープ
- どこでも有効
- 関数スコープ
- 関数内でのみ有効(関数内のブロックでも有効)
- ブロックスコープ
- ブロック内のみ有効
ブロックというのは {...}の内部を指していて
定数(const)宣言もlet宣言と同じくブロックスコープ。
ブロック内で再宣言した変数はvarの場合は関数スコープであるのに対してletの場合はブロックスコープになっている。
グローバルエリア(グローバル実行コンテキスト)で宣言された変数(varもletもconstも)はグローバルスコープとなる。
関数内で変数を宣言すると関数スコープとなり、
ブロック内で変数を宣言するとブロックスコープとなる。
但し、var変数の場合は特別で関数内で宣言してもブロック内で宣言しても関数スコープとなり、同一スコープ内で何回でも再宣言が可能。
さらにvar変数は変数の巻き上げが起きる。
if文の波カッコ内{}
もブロックスコープ。
const C = "const"
let l = "let"
var v = "var"
if (true) {
const C = "const_block"
let l = "let_block"
var v = "var_block"
}
console.log(C); // const
console.log(l); // let
console.log(v); // var_block
宣言されているが値が代入されていない値はundefinedになる。
未宣言の値を使うとerrorが発生する。
let l;
var v;
console.log(l); // undefined
console.log(v); // undefined
console.log(x); // error Uncaught ReferenceError: x is not defined
varは(同一スコープの中で)再宣言可能だがletは再宣言するとerrorになる。
var x = 'x';
let y = 'y';
var x=10;
let y=20; // Uncaught SyntaxError: Identifier 'y' has already been declared
未宣言のグローバル変数とstrictモード
また、x = 42 のように、単純に値を変数に代入することもできます。この形は、未宣言のグローバル変数を生成します。
strict モードの JavaScript では警告が発生します。未宣言のグローバル変数は、よく予期しない動作を引き起こします。
したがって、宣言されていないグローバル変数を使用することはお勧めしません。
JavaScriptでは未宣言で変数を使う事ができて、未宣言の変数はグローバルな変数となる。
function func(){
x="未宣言の変数x";
}
func();
console.log(x);
未宣言の変数とvar変数の使い分けは。
微妙な振る舞いの違いはあるが、未宣言の変数は宣言を省略したvar変数という事かな。
strictモード
strictモード ”use strict”宣言を指定する事で、未宣言の変数の使用を禁止したり,プログラムの誤りがみつけやすくなる。
- “use strict”(厳格モード)を使うべきか?|もっこりJavaScript|ANALOGIC(アナロジック)
strictモードでは、より的確なエラーチェックが行われるため、これまでエラーにならなかったような曖昧な実装がエラー扱いになります。
このことにより、コード内に存在する潜在的な問題を早期に発見しやすくなります。
また、JavaScriptエンジンによる最適化処理を困難にする誤りを修正するため、strictモードのコードは非strictモードの同一コードよりも高速に実行することができる場合があるなどのメリットがあります。 - 厳格モード - JavaScript | MDN
- 厳格モードへの移行 - JavaScript | MDN
strictモードでは未宣言の変数はエラーになる。
'use strict';
u = 'undefined_v'; // Uncaught ReferenceError: u is not defined
グローバルオブジェクト
- Global object (グローバルオブジェクト) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN
JavaScript では、グローバルオブジェクトが常に定義されています。
ウェブブラウザー上でスクリプトがグローバル変数を生成する時、グローバルオブジェクトのメンバーとして作成されます。 (Node.js ではこの限りではありません。)ウェブブラウザーでは、明示的にバックグランドタスクとして起動されるコードを除き、 Window がグローバルオブジェクトになります。
ウェブにおける JavaScript コードのほとんどはこのケースに該当します。
Worker 内で実行されるコードでは WorkerGlobalScope オブジェクトがグローバルオブジェクトになります。
Node.js で実行されるスクリプトの場合、 global と呼ばれるオブジェクトがグローバルオブジェクトになります。
未宣言の変数やグローバル実行コンテキスト (すべての関数の外側) で宣言されたvar変数はグローバルスコープの変数でこれはグローバルオブジェクトのプロパティとなる。
ウェブブラウザーで動作するいわゆるクライアントサイドJavaScriptでは基本的にはWindow がグローバルオブジェクト。
ウェブブラウザーで動作するいわゆるクライアントサイドJavaScriptのコード
var x = 'var_x';
y = '未宣言の変数y';
console.log(window.x); // var_x
console.log(window.y); // 未宣言の変数
Node.jsではglobalと呼ばれるオブジェクトがグローバルオブジェクト。
{"title": "{"title": "ウェブブラウザーで動作するいわゆるクライアントサイドJavaScriptのコード"}
のコード"}
var x = 'var_x';
y = '未宣言の変数y';
console.log(global.x); // var_x
console.log(global.y); // 未宣言の変数
WebページのJavaScriptではwindowオブジェクト,Node.jsではglobalオブジェクトによってグローバルオブジェクトにアクセス可能であるがGAS(Gooogle Apps Script)ではこれらは使えない。
this変数
this変数はその実行コンテキストによって指す値が異なる。
グローバル実行コンテキストでのthis
グローバル実行コンテキスト (すべての関数の外側) では、strict モードであるかどうかにかかわらず、this はグローバルオブジェクトを参照します。
関数コンテキストでのthis
関数内での this の値は、関数の呼び出し方によって異なります。
下記のコードは strict モードではないため、また呼び出し時に this の値が設定されないため、this は既定でグローバルオブジェクトとなり、これはブラウザーでは window です。
オブジェクトのメソッド内のthis
関数がオブジェクトのメソッドとして呼び出されるとき、その this にはメソッドが呼び出されたオブジェクトが設定されます。
globalThis
globalThis はグローバルプロパティで、グローバルオブジェクトと同等であるグローバルな this が格納されています。
globalThisを使えばWebページやNode.jsそしてGASでもグローバルオブジェクトにアクセス可能になる。
以下のコードはthis変数とグローバルオブジェクトのふるまいを示している。
var x = 'global_x';
function f(){
let x="local_x";
console.log(this.x); // obj.x
console.log(globalThis.x); // obj.x
}
f();
obj={
x: "obj.x",
f: function(){
console.log(this.x); // obj.x
console.log(globalThis.x); // global_x
}
};
obj.f();
変数・関数の巻き上げ - ホイスティング
変数の巻き上げと言って宣言される前の変数を参照できてその時その変数の値はundefinedとなる。
console.log(v); // undefined
var v="v";
その変数のスコープ内で宣言されたvar変数は、 その変数のスコープの先頭で宣言されたものとみなされる。
つまり、上記の例は以下のコードと等価。
var v;
console.log(v); // undefined
var v="v";
しかし、変数の巻き上げが起こるのはvar変数だけのようで、let変数や未宣言の変数ではエラーとなる。
console.log(l); // Uncaught ReferenceError: Cannot access 'l' before initialization
console.log(x); // Uncaught ReferenceError: x is not defined
let l="l";
x="x";
関数の巻き上げというのもあって
複数の変数をまとめて宣言
C言語のように、複数の変数をカンマでくぎってまとめて宣言する事ができる。
const c1="c1",c2="c2";
let l1="l1",l2="l2",l3;
var v1="v1",v2="v2",v3;
console.log(c1,c2);
console.log(l1,l2,l3);
console.log(v1,v2,v3);
c1 c2
v1 v2 undefined
l1 l2 undefined
いろいろなケースでの変数の振る舞い
変数の振る舞いがいろいろな場合でどのように動作するのか検証してみた。
グローバル実行コンテキストで宣言した変数や未宣言の変数はグローバル変数になる。
つまり、関数内やオブジェクトのメソッド内でアクセス可能。
const C = "const"
let l ="let"
var v ="var"
u = 'undefined_v';
function f() {
console.log(C); // const
console.log(l); // let
console.log(v); // var
console.log(u); // undefined_v
}
f();
obj={
x: "obj.x",
f: function(){
console.log(C); // const
console.log(l); // let
console.log(v); // var
console.log(u); // undefined_v
}
};
obj.f();
constを除いて関数内やメソッド内で内容を変更すれは、その値はグローバルに反映される。
const C = "const"
let l = "let"
var v = "var"
u = 'undefined_v';
function f() {
//C = C + "_change"; // Uncaught TypeError: Assignment to constant variable.
l = l + "_change";
v = v + "_change";
u = u + "_change";
}
f();
console.log(l); // let_change
console.log(v); // var_change
console.log(u); // undefined_v_change
obj = {
x: "obj.x",
f: function () {
//C = C + "_obj"; // Uncaught TypeError: Assignment to constant variable.
l = l + "_obj";
v = v + "_obj";
u = u + "_obj";
}
};
obj.f();
console.log(l); // let_change_obj
console.log(v); // var_change_obj
console.log(u); // undefined_v_change_obj
関数内ではグローバル変数と同名の変数を定義するとそれはローカル変数となり、
constとletの場合は宣言の前で変数を使うとエラーとなるが、
varの場合は変数の巻き上げが起こってエラーとはならず値はundefinedとなる。
関数内での変数の値はローカルの値となり、グローバルな変数の値は隠される。
関数の実行後もグローバル変数の値は変わらない。
const C = "const"
let l = "let"
var v = "var"
function f() {
//console.log(C); // Uncaught ReferenceError: Cannot access 'c' before initialization
//console.log(l); // Uncaught ReferenceError: Cannot access 'l' before initialization
console.log(v); // undefined
const C ="local_const"
let l = "local_let"
var v = "local_var"
}
f();
console.log(C); // const
console.log(l); // let
console.log(v); // var
関数内ではグローバルなvar変数や未宣言の変数はグローバルオブジェクトのプロパティとなりthisやglobalThis変数を使ってアクセスできる。
しかし、constやlet変数はグローバルオブジェクトのプロパティとはならず参照できない。
const C = "const";
let l = "let"
var v = "var"
u = 'undefined_u';
function f() {
const C = "local_const";
let l = "local_let"
var v = "local_var"
console.log(this.C); // undefined
console.log(this.l); // undefined
console.log(this.v); // var
console.log(this.u); // undefined_u
console.log(globalThis.C); // undefined
console.log(globalThis.l); // undefined
console.log(globalThis.v); // var
console.log(globalThis.u); // undefined_u
}
f();
オブジェクトのメソッド内ではthisはオブジェクトを指していて、グローバルオブジェクトにはglobalThisやwindow等の変数でアクセスすることになる。
var v = "var"
u = 'undefined_v';
obj={
x: "obj.x",
f: function(){
console.log(this.x); // obj.x
console.log(globalThis.v); // var
console.log(globalThis.u); // undefined_v
}
};
obj.f();
指針(私信)
バグをつくらないために。
未宣言のグローバル変数は使わない。(”use strict”宣言を指定する。)
var変数はlet変数にできるだけ置き換える。
グローバルオブジェクトはglobalThis変数を使う。(windowやthisではなく)