※スマホ対応はしてません。

タグ: es2015

ES6とES2015に違いはないです。(現代的JavaScriptおれおれアドベントカレンダー2017 – 24日目)

カテゴリー: JavaScript

現代的JavaScriptおれおれアドベントカレンダー2017 – 24日目

昔はES5とかES6とか言ってたはずなのに気付いたらES2015とか呼ばれるようになってて、なんかマリオが64になって急に数字が増えたなあみたいなのに近い感覚だったんですが、そんなことありませんでしたかそうですか。

概要

  • ES6とES2015は同じもの
  • わかりやすいからES2015と呼ぼうぜ
  • ES2015以降は毎年更新
  • 正式名称は “ECMA-262 edition 6” とかそういうの

ES vs JS

JavaScript (JS) はプログラミング言語ですが、その大本の仕様となるのがECMAScript (ES) です。JSはESの一種であり、ESの仕様で定められたものはJSで動きます。(あるいは、動くことが期待される。)

JSは各ブラウザが環境を用意していますが、ESの方はEcma Internationalという団体の中のTC39という委員会が仕様策定を行っています。

ベンダーはESの仕様通りにJSの動作を実装することになります。ES側は、逆にベンダーの提案と先行実装から仕様をまとめてる感じです。(たぶん。ここらへん怪しい。) 二つ以上の実装が存在するまで正式な仕様にはなりません。

ECMAScriptの仕様

ESも、具体的には “ECMA-262” という名前の仕様です。

Ecma International

色々と仕様策定を行ってる団体みたいです。ES以外にもC#とか。

E-C-M-A

前はECMA = European Computer Manufacturers Association(欧州電子計算機工業会)という団体でしたが、なんか国際化した現状に合わせて1994年に名前を変えたそうです。Windows 95より前だ。

なので “ecma” という英単語が存在するわけではないですが、かといって現在は略語というわけでもありません。

ES6 vs ES2015

この二者は同じものを指します。一般には後者の呼び方が推奨されます。

ES6

本来はESの第6版のことです。仕様の正式名称としては “ECMA-262” で、その “Edition 6” と。 class やアロー関数 => 等、「いまどき」の機能が多数追加されました。

前身のES5が2009年の発表で、2011年のES5.1を挟んで久しぶりに、かつ大きく更新されました。その発表年が、2015年です。

ES2015

というわけで、ES6こと “ECMA-262 Edition 6” のことです。

ウェブ界隈の進みが早いのでもっと細かく、毎年仕様を策定していこうという話になりました。それなら連番はわかりづらいよね、と。(ES7, ES8, ES9, ES10, …) そこで連番よりも発行年で、ES6ならES2015と呼ぶことが推奨されるようになりました。

ES2016, ES2017

それぞれ公開されています。

ES2015に比べて影が薄いけど、都度新機能なりが追加されます。

年で呼ぶことを推奨

ESの5、5.1、6版の仕様策定を引っ張ったAllen Wirfs-Brock氏のブログから。

So, why the year-based designation? The 6th edition of ECMA-262 took a long time to develop, arguably 15 years. As ES6 was approaching publication, TC39 (the Technical Committee within Ecma International that develops the ECMAScript specifications) already knew that it wanted to change its process in a way that enabled yearly maintenance updates. That meant a new edition of ECMA-262 every year with a new edition number. After a few years we would be talking about ES6, ES7, ES8, ES9, ES10, ES11, etc. Those numbers quickly loose any context for people who aren’t deeply involved in the standards development process. Who would know if the current standard ES7, or ES8, or ES9? Was some feature introduced in ES6 or ES7? TC39 couldn’t eliminate the actual edition numbers (standards organizations love their document numbers) but it could change the document title. We decide that TC39 would incorporate the year of release into the documents title and to encourage people to use the year when referring to a specific edition. So, the “newest version of JavaScript” is ECMA-262, Edition 8 and its title is ECMAScript 2017 Language Specification. Some people still refer to it as ES8, but the preferred shorthand name is ECMAScript 2017 or just ES2017.

