質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

10419閲覧

ES6からグローバル汚染を避けるために、すべて即時関数の中に記載しなくてよくなったと聞きますが、疑問点があります。

退会済みユーザー

退会済みユーザー

総合スコア0

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

1グッド

7クリップ

投稿2017/03/09 12:28

編集2017/03/09 13:19

ES6からグローバル汚染を避けるために、すべて即時関数の中に記載しなくてよくなったと聞きました。

ただの{}ですべて処理のグループをとりあえず囲めばブロックスコープになるのでこれにvar以外を使えばそれでよいのでしょうか?
初心者にはただの{}に式が詰まっているのは文法エラーのように見えるのですが、ただの{}というのは問題ないのでしょうか?

またトランスパイルする場合は、このやり方だと、letがvarに代わってしまうので、相変わらず即時関数か、無名関数で囲むしかないという認識でよいでしょうか?
また、一般的に正しい書き方は、トランスパイルする場合は、
まず全体を即時関数で囲み、その後各グループをさらに即時関数で囲んでいくというやり方でよいでしょうか?
即実行したくないものは、ここに入れてはいけないのかという疑問も初心者には出てくるのですが、そもそも即実行しないものなんて実際はないですかね?

・JS本格入門より
>>>
即時命令は使わない
前項で触れた即時関数は、ES2015の環境では要らなくなります。以下のように、該当するコードをブロックでくくり、配下の変数をlet命令で宣言すれば、即時関数と同じ効果を得られるからです。
◉リスト4-20 let_block.js
{
let i = 5;
console.log(i); // 結果:5
}

console.log(i); // 変数iはスコープ外なのでエラー
リスト4-18と比べると、ぐんとコードがシンプルになっていると思います。

guest1213👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kei344

2017/03/09 13:23 編集

引用部はわかるように「> 」を行頭に付けてください。(追記:)半角>と半角スペースを行頭にです。PCからの投稿であれば入力部分のテキストエリアの上にツールバーがあるので、それでも出来ます。
guest

回答1

0

ベストアンサー

疑問の通り、Babelを使用してES5にトランスパイルした場合は、変数の隔離は正常に行われません。本に書いてある方法では問題が発生する場合があります

実例を見てみましょう。下記のような二つのカウンタがあり、それぞれの処理するためのJavaScriptが別々だったとします。

test.html

HTML

1<!DOCTYPE html> 2<html> 3 <head> 4 <meta charset="utf-8" /> 5 <title>カウンタ</title> 6 </head> 7 <body> 8 <h1>カウンタ</h1> 9 <p> 10 A: 11 <button id="a-up">上げる</button> 12 <input id="a-counter" type="number" value="0"> 13 <script src="a.js"></script> 14 </p> 15 <p> 16 B: 17 <button id="b-up">上げる</button> 18 <input id="b-counter" type="number" value="0"> 19 <script src="b.js"></script> 20 </p> 21 </body> 22</html>

a.es

ES6

1"use strict"; 2{ 3 const aCounter = document.getElementById("a-counter"); 4 const aUpButton = document.getElementById("a-up"); 5 let count = parseInt(aCounter.value); 6 aUpButton.addEventListener("click", function() { 7 count++; 8 aCounter.value = count; 9 }); 10}

b.es

ES6

1"use strict"; 2{ 3 const bCounter = document.getElementById("b-counter"); 4 const bUpButton = document.getElementById("b-up"); 5 let count = parseInt(bCounter.value); 6 bUpButton.addEventListener("click", function() { 7 count++; 8 bCounter.value = count; 9 }); 10}

ES6がわからないブラウザのためにBabelで変換(babel --presets=latest a.esとかで)するとこうなります。

a.js

JavaScript

1"use strict"; 2 3{ 4 var aCounter = document.getElementById("a-counter"); 5 var aUpButton = document.getElementById("a-up"); 6 var count = parseInt(aCounter.value); 7 aUpButton.addEventListener("click", function () { 8 count++; 9 aCounter.value = count; 10 }); 11}

b.js

JavaScript

1"use strict"; 2 3{ 4 var bCounter = document.getElementById("b-counter"); 5 var bUpButton = document.getElementById("b-up"); 6 var count = parseInt(bCounter.value); 7 bUpButton.addEventListener("click", function () { 8 count++; 9 bCounter.value = count; 10 }); 11}

実際動かしてみればわかりますが、AとBのカウンタの動きが互いに副作用を起こし、独立して動作しません。なぜなら、両方ともグローバルスコープにcountという変数があり、同じ物を使ってしまうからです。

