Help us understand the problem. What is going on with this article?

老害と言われないためのES6勉強会報告・イントロダクション編

More than 3 years have passed since last update.

老害と言われないためのES6勉強会報告・イントロダクション編

by esehara@github
1 / 24

はじめに

だいたい昔からJavaScriptを触ってたりすると、ECMA-262 Edition 5.1に慣れきってしまって、もうそれでいいじゃんという雰囲気になったりする。しかし、既に5.1だけを使うのは老害なのではないか(はっきりとは断言しない)という危機感によって、今回はECMAScript 2015を勉強しようという話になった。


なぜ5.1は老害なのか

01.png

まず最初にBabelによって、ECMA Script2015を無理矢理5.1にすることが可能になった。その後、段々と対応ブラウザが増えつつあり、今後おきかわる可能性がある。これはプロダクトの都合。プログラマーとしては格段に書きやすくなった。


参考資料

book.jpg

2016年11月に出版されたので、とにかくECMAScript 2015の対応は早かった。本の内容も、めくってみた感じだと、それほど悪くない印象がある。


参考資料2

はてな教科書 JavaScript 編

無料で見れる研修テキスト。はてななので、それほど間違っていないことを期待したい。


当然、全てを網羅できるわけではないので、主要トピックだけを追いかけていく。今回は、関数やクラス以外の部分について勉強する。


地道に代入からやっていく(1)

分割代入ができるように!!

var [a, b] = [1, 2];

※ただし、間違えてこう書いちゃうとダウト

var a, b = [1, 2]

この場合だとbだけに代入されるので困ったことになる。この機能は、スワップもしてくれるので楽。

var [a, b] = [1, 2];
var [a, b] = [b, a];
console.log(a, b);

地道に代入からやっていく(2)

オブジェクトも分割代入できる

let {firstname, lastname} = {firstname: "esehara", lastname: "shigeo"}

こういう代入は、ライブラリを実際に使っていくときにでくわしたりすることがある。


地道にletからやっていく

だいたいの変数宣言はvarからletに起きかえることが可能。

理由としては、殆どの変数宣言はスコープ内で使われるものでしかなく、letはスコープから脱出すると、その変数が解放される。以下は「はてな」の教科書から取ってきた実例。

if (true) { let x = 100; }
console.log(x);
// ReferenceError: x is not defined

余程スコープを横断したいときにはvarを使うと便利ではあるんだが……

あと地味に重要な点としては、letを使うと、同じ変数が宣言できなくなる。このことによって、宣言しようとした変数をかぶらせない、といった配慮が可能になるのだが、そもそも二重に宣言してしまいそうになるコードを書くほうが悪いということになる。


地道にconstからやっていく

constは見た目の通り、定数を宣言するときに使う。この場合の定数とは「再代入不可能」ということである。ただし、その変数に対して再代入を禁止するという意味であって、代入された値の不変性を保証するものではない。ここは勘違いされやすいが、再代入が不可能であるということは、必ずしもImmutableであることを意味してはいない。

const hoge = [1, 2, 3];
hoge[0] = "foobar";
console.log(hoge);

通常、この手の配列はバグを生みやすい。配列は地獄だ。


配列あるある

x = [1, 2, 3];
y = x;
y[0] = 9;
console.log(x);

詳しい解説は値渡しと参照渡しについてあらためてまとめてみるを読みましょう。


その他の罠

ユニコードにもサロゲートペア文字というのがある。いわゆるUnicodeの文字列が不足するに従って利用するバイト数を増やした結果、日本語の表示上では2文字であるものが、3文字として認識されてしまうような状態のことである。その一例として:

console.log('😇サロゲートペア文字最悪っぽい'.match(/^.サロゲートペア/g));

とするとnullになる。それは当然絵文字の部分が2文字になっているせいだからである。当然、これを解決しないとまずいわけで、ECMAScript2015では:

console.log('😇サロゲートペア文字最悪っぽい'.match(/^.サロゲートペア/gu));

gというオプションを付けられるところにuを付けることで回避することができるようになった。


地味に便利になったもの

  • 2進法リテラル、8進法リテラル(正直あったんだね……という)
  • テンプレート文字列

テンプレート文字列

以前だったら

let greeting = "Hello, " + name + " !";

+がうっとおしかったのが:

let greeting = `Hello, ${name} !
How are you?`;

「`」を使うことによって便利になりました。複数行も書けるので便利〜


for .. of .. 構文

for(value of [1, 2, 3]) { console.log(value) };

特に言うことはなし。ただしArrayのメソッドであるforEachメソッドと若干被っているようにも見える。

また、実際に使えない例もある。ハマった例としては、getElementsByIdなどのメソッドでDocumentを取得するさいに、forEachや、for .. of ..は使えない。おそらくイテレーターのインターフェイスが存在していないからだろう。

参考資料


スプレット演算子

イテレーターの話題が出てきたので、ついでにスプレット演算子。こいつはArrayの中でイテレーターを展開してくれるという超便利演算子。

let a = [1, 2, 3];
let b = [...a, 4];
console.log(b);

このように、イテレーターが代入された変数の前に「...」を付けることによって、展開が可能になる。ただし、こいつが使えるようになるのは配列の中のようで、Unxepected Tokenになる。


Advanced Topic

とすると、オブジェクトでも同様の展開式が欲しいという気持ちになることが多いかもしれないが、現状、オブジェクトに対しての展開式がES2015の仕様として策定さていることはない。しかし、例えばRedux.jsの例からひっぱって来ると:

// ...

    switch (action.type) {
      case requestType:
        return {
          ...state,
          isFetching: true
        }
      case successType:
        return {
          ...state,
          isFetching: false,
          ids: union(state.ids, action.response.result),
          nextPageUrl: action.response.nextPageUrl,
          pageCount: state.pageCount + 1
        }
      case failureType:
        return {
          ...state,
          isFetching: false
        }
      default:
        return state
    }
// ...

謎の{...}みたいな記法が使われているのがわかる。これは公式の解説の通り、提案の第二ステージくらいでまだ採用には遠いので、Babelの拡張プラグイン使いましょうということがさらっと書かれている。とりあえずオブジェクト展開は現状できなくて、先走っている拡張であるということは覚えておいて損は無いかと。


Symbol

いわゆるユニークなオブジェクトを返す。ユニークなので、これで生成されたオブジェクト同士を比較してもtrueにはならない。使い方としては、マジックナンバーを避ける定数の例が一般的。ただ、あまり必要性もないのではないかとも言われている不憫な子。


Map

一般的にJavaScriptでは、オブジェクトリテラルで連想配列を代用していたため、下のようなことが起こる:

let my = {name: "esehara"};
console.log(my.name);
console.log(my["name"]);

そのため、いわゆるメソッドと連想配列的なアクセスがごっちゃになって、データ構造を作るときに面倒になる。そこで、Mapを使うことで、キーと値が対応するような、一般的な「連想配列」を作ることが可能になった。Mapの初期化はちょっと面倒で、次のように使うことになる:

let name = new Map([["name", "esehara"]])
console.log(name.get("name"))

このことによって、アクセスの方法が一意になるため、便利。また、いわゆるキーリストなどの習得も楽なので、データの使いかたによっては、こっちのほうが便利である。


Set

Setという名前から察することができるように、「擬似的な集合」を作ることができる。普通、集合は要素の重複しないようにする。


特別編: Object


is(v1, v2)というメソッドが追加された

オブジェクト同士が同一であるかどうかを判定する。例えば、値自体は一緒だが、オブジェクトが違う例として、次のような例が挙げられる:

Object.is([1], [1]);

この場合、オブジェクトが違うのでfalseになるのだけれども、しかし次の場合はtrueになる。

let a = [1];
let b = a;
Object.is(a, b);

これによって、オブジェクト同士が本当に同じものなのかを確かめることが可能になった。


assign(target, source)というメソッドが追加された

Object.assign(target, source...)みたいな形式で書くことが可能。これはオブジェクト同士をマージすることができる。

Object.assign({a: 1, b: 2}, {b: "foo", c: "bar"})

このとき、二つのオブジェクトが合体したオブジェクトが返ってくるわけども、最初の引数に渡されたオブジェクトも同様に変更されるようになるので、もし最初の引数に対して副作用を発生させたくなければ、空のオブジェクトを渡すとよい。


続きはこちら

esehara@github
本物のプログラマーではないほうを担当しています
http://bugrammer.hateblo.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away