6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2022年1月13日 JavaScript (JS Primer) Array#lengthをアクセッサプロパティで再現する

復習

  • getterは"インスタンス.プロパティ名"とする事でプロパティを参照した時に実行されます。getterは値を返すものです。
  • setterは"インスタンス.プロパティ名 = 値"とする事で値を代入した時に、値が仮引数となって実行されます。

Array#lengthをアクセッサプロパティで再現する

getterやsetterを利用しないと実現が難しいものとしてArray#lengthプロパティがあります。

以下はlengthプロパティの仕組みです。

const array = [1, 2, 3, 4, 5];
// 要素数を減らすと、インデックス以降の要素が削除される
array.length = 2;
console.log(array.join(", ")); // => "1, 2"
// 要素数だけを増やしても、配列の中身は空要素が増えるだけ
array.length = 5;
console.log(array.join(", ")); // => "1, 2, , , "

このlengthプロパティの挙動を再現するArrayLikeクラスを実装してみます。

  • 現在要素数より小さな素数が指定された場合、その素数を変更し、配列の末尾の要素を削除する
  • 現在要素数より大きな素数が指定された場合、その素数だけを変更し、配列の実際の要素はそのままにする
/**
 * 配列のようなlengthを持つクラス
 */
class ArrayLike {
    constructor(items = []) {
        this._items = items;
    }

    get items() {
        return this._items;
    }

    get length() {
        return this._items.length;
    }

    set length(newLength) {
        const currentItemLength = this.items.length;
        // 現在要素数より小さな`newLength`が指定された場合、指定した要素数となるように末尾を削除する
        if (newLength < currentItemLength) {
            this._items = this.items.slice(0, newLength);
        } else if (newLength > currentItemLength) {
            // 現在要素数より大きな`newLength`が指定された場合、指定した要素数となるように末尾に空要素を追加する
            this._items = this.items.concat(new Array(newLength - currentItemLength));
        }
    }
}

const arrayLike = new ArrayLike([1, 2, 3, 4, 5]);
// 要素数を減らすとインデックス以降の要素が削除される
arrayLike.length = 2;
console.log(arrayLike.items.join(", ")); // => "1, 2"
// 要素数を増やすと末尾に空要素が追加される
arrayLike.length = 5;
console.log(arrayLike.items.join(", ")); // => "1, 2, , , "

Array.prototype.slice()

第一引数に開始位置、第二引数に終了位置を指定し、その範囲を取り出した新しい配列を返します。

・第二引数は省略でき、省略した場合は文字列の末尾が終了位置となります。
・終了位置のインデックスの要素は取り出してくれないというのが意外な部分です。

const array = ["A", "B", "C", "D", "E"];
// インデックス1から4の手前の範囲を取り出します。
console.log(array.slice(1, 4)); // => ["B", "C", "D"]

Array.prototype.join()

join() メソッドは、配列 (または配列風オブジェクト) の全要素を順に連結した文字列を新たに作成して返します。区切り文字はカンマ、または指定された文字列です。配列にアイテムが一つしかない場合は、区切り文字を使用せずにアイテムが返されます。

 joinメソッドは仮引数に何も渡さないと,で区切ってくれるので今回の出力結果と同じですが、,とコンマの後に半角スペースを持たせるためにあえて明示されていました。配列を実際にコードで書くときには半角スペースを入れるので、joinを使用した場合も今回のコード例のように入れた方が丁寧だと感じました。

  • 何を返す?・・・文字列を返します。

  • どんな文字列?・・・配列の要素をコンマ付きでつなげた文字列。

  • 引数は何を設定?・・・コンマの部分に差し込むもの

デフォルト引数

JavaScript では、関数の引数は、指定しなければ undefined になります。

  • constructor(items = [])とすることで例外やundefinedが出力されるケースで代わりに空配列を出力するという宣言をしています。
インスタンス化する際に引数を持たせなかった場合の出力結果です。
    constructor(items = []) {
        this._items = items;
    }

・ デフォルト引数を使わない場合(constructor(items)とした場合)

TypeError: Cannot read properties of undefined (reading 'length')  

・ デフォルト引数を使った場合(constructor(items = [])とした場合)

const arrayLike = new ArrayLike();
console.log(arrayLike);

出力結果
{ _items: [] }    

参考

JS primer Array#lengthをアクセッサプロパティで再現する

new Array()と[]の違い

Array.prototype.join() - JavaScript | MDN

Array.prototype.slice() - JavaScript | MDN

デフォルト引数