Babelで変換せずにES6としてそのまま読み込んだ場合はうまくいきます。ではBabelのバグなのかというと、バグと言うよりも限界です。Babelは、letやconstのブロックスコープは事前に知っている変数と重複していれば、変数名を変えるなどでブロックスコープをエミュレートできるように処理できます。しかし、独立して存在する別のJavaScriptで変数名が被っているかどうかはわからないため、そのような処理ができません。今回のように別々に存在する場合は、互いにletで宣言した変数とかは知らないので、単純なvarに置き換えてしまいます。

そもそも、その本に書いている方法では関数宣言が対応できません。ES5のときと同じ問題が起きるでしょう。

では、どうするのが良いのかというとモジュールベースで作成するというのがES6らしい方法かと思います。

JavaScript

1"use strict"; 2import './a.es'; 3import './b.es';

これをbrowserifyやwebpackとBabelを組み合わせて変換します。そして、これだけをブラウザで読み込むのです。

import/export文はES6に含まれますが、モジュールファイルをどう探すのか等はまだ策定されていません。しかし、先行してBabelなどではCommonJS風require()などに変換してくれるので、それらをまとめられるbrawserifyやwebpackを使えばES5向けのJavaScriptにすることができます。これらモジュールベースでは、完全に名前空間がわかれているため、varを使おうがブロックがなかろうが問題ありません。唯一互いに副作用を及ぼすのは、グローバルオブジェクト(ブラウザならwindowのこと)のプロパティとして扱う場合(いわゆるグローバル変数)だけです。

何かよくわからないような気もしますが、import/exportを使ったモジュールベースなら、モジュールごとに名前空間がわかれるので、無名関数で囲まなくてもいい、とだけまずは覚えておけばいいかと思います。


【補足】

Rails等で使われるsprocketsには同じ問題があります。元々はCoffeeScriptを使う文化(CoffeScriptはデフォルトで変換時に無名関数で囲む)だったので誰も気にしなかったのかも知れません(Rails 5.1からそこら辺は変わってきています)。

某所で「無名関数で囲むな」と私は書いていますが、それはモジュールベースで作ることが前提の話です。この部分の解説を書かなきゃ…。

投稿2017/03/09 22:09

raccy

総合スコア21737

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/03/10 00:34

難しいですね。 cssのimportと同じように下記は、ES6では{}で全体を囲んでおけば済む話を、{}で囲まずすべて、別ファイルにして、 最後に一つのファイルにまとめてしまえば、babelか何かがが勝手にカプセル化してくれるのでしょうか? "use strict"; import './a.es'; import './b.es'; >>> 何かよくわからないような気もしますが、import/exportを使ったモジュールベースなら、モジュールごとに名前空間がわかれるので、無名関数で囲まなくてもいい、とだけまずは覚えておけばいいかと思います。 難しくて内容はわからないので、上記の認識でよろしいでしょうか? それかブラウザが完全にES6をサポートするまで即時関数でとりあえず囲んでおくかですね。 もう少しでしょうから
raccy

2017/03/10 10:16 編集

> cssのimportと同じように ES6のimportはCSSの@importとは動作が全く異なります。同じではありません。 > ES6では{}で全体を囲んでおけば済む話を、 {}で囲んでもグローバル汚染の対策になっていません。回答の前半はその説明です。 > 最後に一つのファイルにまとめてしまえば、 一つにまとめるといっても、単純に結合することではありません。importのような仕組みを使ってまとめた場合だけ有効です。 > babelか何かがが勝手にカプセル化してくれるのでしょうか? browserifyやwebpackが無名関数で囲った形に変換します。実際どうなるのかは自分で試して見てください。 > 上記の認識でよろしいでしょうか? そうです。 > それかブラウザが完全にES6をサポートするまで即時関数でとりあえず囲んでおくかですね。 IE対応を捨てない限り、ES6をそのまま使える日は来ません。
退会済みユーザー

退会済みユーザー

2017/03/10 12:23

>>> ES6では{}で全体を囲んでおけば済む話を、 ES6でbabelでトランスパイルをしなければという意味です。 >>> 一つにまとめるといっても、単純に結合することではありません。importのような仕組みを使ってまとめた場合だけ有効です。 gulpで一つにまとめているんですが、それではだめでimportという特別な方法でないとだめなのですね。 >>> IE対応を捨てない限り、ES6をそのまま使える日は来ません。 23年まではbabelでES5にしないといけないのか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問