Home About
javascript, object-oriented

オブジェクト指向的な JavaScript

先日 closure を使った オブジェクト指向的なコードを JavaScript で書くというデモをする機会があり、これはその覚書です。

ES6 などでは そもそも class が使えるので、ここで説明する方法で記述することはないと思いますが、 InDesign などの ExtendScript では未だに ES3 レベルで class は使えないので、その向きには有効かと思います。

Java(Groovy) で表現するこんなコードを JavaScript で実装し直す

Product というクラスを使った Java(Groovy)ではごく普通のコード。
製品名と価格を渡して Prodcut オブジェクトを生成し、render() を呼ぶと 製品情報を出力する機能を持つ。

class Product {
    private final String name
    private final int price
    private final int priceWithTax

    Product(String name, int price, float taxRate){
        this.name = name
        this.price = price
        this.priceWithTax = (price * (1.0f + taxRate)) as int
    }

    private static String addComma(int value){
        def m = ("${value}" =~ /(\d+)(\d{3})/)
        if( m.find() ){
            return "${m.group(1)},${m.group(2)}"
        }
        return "${value}"
    }

    String render(){
        return "- ${name} : ${addComma(price)}円 (税込み ${addComma(priceWithTax)}円)"
    }
}

def taxRate = 0.1f

def macmini    = new Product('mac mini', 72_800, taxRate)
def macbookAir = new Product('macbook air', 104_800, taxRate)
def macbookPro = new Product('macbook pro', 134_800, taxRate)

[macmini, macbookAir, macbookPro].each {
    println it.render()
}

実行すると結果はこれ:

- mac mini : 72,800円 (税込み 80,080円)
- macbook air : 104,800円 (税込み 115,280円)
- macbook pro : 134,800円 (税込み 148,280円)

JavaScript (Node.js v12.16.2) に翻訳

JavaScript で class を使わないので Product クラスを makeProduct という名前の 関数 に変えています.

const makeProduct = function(name, price, taxRate){
    const addComma = function(value){
        String(value).match(/(\d+)(\d{3})$/)
        return RegExp.$1 + "," + RegExp.$2;
    };

    const priceWithTax = Math.round( price * (1.0 + taxRate) );
    return function(){
        return `- ${name} : ${addComma(price)}円 (税込み ${addComma(priceWithTax)}円)`;
    };
};

const taxRate = 0.10;

const macmini = makeProduct('mac mini', 72_800, taxRate);
const macbookAir = makeProduct('macbook air', 104_800, taxRate);
const macbookPro = makeProduct('macbook pro', 134_800, taxRate);

[macmini, macbookAir, macbookPro].forEach( function(item){
    console.log(item());
});

makeProduct 関数は 無名の関数を返し、その無名の関数内に name, price, priceWithTax という変数を捕捉(close)している、ということだと思う。 Closures の詳細は JavaScript/Closures などを御覧ください。

補足: VSCode のプロジェクト設定

JavaScript を VSCode でデバッグする場合、プロジェクトディレクトリの .vscode/launch.json に以下を書く。 これで VSCode 上で JavaScript のデバッグできるのはとても楽です。

{
    "version": "1.0.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "file",
            "program": "${file}",
            "stopOnEntry": false
        }
    ]
}