TC39 Shorthand Property Assignment Improvementsの紹介

※これはJavaScript Advent Calendar 2019 7日目の記事です。

この記事ではTC39 のproposalの1つ、「Shorthand Property Assignment Improvements」について紹介します。 以前、勉強会で発表した内容で、スライドはこちらにあります。

TC39って?proposalって?

TC39について、公式サイトの言葉を借りると、

Ecma International's TC39 is a group of JavaScript developers, implementers, academics, and more, collaborating with the community to maintain and evolve the definition of JavaScript.
引用元: https://tc39.es/

早い話、JavaScriptの標準化について、新機能や改善、それらの具体的な実装について、専門とした組織です。

本記事で紹介するのはTC39に提案されているproposalのうちの1つであり、proposalは0から4までの5つのstageがあり、提案内容について議論や、具体的な実装方法について議論されています。

各stageの説明について、詳しくは公式のThe TC39 Processを読むと良いです。 https://tc39.es/process-document/

Shorthand Property Assignment Improvements」は本日2019/12/07現在でstage 0であり、一番最初、提案内容を紹介する最初のフェーズ。

残念ながら、提案によってはstageが年単位で進んでいないものもあるため、実際にこの提案がJavaScriptの標準に取り込まれるかは、まだわからないです。

Shorthand Property Assignment Improvements

本題であるShorthand Property Assignment Improvementsは、オブジェクトプロパティへの値の代入の省略記法の提案です。

具体的には

  • オブジェクトの初期化
  • オブジェクトプロパティに対する分割代入

の省略した記法を提案です。

オブジェクトの初期化

// dot notation
const a = { x: o.x };
// bracket notation
const b = { 'x': o['x'] };

これを

// dot notation
const a = { o.x };
// bracket notation
const a = { o['x'] };

と記述できるようにするという内容です。

似たものとして既存のJavaScriptにおいても、ES2015から変数をオブジェクトプロパティに割り当てる際に、同名プロパティに割り当てるならば、プロパティ名を省略することができますね。 他のオブジェクトのプロパティを割り当てることはできないので、これをカバーするというところかもしれないですね。

const a = "Hello!";
const b = { a };

console.log(b); // { a: "Hello!" }

developer.mozilla.org

オブジェクトプロパティに対する分割代入

次に、オブジェクトプロパティに対する分割代入の省略記法です。 初期化と同様の省略記法を、もちろん分割代入でも利用できるようにしようという話です。

// dot notation
({ x: a.x } = o);
// bracket notation
({ a.x } = o);

これを

// dot notation
({ 'x': a['x'] } = o);
// bracket notation
({ a['x'] } = o);

と記述できるようにする。

まとめ

Shorthand Property Assignment Improvements」はオブジェクトプロパティの割り当ての際の省略記法の提案です。

自分の所感としては、記述が簡潔になり、コードとして冗長な記述をしなくてよくなるのはよいかな〜と感じます。

1つのプロパティについて、割り当てるプロパティを変更する際、

  • 割り当て先のプロパティ名
  • 割り当て元のプロパティ名

の二箇所を修正しなければいけないところが

  • 割り当て元のプロパティ名

のみを修正することで、対応できるようになるのは少し嬉しいですね。

JavaScript アドベントカレンダー 2019

これはJavaScript Advent Calendar 2019 7日目の記事です。

昨日、6日目の記事はこちらになります。

JavaScriptでAA Quine入門

これはウィルゲート Advent Calendar 2019 1日目の記事です。

はじめに

本日で25歳になります。shoponです。

ウィルゲート Advent Calendar 2019の1日目ということで、最近私が趣味で書いているAA Quine(アスキーアートクワイン)の書き方を紹介します。

最終的に出来上がるもの

f:id:shoponpon:20191129184013p:plain
reiwa_quine.js

https://gist.github.com/shoponpon/ebc65bf2cf529f8c23b6fd7a816de172

必要なもの

  • AAの元となる画像
  • Node.js環境
    • Node.jsが利用できる環境であれば大抵動くはずです。

Quine

Quine(クワイン)は自分自身を出力するプログラムです。

JavaScriptは比較的簡単に自分自身を出力することができます。

例:JavaScriptによるQuine

今回の記事では、ソースコードの形がAA(アスキーアート)になっているQuineを、特にAA Quineと呼ぶことにします。

AA Quineを書くメリット

ないです。コーポーレートロゴや推しキャラでプログラムがかけます。

AA Quineの構成要素

今回作成するAA Quineは以下の要素で構成されます

紹介するQuineの作成方法では、最終的にAAを1行にまとめて eval() で評価するために、以下のルールを満たしてコーディングを行います。

  • ソースコードの文字列をAAの文字数以内に収める
  • スペース、バッククォート、バックスラッシュを使わない

AA Quineの書き方

本題のAA Quineの書き方について紹介していきます。

AA Quineを書く際にいきなりAAの形でプログラムを書くことは困難です。

ではどうするかというと、AA Quineを出力するコードを作成すれば良いです。

アスキーアートの形を圧縮した文字列の作成

まず、AA Quineに用いるAAを作成するために、ベースとする画像を加工し、モノクロの画像を作成します。

モノクロ画像の作成方法について、ここで詳しい説明は省略しますが、お好きなPhotoshop, GIMP, CLIP STUDIO PRO等でどうぞ。

f:id:shoponpon:20191129184136p:plain
モノクロの令和

次に、モノクロ画像をアスキーアートにします。

Web上に既存のジェネレータがいくつかあるので、いずれかを使います。

今回は以下のジェネレータを利用してAAを作成し、不要な文字を削り、調整しました。

AA変換(アスキーアート生成)

