こんにちは! キャスレーコンサルティングTS(テクニカル・サービス)部の松永です。

ECMAScript 10(ECMAScript2019)のリリースが近づいてきています。

現場ではES6(ES2015)が普及してきており、
const let アロー関数 テンプレートリテラルを見る機会も増えてきました。

ES5からES6までのブランクが6年近くあります。
オブジェクト構造の中身を詳細に扱う為、主力が裂かれていたリリースES5から、
ES6で他言語習得者からの指摘にこたえる形で、グローバル変数の扱いを改めるスコープや、
prototypeの伝統を変更した点は、記憶に新しいのではないかと思います。

ESMA(ECMAはJavaScriptの標準化団体です)の策定団体である
TC39(Technical Committee)は毎年新リリースを発表しています。
リリースはブラウザのメーカーに、いくつかの段階を経て実装が完成されます。
デファクトスタンダードを握っている、Google Chromeの最新実装はChromeCanaryで試すことができ、
proposalという策定段階の初期で、既に済んでいる実装もあります。
proposalは、4段階のステージングを経てproposal4に届いたものがリリースされます。

ES6以降小ぶりな修正が続くため、見落とされがちな近年の公式リリースですが、
地味に便利になっているシンタックスもあります。
ここでは、ES6以降のリリースを一覧し、来年の前半で発表になるECMAScript10(ECMAScript2019)について、
すでにproposal4となっているものを、先駆けて概観していきたいと思います。

なお、近年はECMAの公式サイトとは別に、
GITHAB上でproposalのオファーの内容を見ることができるようになっており、こちらで確認できます。

ECMAScript8(ECMAScript2017)

Object.values/Object.entries

JSONデータを扱うとき、Object値を列挙するメソッド かつてオブジェクトの値の取得は、
一度keyを取得し、そのkeyに付随するvalueを取得していました。

for(var key of Object.keys(obj)){
  console.log(obj[key])
}

しかしこの実装は、やや意味が分かりにくい。 ということで、

let obj = { a: 1, b: 2, c: 3 }

console.log( Object.keys( obj )    )  // [ "a", "b", "c" ]
console.log( Object.values( obj )  )  // [ 1, 2, 3 ]
console.log( Object.entries( obj ) )  // [ ["a", 1], ["b", 2], ["c", 3] ]

 参考 JSnext様 となりました。
Object.keysに習ったというか、配列感覚でvalue一覧が取得できます。
object.entriesとすると、keys valuesが両方とも取得できます。

String.prototype.padStart / String.prototype.padEnd

指定した長さに合わすため、前方/後方に文字列を加えます

var string = "apple" ;
var a = string.padStart( 6, "a" ) ;
//→"aapple"
var a = string.padEnd( 6, "a" ) ;
//→"applea"

padStartは前方に、padEndは後方に字埋めします。
地味な変更ですが、String.prototype.slice()などと並ぶよく使う文字列操作組込メソッドです。
今後、利用頻度は増えると思います。

Object.getOwnPropertyDescriptors

既存実装のObject.getOwnPropertyDescriptor( obj, ‘hoge’ )の複数形
オブジェクト内のプロパティの記述子の内部属性を一覧で列挙。
複数形になったのが、ES7での変更点。

このように見てみると、わかりやすいのではないでしょうか。
個々のオブジェクトに与えられたデータやアクセサの一覧を取得でき、
ここに違う性質を持つオブジェクトのプロフィールようなものを取得します。

var foo = {
  bar: 100
};

var descriptor = Object.getOwnPropertyDescriptor(foo, 'bar');
console.log(descriptor);

// 出力
// {value: 100,
//  writable: true,
//  enumerable: true,
//  configurable: true }

 参考 奥の細道

関数の引数リストで、末尾のカンマが使用できるように

問答無用でしょう。以下を見れば一目瞭然。

//これまで 
function f(p) {}
//これから 
function f(p,) {} 
//ただし、引数がないのに (,)とするとエラーになる

asyncとawait

