やりたかったこと
一つの連想配列(オブジェクト)を利用して、複数のオブジェクトを持った配列を作成。
特定のプロパティの値に対して連番を振りたかった。
コード
const makeObjArr = (obj, num) => {
const results = [];
for (let i = 1; i <= num; i++) {
results.push(obj);
}
return results;
};
const incrementObj = (arr, targetKey) => {
return arr.map((item, idx) => {
item[targetKey] = item[targetKey] + String(idx + 1);
return item;
});
};
const a = {hoge: "hoge"};
const b = makeObjArr(a, 3);
console.log(incrementObj(b, "hoge"));
理想
{hoge: "hoge"} => [{hoge: "hoge1"}, {hoge: "hoge2"}, {hoge: "hoge3"}]
現実
{hoge: "hoge"} => [{hoge: "hoge123"}, {hoge: "hoge123"}, {hoge: "hoge123"}]
Q.なぜこうなった?
A. JavaScriptのObjectは参照渡しだから。
JSではプリミティブデータ型(Boolean, Null, Undefined, Number, String, Symbol)は値渡し。
Objectは参照渡しとなっています。(JavaScript に call by reference は存在しないため、誤解を生まないように打ち消し)
A. JavaScript の Object が保持するプロパティは書き換え可能だから。
例を挙げてみると
String
/* String */
var a = "a";
var b = a;
b = "b";
console.log(a);
console.log(b);
console.log(a === b);
a
b
false
Number
/* Number */
var a = 1;
var b = a;
b = 2;
console.log(a);
console.log(b);
console.log(a === b);
1
2
false
Symbol
/* Symbol */
var a = Symbol("a");
var b = a;
b = Symbol("b");
console.log(a);
console.log(b);
console.log(a === b);
Symbol(a)
Symbol(b)
false
コードを書いていて想定通りの動きになっているはず。
変数aの値は書き換えられず、変数bの値だけが書き換わっている状態。
ところがObjectだと・・・
/* Object */
var a = {value: "a"};
var b = a;
b["value"] = "b";
console.log(a);
console.log(b);
console.log(a === b);
{ value: 'b' }
{ value: 'b' }
true
見事にaのvalueプロパティの値が書き換えられている・・・。
/* {}とnew ObjectはObjectの生成/初期化を意味する */
var a = {};
var b = new Object;
var c = a;
console.log(a);
console.log(b);
console.log(a === b);
console.log(a === c);
c = {};
console.log(a === c);
{}
{}
false
true
false
で、どうすれば理想が現実になるの?
Object.assign()メソッドを使用する。
const makeObjArr = (obj, num) => {
const results = [];
for (let i = 1; i <= num; i++) {
results.push(Object.assign({}, obj));
}
return results;
};
const incrementObj = (arr, targetKey) => {
return arr.map((item, idx) => {
item[targetKey] = item[targetKey] + String(idx + 1);
return item;
});
};
const a = {hoge: "hoge"};
const b = makeObjArr(a, 3);
console.log(incrementObj(b, "hoge"));
[ { hoge: 'hoge1' }, { hoge: 'hoge2' }, { hoge: 'hoge3' } ]
Object.assign()
一つ以上の ソース オブジェクトから、直接所有で (own) 列挙可能な (enumerable) すべてのプロパティの値を、ターゲット オブジェクトへコピーします。戻り値はターゲット オブジェクトです。
素敵。
ただし、IEでは動作しない可能性があるので、そこは注意が必要。
Babelとか使ってなくて、IE対応があるから使えない・・・って場合だったらfor文で頑張って下さい。