では、何故年ごとの呼称になるのでしょうか。 ECMA-262の第6版は長い時間がかかりました。 15年ですよ。 ES6の公開が近づいていますが、その作業工程を変更し毎年更新することが望まれていると、TC39 (Ecma International内のECMAScript仕様策定の専門委員会)はわかっていました。 そう、毎年新しいECMA-262、そして新しい版番号です。 数年後、私たちは ES6, ES7, ES8, ES9, ES10, ES11, etc. について会話することになります。 標準化作業に明るくない方々にとって、これらの番号はすぐわけがわからないものになってしまうでしょう。 現在の標準がES7なのかES8なのか、それともES9なのか、誰が知っているというのでしょうか。 ある機能が追加されたのはES6? それともES7?  TC39は実際の版番号を消し去ることはできませんでしたが(標準化組織は文書番号が好きなのです)、文書のタイトルを変えることはできました。 TC39は発行年を文書タイトルへ組み込み、また特定の版へ言及する場合はこの年を使うことを推奨することにしました。 ですので、「JavaScript最新版」はECMA-262の第8版であり、そのタイトルはECMAScript 2017 Language Specificationとなります。 (訳注: 2017/08/31当時)  これをES8と呼ぶ人もいますが、簡略化する場合はECMAScript 2017、あるいはただES2017とするのが良いでしょう。

(訳注: “edition number” を日本語で「版次」というらしいんだけど、あんまり一般的じゃなさそうなので「版番号」としました。いやまあおれが知らないだけかもしらんけど。)

そもそもESではなくJSと呼んだ方が

JSはESではありますが、本当にES自体についての文脈でなければJSの名前で呼んだ方が良いだろう、との提言もしておいでです。(前項引用箇所の次の段落。) 同感です。

「現代的なJavaScript」という呼び方

版番号が関係する場合でも、単純に新旧で分けるなら、ES2015以前を「古いJS (legacy JavaScript) 」、以後を「現代的JS (modern JavaScript) 」と呼びましょう、と。

ES2015は大きな変更でしたから、そこで分けるのは妥当だと思います。

まだIE 11(2013年リリース)とか対応しなきゃとかってのはあると思うんだけど、そこはBabelを使う等して、できるだけ現代的な書き方でやっていきたいっすねー。便利だもの。

その他

他のもさー

ついでにIE 9とかIE 11とかじゃなくて、IE2009とかIE2013とか呼びたくない?

あとAndroid 4.4じゃなくてAndroid 2013とか。iOSは……まあいいか。いいか?

おしまい

というわけで「現代的JavaScriptおれおれAdvent Calendar」全24回でした。ここまでお付き合い頂きありがとうございました。

良いお年を!

(でもいくつか触れ損ねた話題もあるので、もうちょっとだけ続くんじゃ。既存記事も少し書き足したりします。)

参考

JavaScript待望のクラス。(現代的JavaScriptおれおれアドベントカレンダー2017 – 04日目)

カテゴリー: JavaScript

現代的JavaScriptおれおれアドベントカレンダー2017 – 04日目

JavaScriptはオブジェクト指向プログラミング言語の一種ですが、「クラスベース」の他言語と異なり「プロトタイプベース」だと言われています。クラスの代わりにプロトタイプ prototype を使ってあれやこれやしてきました。

そんな我が道を行くJS君でしたが、やっぱり寂しかったのか、クラスの仕組みが導入されました。継承もできる。使っていきましょう。

使い方

ざっくりこんな感じ。

class Sushi {
  constructor(neta) {
    this.neta = neta;
  }

  get name() {
    return this.neta;
  }

  make() {
    console.log(`Hey ${this.name} o-match!`);
  }
}

const tuna = new Sushi('tuna');
tuna.make();  // => "Hey tuna o-match!"

なんとなくオブジェクトにメソッド書いてきたやりかたに近い感じですね。カンマとかいらないけど。

constructor() はインスタンス生成時に自動的に呼ばれるやつです。プロパティの初期化とかしましょう。

プロパティ

メソッドは前項のように書けるんですが、プロパティはどうするかというと、全部 constructor() の中で、動的に this へ追加していきます。

constructor(neta) {
  this.neta = neta;
}

JavaScriptのクラスはメソッド(か静的メソッド)しか定義できません。無理やり書くと “Exception: SyntaxError: bad method definition” とかになります。(プロパティも書けるようになるとかいう話も聞いた気がするけど、ES2017でもそうなってないすね。)

他の言語やってると「ンン!」という感じあるかもしれないですけど、まあほらJavaScriptは動的にプロパティ持てるから……。むしろ静的に持とうとすると失敗する (FAQ) から……。どうせ初期化時に値を設定するんだからこっちの方がわかりやすくない? だめ? だめならいいです。

