ES6(ES2015)でグローバルに定義したlet,const,classはグローバルオブジェクトのプロパティにならない

## グローバル変数はグローバルオブジェクトのプロパティになる

グローバルオブジェクト(the global object)は、ブラウザであれば`window`、nodejsであれば`global`といった名前でデフォルトに定義されている。そして定義されたグローバル変数を自身のプロパティとして持つという特徴がある。


```js:グローバルオブジェクトのプロパティ一覧
"use strict";

var a;
function b(){};

//グローバルオブジェクトを取得するイディオム
console.log(Object.keys(Function(`return this`)()));
```

```js:Chrome_v46.0.2490.86(64-bit)
["external", "chrome", "document", "a", "b", ..., "self", "window"]
```

```js:Safari_v9.0(9537.86.1.56.2)
["window", "b", "a", "document", "screen", "history", "locationbar", "menubar", "personalbar", "scrollbars", …]
```

この場合、グローバル変数`a`,`b`は `window.a`/`window.b`と言った形で参照することができるのだ。

※なお、Nodejsでは一見グローバルなスコープに見える場所はスクリプト単位のローカルスコープなので、グローバルオブジェクト（`global`）のプロパティにはならない。

## let/const/class宣言の場合

ES6(ES2015)のlet,const,classは上記と異なり、仕様ではグローバルなスコープで定義してもグローバルオブジェクトのプロパティとはならない。しっかりとスコープが区切られ、外部に公開されていない。そのためグローバルオブジェクト経由で参照することもできない。

```js:let/const/classはプロパティとなるか？

"use strict";

var a;
function b(){};
//es2015>=
let c;
const d=0;
class e{};


console.log(Object.keys(Function(`return this`)()));
```


```js:ならない＠Chrome_v46.0.2490.86(64-bit)
["external", "chrome", "document", "a", "b", "speechSynthesis", "caches", "localStorage", "sessionStorage", "webkitStorageInfo", "indexedDB", "webkitIndexedDB", "crypto", "applicationCache", "performance", "styleMedia", "defaultstatus", "defaultStatus", "screenTop", "screenLeft", "clientInformation", "console", "devicePixelRatio", "outerHeight", "outerWidth", "screenY", "screenX", "pageYOffset", "scrollY", "pageXOffset", "scrollX", "innerHeight", "innerWidth", "screen", "navigator", "frameElement", "parent", "opener", "top", "length", "frames", "closed", "status", "toolbar", "statusbar", "scrollbars", "personalbar", "menubar", "locationbar", "history", "location", "name", "self", "window"]
```


参考:[Variables and scoping in ECMAScript 6](http://www.2ality.com/2015/02/es6-scoping.html)

なお、以下のような[クラス式](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/class)で表現する場合は、`var`で宣言されたグローバル変数に代入するので、変数名でプロパティ化される。

```js
var f = class {}
```

```js:Chrome_v46.0.2490.86(64-bit)
["external", "chrome", "document", "a", "b", "f", ...
```




## トランスパイラ/AltJSの問題

BabelやTypeScriptではES6のコードをES3/5に変換する際、`let`/`const`はただの`var`、`class`も`var`と`function`の組み合わせで表現される。そのため、グローバルに宣言すると`var`/`function`と同じく、グローバルオブジェクトのプロパティとして追加されてしまう。

```js:typescriptでes5に変換した例
"use strict";
var a;
function b() { }
;
//es2015>=
var c;
var d = 0;
var e = (function () {
    function e() {
    }
    return e;
})();
;
console.log(Object.keys(Function("return this")()));
```

```js:当然、c/d/eもプロパティになる
["external", "chrome", "document", "a", "b", "c", "d", "e", "speechSynthesis", "caches", "localStorage", "sessionStorage", "webkitStorageInfo", "indexedDB", "webkitIndexedDB", "crypto", "applicationCache", "performance", "styleMedia", "defaultstatus", "defaultStatus", "screenTop", "screenLeft", "clientInformation", "console", "devicePixelRatio", "outerHeight", "outerWidth", "screenY", "screenX", "pageYOffset", "scrollY", "pageXOffset", "scrollX", "innerHeight", "innerWidth", "screen", "navigator", "frameElement", "parent", "opener", "top", "length", "frames", "closed", "status", "toolbar", "statusbar", "scrollbars", "personalbar", "menubar", "locationbar", "history", "location", "name", "self", "window"]
```

この非互換性には注意する必要があるだろう。


## Safariの問題

現時点のSafari(ver.9)は[class宣言に対応している](http://kangax.github.io/compat-table/es6/#safari9)(let/constは未対応)が、classを定義するとグローバルオブジェクトのプロパティとして見えてしまう。

```js:let/constをのぞくと動く
"use strict";
var a;
function b() { }
class e {}
console.log(Object.keys(Function(`return this`)()));
```

```js:Safari_v9.0(9537.86.1.56.2)
["window", "b", "a", "document", "e", "screen", "history", "locationbar", "menubar", "personalbar", …] 
```

Safariのclassは[レキシカルスコープに未対応](http://kangax.github.io/compat-table/es6/#test-class_class_name_is_lexically_scoped)であり、それと同根の問題であると思われる。