promise(fetchAPI)と同等の非同期処理の構文 asyncは、async function hoge() {} で定義し、
resolve またはエラー値であるrejectを返す。
その返値を待って、実行する関数がawait fuga(); awaitは、asyncの中に書く。
まだ難しかったpromiseの構文が、もっと直感的に。 asyncとawaitは大注目されており、ここでは簡単に。

function doSomethingAsync() {
    return new Promise((resolve) => {
        setTimeout(() => resolve('I did something'), 3000)
    })
}

async function doSomething() {
    console.log(await doSomethingAsync())
}

console.log('Before')
doSomething()
console.log('After')

 参考 rana_kualu これを実行すると、I did something が最後に出力されます。
改良というか、promiseの文法が少し特殊であったために出てきたもののように思います。
やってることはそれほど違いがあるように見えません。

SharedMemory と Atomic

JSでCPUのマルチスレッドを生かす方法は、webGLを使う系とworkerを使う系があり、
こちらは、workerがマルチスレッドとして機能した場合に、メモリ利用をより柔軟にするものです。

具体的には、既存のwebworkerはプロセスが立ち上がるたびに、 独自のメモリ領域を確保し、
複数コアが同一の処理対象を扱うときに重複がメモリを無駄に使います。
対してSharedMemoryでは同一の対象をメモリに配置し、複数コアが非同期で処理をかけてゆきます。
このときにおこる競合を解消するのがAtomicです。
近年、クライアントサイドで扱う機械学習libraryが出てきており、
マルチコアの有効利用はJSの可能性を広げるでしょう。
参考 nhiroki’s weblog

ECMAScript9(ECMAScript2018)

こちらはよくまとまっているサイトがありますのでここでは概観です。
https://arui.tech/es2018-new-features/

テンプレートリテラルの改修

タグ付きテンプレートから、エスケープシーケンスの構文制限を削除するそうです。
エスケープシーケンス制限は、 タグ付きテンプレートからのみ削除され、
タグなしテンプレートリテラルからはこれまで通りの動作です。

正規表現 s (dotAll) flag for regular expressions

正規表現で、Sオプションを追加。これをつけることによって
. でもマッチしないあらゆるデータ(例えば改行コード)にマッチするそうです。

console.log(/foo.bar/.test(`foo 改行
bar`)); // false
console.log(/foo.bar/s.test(`foo 改行
bar`)); // true

とします。

正規表現 named capture groups

これまでvar hoge =””.match(/page/i)では、返値が必ず配列であったため、
hoge[0]で取得していましたが、それに名前を付けられます。
正規表現の返値を、objectとして扱えるようになりました。

Spread properties

ES5で先行した配列で使うspread operator(スプレッド演算子(または、パラメータ))を、
オブジェクトで使えるようにしました。

const data = [15, -3, 78, 1];
console.log(Math.max(...data));    // 78

// もう以下のように書く必要はありません
console.log(Math.max.apply(null, data));


//配列の複製
const ary = ['Pen', 'Pineapple', 'apple'];
const myAry = [...ary];

console.log(ary === myAry);    // false
console.log(myAry);            // ["Pen","Pineapple","apple"]

と使っていたものをオブジェクトで使え、こうなります。

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
let n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }

参考 スプレッド演算子の便利な使い方まとめ

Rest properties

ES5で先行した配列で使うRest operator(レスト演算子(または、パラメータ))を、オブジェクトで使えるようにしました。

const arr = [1, 2, 3, 4, 5];
 
const [a, b, ...rest] = arr;
console.log(a, b, rest);  // => 1, 2, [3, 4, 5]

と使っていたものをオブジェクトで使え、こうなります。

const obj = { a: 1, b: 2, c: 3 };
const { ...objProps } = obj;
console.log(objProps); // { a: 1, b: 2, c: 3 }

//とか
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }
//と使えます

正規表現 Lookbehind Assertions

正規表現のパターンマッチの前に、評価を行ってからマッチを行います。
説明が入り組んでいるので、こちらを参照してください。
https://techracho.bpsinc.jp/hachi8833/2017_04_12/37386