あとは代替手段じゃないですけど、今回の name のように、getterを用意するという手もあります。場合によってはこちらもご検討ください。(別稿参照)

継承

extends でやります。あと constructor() からは super() で継承元クラスのコンストラクタを実行しましょう。

class SushiRoll extends Sushi {
  constructor(neta) {
    super(neta);
  }

  get name() {
    return `${this.neta} roll`;
  }
}

const californiaRoll = new SushiRoll('california');
californiaRoll.make();  // => "Hey california roll o-match!"

extends の右辺、上記の Sushi の部分には割と何でも書ける感じです。例えばここに class 式を丸ごとぶっこむことも可能です。(実践はご遠慮ください。)

静的メソッド(クラスメソッド)

static を付けます。

class Sushi {
  static prepareSumeshi() {
    console.log('Cooking rice...');
  }

  // ...
}

Sushi.prepareSumeshi();

class 宣言と class 式

function と同様に、クラスも宣言 (declaration) と式 (expression) の両方が用意されています。前項の例は宣言の方ですね。(セミコロンもいらない。)

いちおう、 class 式で書いて関数の引数にクラスを与えたりできます。

// 引数に
console.log(class {});

// 変数に
const Tako = class extends SushiNeta {
  // ...
};

それより module.exports に与えるのがありそう。

module.exports = class MyModule {
  // ...
};

ちなみに export に与える場合は宣言扱いです。セミコロン不要。

export default class MyModule {
  // ...
}

その他

巻き上げがない

var に対する let のように(?)、 class も function と異なり「巻き上げ」が起こりません。

なのでこれ↓だと未定義エラー。

const tuna = new Sushi('tuna');  // Uncaught ReferenceError: Sushi is not defined
                                 // Exception: ReferenceError: can't access lexical declaration `Sushi' before initialization

class Sushi {
}

常にStrictモード

クラス記法の中身は常に厳格な記述をすることになってます。

変数宣言なしで foo = 123 とか書いたらグローバル変数が作られる代わりにエラーになります。うれしい。

“Exception: ReferenceError: |this| used uninitialized in Bar class constructor”

コンストラクタの最初に super() を呼んでください。

Uncaught ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor

実際は最初じゃなくてもいいんですが、スーパーコンストラクタに与える値をこねくり回す以外の処理はやめましょう。

どうしても長くて関数にしたい場合は、クラスメソッドにするかなあ。

class Bar extends Foo {
  constructor(v) {
    const v2 = Bar.makeFoo(v);
    super(v2);
  }
  
  static makeFoo(v) {
    return { foo: v.toUpperCase() };
  }
}