f:id:shoponpon:20191129184306p:plain
令和AA generated by AA変換(アスキーアート変換)

AA Quineでは、AAがソースコードで構成されるため、この段階でのAAがどういった文字で構成されるかといった情報は不要です。

作成したAAを構成する文字のうち、プログラムに置き換わる場所を「1」、それ以外をスペースに置換します。

f:id:shoponpon:20191129184432p:plain
1とスペースでできた令和

これでAAの準備は完了です。

アスキーアートの形を圧縮した文字列を作る

作成した1とスペースでできたAAは文字数が多く、AA Quineに入れることが難しい場合があります。

そこで、AA Quineに埋め込みやすい大きさに圧縮します。

ここでは以下のプログラムを利用することで圧縮した文字列を作成します。

このプログラムは1とスペースでできたAAを

  1. Deflate圧縮
  2. Base64エンコード

して圧縮&暗号化します。

// これは令和のAAを圧縮したもの
eJzV1dENwyAMBND/TOEVsv9ylaqiEnJ3nCmU1l8RCD8wTnKcm+JQkxF74HhGNqO5SsADbGKrHE6y2fJQuErjpmvhQNGBr7s3T4Lge9pQcLvEky24PCIYXZZzf8pxYGIomlxlCkb533lQJYJOJ+AyQdpRvjMfwN49Eh2MerD98mMYjWa72rKhG+elbNnmEiF3cmvmsRPDyjH6ddYGnAkLt0r7Jdip7Ao4qvl+hnkwb+LFMEz7g3AZ+QNYfkAWw5JdWOpeIJh+dSfCuQVjf6cJsQ9+AK+Cjmc=

AA Quineを出力するプログラムを書く

最後にAA Quineを出力するプログラムを書きます。

処理の流れとしては

  1. Delflate圧縮&Base64されたAAを復元
  2. 自分自身であるプログラムコードを取得
  3. 1で復元したAAのうち、「1」の箇所に2で取得したコードから1文字ずつ取り出し置換した文字列を作成する
  4. 出力

となります。またこのプログラムを作成する際のルールとして

  • ソースコードの文字列をAAの文字数以内に収める
  • スペース、バッククォート、バックスラッシュを使わない

を守ったものを実装していきます。

今回実装したコードはこちら。

Delflate圧縮&Base64されたAAを復元

AA_DATA = 'Delflate圧縮&Base64エンコードした文字列';
AA = require('zlib').inflateSync(Buffer.from(AA_DATA, 'base64')).toString();

下準備で用意した文字列から1とスペースでできたAAを復元します。

直接使用できない文字をUnicodeから変換

SPACE = String.fromCharCode(32);
NEW_LINE = String.fromCharCode(10);
BACK_SLASH = String.fromCharCode(92);
BACK_QUAT = String.fromCharCode(96);

今回紹介している書き方では eval() する文字列に、任意の改行を含められるテンプレート文字列 ```` にAAとなった文字列を入れます。

そのため、AAを構成するプログラムではバッククォート ``` が使えなかったり、AAを実行可能な文字列に変換する際に使用できないスペースが必要になったりします。また、改行文字\nやバックスラッシュ` についても意図しないエスケープシーケンスとして認識されてしまい、単純に記述することができません。

そこで、上記のようにUnicodeから変換して変数に入れることで、AA中の文字列処理で利用できるようにします。

自分自身であるプログラムコードを取得

ONE_LINER = '(F=' + F.toString().split(SPACE).join("").split(NEW_LINE).join("") + ')()' + "//";
CHARS = ONE_LINER.split('').join('');

JavaScriptでは関数の中身を文字列で取得することができるので、全体の処理を含む関数Fを文字列で取得します。

最後に // を付けることでワンライナーになった際に // 以後がコメントとして扱われるようにします。

1で復元したAAのうち、「1」の箇所に2で取得したコードから1文字ずつ取り出し置換した文字列を作成する

p = 0;
aaCode = '';
AA.split('').map(c => {
    if (c === SPACE) {
        aaCode += SPACE;
        return;
    };
    if (c === '1') {
        p %= CHARS.length;
        aaCode += ONE_LINER[p];
        p++;
        return;
    };
    aaCode += c;
});

復元したAAを1文字づつ見ながら、「1」の部分のみを関数F中の文字に置き換えた文字列を作成します。

p %= CHARS.length;
aaCode += ONE_LINER[p];

AAに従って文字列を作成する中で、関数Fの文字が尽きてしまう場合がありますが、上記のようにすることで、繰り返し関数Fの文字を使い回すことができます。

出力

console.log("eval("+BACK_QUAT+aaCode+BACK_QUAT+".split('"+SPACE+"').join('').split('"+BACK_SLASH+"n').join(''))");

最後に eval() で囲み、evalの引数(アスキーアート)をワンライナーに変換する処理を追加して出力します。

実行

実際に作成したプログラムのgistです。 https://gist.github.com/shoponpon/ebc65bf2cf529f8c23b6fd7a816de172

$ node reiwa.js | node

まとめ

今回はJavaScriptによるQuineの書き方の一例を紹介しました。

この他にも eval() を使わない方法や、もっと短いコードで構成する方法もありますが、今回のような関数を直接出力するような書き方であれば、意外と簡単に書くことができます。 今回紹介した書き方で登場した関数Fの中にロジックを仕込むと、こんなものも書けたりします。

皆さんもこれを機に、難解プログラミングしませんか?

明日は…

ウィルゲート Advent Calendar 2019 2日目は cocoeyes02さんの「PHPカンファレンス 2019 に、ウィルゲートのエンジニアが登壇しました! #phpcon」です!

お楽しみに!!