ブログ

オブジェクトの参照渡しと値渡し [JavaScript]

JavaScriptでオブジェクトを別の変数に格納(複製)する際には、「参照渡し」「値渡し」を意識する必要があります。

 

参照渡しは、変数間でオブジェクトを共有する振る舞いをします。

一方、値渡しは、別のオブジェクトとして扱われます。

 

JavaScriptではこれらの渡し方を、以下のようなメソッドで実現することができます。

代入記号は参照渡し

以下のように、代入記号を使った場合は、参照渡しになります。


var obj = {
  'name' : 'hoge',
  'age' : 22,
  'address' : {
    'country' : 'Japan',
    'city'    : 'Tokyo'
  }
}

var obj_copy = obj;

obj_copy.age = 44;

console.log(obj.age); // 44
  

変数にはオブジェクトを参照するメモリが格納されます。

例えば、代入後のオブジェクトの値を変更すると、元のオブジェクトの値も変更されます。

Object.assign()も参照渡しの挙動

ECMAScript 6でObject.assign()が追加されました。

第一引数に空のオブジェクト、第二引数にコピー元のオブジェクトを渡すと、コピーされたオブジェクトを返します。


var obj = {
  'name' : 'hoge',
  'age' : 22,
  'address' : {
    'country' : 'Japan',
    'city'    : 'Tokyo'
  },
}

var obj_copy = Object.assign({}, obj);

obj_copy.age = 44;
obj_copy.address.city = 'Osaka';

console.log(obj.age); // 22
console.log(obj.address.city); // 'Osaka'
  

プロパティの値を変えても、元のオブジェクトには影響がないように見えます。

しかし、オブジェクトのオブジェクトの値を変えてみると、参照渡しになっていることがわかります。

 

オブジェクト内に含まれるオブジェクトはコピーではなく、参照となるコピーをシャローコピーといいます。

値渡しをJSONで実現する

オブジェクトをJSON化して、それをオブジェクトに戻します。

そうすることで、オブジェクト内のオブジェクトもコピーされます。


var obj = {
  'name' : 'hoge',
  'age' : 22,
  'address' : {
    'country' : 'Japan',
    'city'    : 'Tokyo',
  },
  'birthday' : function(){}
}

var obj_copy = JSON.parse(JSON.stringify(obj));

obj_copy.age = 44;
obj_copy.address.city = 'Osaka';

console.log(obj.age); // 22
console.log(obj.address.city); // 'Tokyo'

console.log(obj_copy.birthday); // undefined
  

オブジェクト内に含まれるオブジェクトもコピーすることをディープコピーといいます。

 

ただし、JSON化することによって、プロパティにfunctionがある場合は、そのプロパティは消えてしまいます。

上記例では"birthday"プロパティに、空のfunctionを追加しました。

コピー後のオブジェクトでは、"birthday"プロパティがundefinedになっています。

JSでの値渡しはライブラリに頼る

上記で紹介したように、JavaScriptでは値渡しを実現する関数が標準でありません。

そのため、自作するか外部ライブラリに頼らざるえません。

jQueryでは、$.extend()メソッドでオブジェクトの値渡しが可能です。


var obj = {
  'name' : 'hoge',
  'age' : 22,
  'address' : {
    'country' : 'Japan',
    'city'    : 'Tokyo',
  },
  'birthday' : function(){}
}

var obj_copy = $.extend(true,{}, obj)

obj_copy.age = 44;
obj_copy.address.city = 'Osaka';

console.log(obj.age); // 22
console.log(obj.address.city); // 'Tokyo'

console.log(obj_copy.birthday); // function birthday() {}
  

$.extend(true,{}, obj)のように第一引数にtrueを指定するとディープコピーになります。

この時、"birthday"プロパティのfunctionもコピーされていることがわかります。

 

一方、$.extend({}, obj)とすると、シャローコピーとなります。