LoginSignup
1

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-06-07

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

実装例

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' なしでもオブジェクトを生成できます。

class StringArray { }

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

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

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

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

  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 をなんとなく理解する
JavaScript(ES2015〜)のProxyで、プロパティにフックする正しいやり方

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

  {
    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という便利オブジェクトに出会いました。
これは運命ですね。

[1, "hello", true, {}]

JavaScriptの配列はなんでもかんでも代入できる子なので、特定の型しか代入できない配列を作れるのは安心ですね。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1