“Exception: SyntaxError: missing { before class body”

ついうっかりクラス名の方に括弧書いちゃうとこのエラーが出ます。

class Foo() {  // Uncaught SyntaxError: Unexpected token (
}

もちろん正しくはこうです。

class Foo {
}

prototype は健在

いちおうクラスという仕組みはできたんですが、その実態は今まで通りの prototype ベースのオブジェクトだったりします。なのでクラスを作ったら Foo.prototype は健在です。

クラスが紹介されたときも「これはただの糖衣構文 (syntax sugar) だ」と言われてました。(なのでBabelで旧環境向けに変換できる。)

互換性を保持したということっすかね。

参考

  • ECMAScript 2015 Language Specification – ECMA-262 6th Edition
    • 14.5 Class Definitions
    • 14.5.11 Static Semantics: PrototypePropertyNameList … プロトタイプオブジェクトを用意する話
    • 14.5.14 Runtime Semantics: ClassDefinitionEvaluation … プロトタイプオブジェクトを作る話
    • 10.2.1 Strict Mode Code … クラスはStrictモードだよ
    • 13.1.4 Static Semantics: DeclarationPart … 巻き上げについて
    • 15.2.3 Exports … export の右辺はクラス宣言
  • class – JavaScript | MDN

ついでに

この記事で、このブログのちょうど256件目の記事みたいです。思ったより少ない。

アロー関数でさくさくコールバック。(現代的JavaScriptおれおれアドベントカレンダー2017 – 03日目)

カテゴリー: JavaScript

現代的JavaScriptおれおれアドベントカレンダー2017 – 03日目

便利ちょう便利。

要約

  • return するだけの場合、ブロックと return を省略できる
  • 一行の場合は自動的に return される
  • 引数がひとつだけの場合、括弧を省略できる
  • this の扱いに注意
  • arguments の代わりに ...args を使おう

使い方

例えばオブジェクトの配列↓を、

const data = [
  { id: 101, name: 'Alice' },
  { id: 102, name: 'Bob' },
  { id: 103, name: 'Charlie' },
];

IDだけの配列へ変換したい場合、やっぱり配列の map() を使うじゃないすか。

const ids = data.map(function(row) {
  return row.id;
});
console.log(ids);  // => [101, 102, 103]

これ↑をアロー関数にすると、こう↓です。

const ids = data.map((row) => {
  return row.id;
});
console.log(ids);  // => [101, 102, 103]

function を書かなくてもよくなりました。そんなに変わってないかもしれないけど、まずはここから。

省略して一行にする

いったんとりあえずそのまま一行にしてみます。

const ids = data.map((row) => { return row.id; });
console.log(ids);  // => [101, 102, 103]

今までも function 式でやってましたよね、こういうの。

さて、今回のように中身が一行だけの場合、なんと、ブロック { ~ } を省略できます。

すると、こう↓書けます。

const ids = data.map((row) => row.id);
console.log(ids);  // => [101, 102, 103]

おおっと、思ったより消えましたね。

return を省略する

実は一行のアロー関数にした場合、その => の右側が自動的に戻り値になります。そう、ブロックだけでなく return すら省略できるのです。

ちなみに戻り値が必要ない場合でも return はされますが、その値は捨てられるだけなので、特に問題はないです。逆に返しちゃいけない場合はブロック作って return undefined してください。

引数の括弧を省略する

加えて、引数の数がひとつだけの場合、括弧も外すことができます。

すると、こう↓書けます。

const ids = data.map(row => row.id);
console.log(ids);  // => [101, 102, 103]

最後に名前も短くしてしまいましょう。ほらこの場合は入る値が明白だから。

const ids = data.map(v => v.id);
console.log(ids);  // => [101, 102, 103]

だいぶ短くなりましたね。

function を単純にアロー関数へ置き換えられない場合

function 式をアロー関数で置き換えましたが、単純に置き換えできない場合もあります。

  • return が特殊な場合
  • this を使っている場合
  • arguments を使っている場合
  • new する場合

このうち return についてはもうやりました。

this が変わらない

例えば、これは駄目な例。(バック側のひとも知ってるかなと思ってるけどどう?)

el.addEventLister('click', (event) => {
  console.log(this === el);  // false
});

function だった頃は this は対象の要素を指すことが期待されました。ところがアロー関数はそうはなりません。

this は、アロー関数を実行した際の this になります。例えばコントローラーオブジェクトとか、グローバルオブジェクトとか。要は、かつて var that = this とか _this とかやってたのがいらなくなる感じです。便利っすねー。

さてじゃあ今回のこれどうするかというと、次項。

クリック他のイベントからは event.currentTarget で

対象の要素を取ってこれます。

// たまにはjQuery
$('.sugoi-button').on('click', (event) => {
  const el = event.currentTarget;
  console.log(el);
});

これはESではなくDOMの仕様。

arguments が取れない

関数実行時の引数リストを得る arguments なんですが、アロー関数の場合は普通の関数とは扱いが違うので、 arguments が用意されません。その外側の関数の引数になっちゃいます。

数がわからない場合、可変長引数 (rest parameters)で引数を全部取れます。

const f = (...args) => console.log(`${args.length}個の引数ゲットだぜ`);
f();  // => 0
f(1);  // => 1
f('ピカチュー', 'カイリュー', 'ヤドラン', 'ピジョン');  // => 4

可変長引数については後日別稿を。

new できない

コンストラクタになりません。

const Foo = () => {};
new Foo();  // TypeError: Foo is not a constructor

まあ this が固定されるという話も合わせれば、そりゃそうだよねーという感じはします。

その他

矢印 => 前は改行できない

なんか知らんけどそういう仕様になってます。

()
=> console.log('!');  // Exception: SyntaxError: no line break is allowed before '=>'

後ならよろしい。

引数がいらないなら _ とかにしたい

二文字 () 打つより一文字 _ の方が良くない? そうでもない?

アンダースコアは「受け取るけど捨てる引数」という意味です。(仕様じゃなくて風習。他言語の。)

ただ、関数に定義されている引数の数って fn.length で取れるんだけど、それを見て処理を変えてる場合はこれだと駄目です。例えばChaiのやつとか。

参考