正規表現 Unicode Property Escapes

unicode文字は1文字ごとに文字情報を持っているそうですが、 それにアクセスできるようになります。

Promise.prototype.finally

プロミスの成功・失敗に関わらず、継続して処理を行うためのPromise.prototype.finallyメソッド。
これまで、非同期処理の糖衣構文でtry~catchを使っていた方にとっては、
既視感のある処理ではないでしょうか。
promise の返値がrejectの場合でも、エラー出力後に、処理が継続できます。

async-iteration 非同期反復

async await 構文で 処理待ちをするawaitを反復処理し for await とできるようにします。
 参考 ES2018 Async Iteration

ECMAScript10(ECMAScript2019)

ここからは、まだ日本語で紹介しているサイトがないようです。

Optional catch binding 

try{}catch(e){}のお馴染みの構文で(e)が省略できる
これまでJSでは、try{}catch の後に必ず(e)を取り、書かないとエラーが出ていました。省略形を容認し、

try {
   // ... 
} catch {
   // ... 
}

とするようです。簡単になりましたね。

JSON superset

構文のJSONを、スーパーセットに拡張する提案。
エスケープされていないU + 2028 LINE SEPARATORと、
U + 2029 PARAGRAPH SEPARATOR文字を読み込めるように変更。
現在、これらを文字列リテラルに含めると、パースエラーが出る。

Symbol.prototype.description

JSには、基本となるプリミティブがBoolean、Null、Undefined、Number、Stringとありますが、
ES5で追加されたのがSymbolです。
constのように、再定義・再代入できないだけでなく、等価演算子===で同じものと評価できない為、
プロセスの中で同名のものは1つしかありません。
symbolは、配列アクセス演算子を使って、任意のオブジェクトにセットできます。
検索をすると、ローカルスコープにしかできない記述がありますが、

var symbol_a = Symbol.for( "key_a" );

とすると、グローバルな値となります。

var obj = new Object();
// ------------------------------------------------------------
// Symbol オブジェクトを作成する
// ------------------------------------------------------------
var symbol_a = Symbol();
var symbol_b = Symbol();
var symbol_c = Symbol();
// ------------------------------------------------------------
// シンボルプロパティを追加して、値をセットする
// ------------------------------------------------------------
obj[symbol_a] = 123;
obj[symbol_b] = "あいう";
obj[symbol_c] = {a:0,b:1,c:2};

// ------------------------------------------------------------
// シンボルプロパティから値を取得する
// ------------------------------------------------------------
var value_a = obj[symbol_a];
var value_b = obj[symbol_b];
var value_c = obj[symbol_c];

// ------------------------------------------------------------
// 出力テスト
// ------------------------------------------------------------
console.log( value_a ); // 123
console.log( value_b ); // "あいう"
console.log( value_c ); // {a:0,b:1,c:2}

  参考 JavaScriptプログラミング講座 これができるということは、
for inやObject.keys() などで取得できそうですができず、
まだない方法で取得するか、上記のような[]で取得しなければいけないようです。
何か、今までと次元が違うobjectとしてふるまうキーが誕生したような感じですが、
今回の修正では、シンボルについた識別名だけを取得するもののようです。

Function.prototype.toString 改修

toString の動作を、修正するそうです。

Array.prototype.flat

そのほかに、現在Proposal3まで来ているものが複数あり、個人的に気になったものを挙げておきます。
多次元配列を1次元配列に書き換えるもののようです。

まとめ

今年の公式がどのようになるかは、まだわかりませんが、
proposal3に到達しており先に実装が完了しているものもあるため、
最終的にはこれより増えることが予想されます。
近年の動向を踏まえると、タスクランナーやnpmなど、
サーバーサイドで先行している機能のブラウザ実装と、
古い機能の改修や補足が多いようです。
個人的には、PHPのarray_diff とarray_intersectが欲しいです。

松永大地
CSVIT事業部 TS(テクニカルサービス)部 松永大地
毎日が戦い