web-dev-qa-db-ja.com

反復可能でない場合、オブジェクトスプレッドはどのように機能しますか?

spreadingの新しい使用法について学習しています。 object spreadはES2018の提案であることを理解しています。 Node 10.5で次のように機能します。

const oldObj = {name:"doug", age:34};
const newObj = {...oldObj}; 
console.log(newObj); // { name: 'doug', age: 34 }

拡散の興味深い用途の1つは、反復可能オブジェクトを配列に変換することです。たとえば、マップでうまく機能し、値のペアの配列の配列を提供します

const mappie = new Map().set("name", "doug").set("age", 234).set("profession", "seeker of Cthulhu");

const arr1 = [...mappie];

console.log(arr1); // [ [ 'name', 'doug' ], [ 'age', 234 ], [ 'profession', 'seeker of Cthulhu' ] ]

しかし、これをオブジェクトに使用することはできません

const obj = {
  name: "doug",
  age: 234,
  profession: "seeker of Chthulhu"
};

const arr2 = [...obj];
console.log(arr2);

くれます

TypeError: obj is not iterable

OK、オブジェクトは反復可能でないことを理解しています(現時点では)。しかしオブジェクトの広がり反復可能な広がりとは異なる種類の生き物ですか?それはある状況では機能するが他の状況では機能しないということであり、オブジェクトはオブジェクトにのみ拡散でき、配列にspreadできないことに気付くべきでしょうか?または、全体像を見逃していますか?私はspreadの新しい使用法を理解しようとしています、そしてどんな啓蒙も感謝しています...

8
Cerulean

しかし、オブジェクトの広がりは、反復可能な広がりとは異なる種類の生き物ですか?

はい。プロパティスプレッドは反復をまったく使用しません。これは新しい主要な構文であり、その実行時のセマンティクスは specによって で定義されており、反復可能/反復ではありません。

PropertyDefinition:... AssignmentExpression

  1. ExprValueを、AssignmentExpressionを評価した結果とします。
  2. FromValueを? GetValue(exprValue)。
  3. ExcludedNamesを新しい空のリストにします。
  4. 戻る? CopyDataProperties(オブジェクト、fromValue、excludeNames)。

プロパティスプレッドはオブジェクトプロパティ専用であり、反復可能なスプレッドの場合のようにそれをさらに一般化する必要はありません。 (あるいは、どうなるかはすぐにわかります。:-))

const arr2 = [...obj];のユースケースでは、おそらく Object.entries が必要です。

const arr2 = Object.entries(obj);

例:

const obj = {
  name: "doug",
  age: 234,
  profession: "seeker of Chthulhu"
};
const arr2 = Object.entries(obj);
console.log(arr2);

...または Object.keys プロパティ名だけが必要な場合、または Object.values 値だけが必要な場合。

もちろん、必要に応じてオブジェクトを反復可能にすることもできます。イテレータを指定するだけです。例えば:

const obj = {
  name: "doug",
  age: 234,
  profession: "seeker of Chthulhu",
  * [Symbol.iterator]() {
    return yield* Object.entries(this);
  }
};
const arr2 = [...obj];
console.log(arr2);

そして、クラスに適切なイテレータを定義し、プロトタイプにSymbol.iterator- namedプロパティを提供することで、イテラブルを作成するクラスのインスタンスを作成できます。

class ExampleList {
    constructor() {
        this.head = this.tail = null;
    }

    add(value) {
        const entry = {value, next: null};
        if (!this.tail) {
            this.head = this.tail = entry;
        } else {
            this.tail = this.tail.next = entry;
        }
    }

    * [Symbol.iterator]() {
        for (let current = this.head; current; current = current.next) {
            yield current.value;
        }
    }
}

const list = new ExampleList();
list.add("a");
list.add("b");
list.add("c");

for (const value of list) {
    console.log(value);
}

それはそれがいくつかの状況では機能するが他の状況では機能しないということです...

まあ、それは一般的にスプレッド表記にも当てはまります。プロパティスプレッドはオブジェクト初期化子内でのみ定義され、オペランドが何らかのオブジェクトである場合にのみ機能します。 (そして、対応する新しいプロパティレスト表記は、分解割り当てパターン内で定義されます。)反復可能スプレッドは、配列初期化子と関数引数リストでのみ定義され、そのオペランドが反復可能である場合にのみ機能します。 (対応する反復可能な残りの表記は、配列を作成する構造化割り当てパターン内で定義されます。)

5
T.J. Crowder

T.J. Crowderの答えは正しいですが、コメントとしては長すぎます。ユーティリティに感謝します。オブジェクトからいくつかのプロパティを取得し、それらを新しいオブジェクトに配置するという頻繁なケースを検討してください。 Ramdaやlodashのようないくつかのサードパーティライブラリは、これを行うユーティリティ関数を実装していますが、省略形のプロパティ、構造化、オブジェクトスプレッドの組み合わせにより、Vanilla JSで簡潔に実行できます。

const foo = { a: 1, b: 2, c: 3, d: 4 };
const { a, d } = foo;
const bar = { a, d };
console.log(bar); // { a: 1, d: 4 }

コンマ演算子を乱用してもかまわない場合は、さらに短くすることができます。

let a, d, bar, foo = { a: 1, b: 2, c: 3, d: 4 };
bar = ({a, d} = foo, {a, d});
3
Jared Smith