String[](String型配列)をProxyオブジェクトを使って作ってみた


「もっとこうした方がいいよ」「ここバグあるよ」など助言、マサカリお待ちしております。

#実装例
```javascript:StringArray.js
function StringArray() {
  let existArgument = true;

  // String型以外は許しまへんで
  const isStringType = value => {
    if (typeof value !== "string") {
      throw new Error("Incompatible types. Please converted to String.");
    };
  };

  if (arguments.length === 0) {
    // new Array()の場合
    existArgument = false;
  } else if (arguments.length === 1 && typeof arguments[0] === "number") {
    // new Array(arrayLength)の場合
    ; // 何もしない
  } else {
    // new Array(element0) と new Array(element0, element1[, ...[, elementN]])の場合
    for (const value of arguments) {
      isStringType(value);
    };
  };

  return new Proxy((existArgument ? new Array(...arguments) : new Array()), {
    get: function (obj, prop, receiver) {
      return Reflect.get(obj, prop, receiver);
    },

    set: function (obj, prop, value, receiver) {
      const convertedProp = Number(prop); // 数値型に変換
      if (Number.isNaN(convertedProp) || !Number.isInteger(convertedProp) || convertedProp < 0) {
        // NaN または 整数値でない または 0未満 であれば通常のプロパティ
        return Reflect.set(obj, prop, value, receiver);
      };
      isStringType();
      return Reflect.set(obj, convertedProp, value, receiver);
    }
  });

}
```
#ところどころ解説

classを使わない理由は、下記のように関数実行ができないためです。
functionで定義することで 'new' なしでもオブジェクトを生成できます。

```js
class StringArray { }

const a = new StringArray(); // a {}
const b = StringArray(); // Uncaught TypeError: Class constructor a cannot be invoked without 'new'
```

isStringType関数は配列に代入される要素がString型であるかを確認します。

```js
  const isStringType = value => {
    if (typeof value !== "string") {
      throw new Error("Incompatible types. Please converted to String.");
    };
  };
```

引数はargumentsオブジェクトから取得します。
argumentsオブジェクトの長さ(引数の個数)によって対応を変えます。

```js
  if (arguments.length === 0) {
    // new StringArray() に対応します。

  } else if (arguments.length === 1 && typeof arguments[0] === "number") {
    // new StringArray(10) などに対応します。
    // 負の数、小数の場合、Arrayオブジェクト側でエラーを吐きます。

  } else {
    // new StringArray("hello") や new StringArray("taro", "jiro", "subro") など対応します。
    // isStringArray関数でString型以外を排除します。

  };
```

Proxyオブジェクトを使います。
Reflectオブジェクトというものをセットで使うそうですが、今回はよく理解せずコードを書いてます（危険）。

ざっくりと、Proxyはsetter, getterのような動きをしますが、
setter, getterが定義されている・されていないに関わらず、自前の処理を噛ませることができます。
プロパティの更新検知や、特定のプロパティにはアクセスさせない処理などを追記できます。

Proxy, Reflectについては、以下の記事を拝読しました。
[ECMAScript 2015 の Proxy(Proxies) / Reflect をなんとなく理解する](https://qiita.com/ToshihiroGoto/items/36a4b355ba274ab25b46)
[JavaScript（ES2015〜）のProxyで、プロパティにフックする正しいやり方](https://qiita.com/tkykmw/items/6981edef82fed25d370a)

今回はString型以外の値が代入されることを防ぎたいため、setterで判定を行います。

```js
  {
    get: function (obj, prop, receiver) {
      return Reflect.get(obj, prop, receiver);
    },

    set: function (obj, prop, value, receiver) {
      const convertedProp = Number(prop); // 数値型に変換
      if (Number.isNaN(convertedProp) || !Number.isInteger(convertedProp) || convertedProp < 0) {
        // NaN または 整数値でない または 0未満 であれば通常のプロパティ
        return Reflect.set(obj, prop, value, receiver);
      };
      isStringType();
      return Reflect.set(obj, convertedProp, value, receiver);
    }
  }
```


#感想
思い付きで作ってみたところProxyという便利オブジェクトに出会いました。
これは運命ですね。

```js
[1, "hello", true, {}]
```
JavaScriptの配列はなんでもかんでも代入できる子なので、特定の型しか代入できない配列を作れるのは安心ですね。