多分コメント欄が本番です
バカみたいな話ですが、今まで、
const hoge = { ...hoge }
みたいなコードを書いてシャローコピーしたつもりになってたんですが、全然そんなことなかったって話です。黒歴史だ・・・
例えば、以下のようなクラスAを作ったとして、
class A {
constructor() {
this.aaa = 'aaa';
this.bbb = {
ccc: 'ccc'
};
}
afunc() {
console.log('クラスAの関数だよ');
}
get aGetter() {
return this.aaa;
}
}
そのインスタンスを以下のように作り、スプレッド演算子でシャローコピー(のつもり)を取ると、
const a = new A()
const _a = { ...a }
_a.aaa
や _b.bbb.ccc
にはアクセスできるんですが、_a.afunc()
や _a.aGetter
にはアクセスできません。
また、instanceof
の結果も変わります。
// Aクラスのインスタンスかどうか
console.log(a instanceof A) // => true
console.log(_a instanceof A) // => false
空オブジェクトにプロパティを詰め直しているイメージだと思うので、 instanceof
が変わるのはまあそうだよねって感じですが、メソッドやGetterも取れなくなるのはちょっと予想外でした。
と思ったら以下のコードは動くんですよね
const c = {
d() {
console.log('d')
},
}
const _c = { ...c }
_c.d() // => メソッドだけどアクセスできる!!
オブジェクトリテラルだといけるのかよどういうことなんだ・・・
ReactのpropやStore周りを書いていると、スプレッド演算子を使ったこんな感じのコードをよく書いているので、気をつけたいです。
シャローコピーを取る方法
以下のように書くとちゃんとシャローコピーを取れるっぽいです。 instanceof
の結果もtrue
になります
const _a = Object.assign(Object.create(Object.getPrototypeOf(a)),a)
2019/10/12 追記
コメントで教えていただきましたが、__proto__
にアクセスするのが良いかはおいておいて、以下でもいけるっぽいです。
const _a = { ...a, __proto__: a.__proto__ }
以上。
以下は検証コードです。暇な人はコピペして実行してみてください
"use strict";
class A {
constructor() {
this.aaa = 'aaa';
this.bbb = {
ccc: 'ccc'
};
}
afunc() {
console.log('クラスAの関数だよ');
}
get aGetter() {
return this.aaa;
}
}
class B extends A {
constructor() {
super(...arguments);
this.ddd = 'ddd';
this.eee = {
fff: 'fff'
};
}
bfunc() {
console.log('クラスBの関数だよ');
}
get bGetter() {
return this.ddd;
}
}
const b = new B();
const _b = { ...b };
const __b = { ...Object.create(Object.getPrototypeOf(b)), ...b };
const ___b = Object.assign(Object.create(Object.getPrototypeOf(b)), b);
console.log(b);
console.log(_b);
console.log(__b);
console.log(___b);
// Bクラスのインスタンスかどうか
console.log(b instanceof B); // => true
console.log(_b instanceof B); // => false
console.log(__b instanceof B); // => false
console.log(___b instanceof B); // => true
// Aクラスのインスタンスかどうか
console.log(b instanceof A); // => true
console.log(_b instanceof A); // => false
console.log(__b instanceof A); // => false
console.log(___b instanceof A); // => true
// Bクラスメソッドへのアクセス
console.log(b.bfunc); // => 返ってくる
console.log(_b.bfunc); // => undefined
console.log(__b.bfunc); // => undefined
console.log(___b.bfunc); // => 返ってくる
// Aクラスメソッドへのアクセス
console.log(b.afunc); // => 返ってくる
console.log(_b.afunc); // => undefined
console.log(__b.afunc); // => undefined
console.log(___b.afunc); // => 返ってくる
// Bクラスのgetプロパティへのアクセス
console.log(b.bGetter); // => ddd
console.log(_b.bGetter); // => undefined
console.log(__b.bGetter); // => undefined
console.log(___b.bGetter); // => ddd
// Aクラスのgetプロパティへのアクセス
console.log(b.aGetter); // => aaa
console.log(_b.aGetter); // => undefined
console.log(__b.aGetter); // => undefined
console.log(___b.aGetter); // => aaa