JavaScript

不変な基本型値と可変なオブジェクト参照

 JavaScript では、基本型値( undefined、null、論理値、数値、文字列)とオブジェクト(配列、関数)の間には、基本的な部分に違いがあります。まず、基本型は不変です。基本型値を変更する方法がないからです。これは、数値や論理値の場合に明らかです。数値の値を変更するということは意味がありません。しかし、文字列は文字の配列のように思えるので、指定した場所の文字を変更できるのではないかと考えても不思議ではありません。しかし、JavaScript では、このような処理はできません。文字列を変更するように見えるメソッドはすべて、文字列を変更するのではなく、新たに文字列を返しています。以下の例を見てください。

// 小文字の文字列から始める。
var s = "hello";

// "HELLO" が返されるが、s が変更されたわけではない。
s.toUpperCase();

// => "hello": 元の文字列は変更されていない。
s

 また、基本型はで比較します。つまり、2つの基本型値が同じ値を持つ場合にのみ、2つの値は等しいと判定されます。数値や論理値、null、undefined の場合は当然です。問題になるのは、文字列の場合だけです。JavaScript では、2つの異なる文字列値を比較する場合、両者が同じ長さで、各インデックスの文字が同じ場合にのみ、2つの文字列値は等しいと判定します。

 これに対して、オブジェクトは可変です。つまり、値を変更できます。以下の例を見てください。

// あるオブジェクトを例にする。
var o = { x:1 };

// プロパティの値を変更して、オブジェクトを更新する。
o.x = 2;

// 新しいプロパティを追加して、更に更新する。
o.y = 3;

// 配列も可変。
var a = [1,2,3]

// 配列の要素の値を変更する。
a[0] = 0;

// 配列に新しい要素を追加する。
a[3] = 4;

 オブジェクトは値では比較しません。同じプロパティを持ち、プロパティの値が同じだったとしても、異なる 2つのオブジェクトは等しいとは判定されません。同じように、同じ要素を同じ順序で持つ配列も等しいとは判定されません。次に例を示します。

// 同じプロパティを持つ 2つのオブジェクト。
var o = {x:1}, p = {x:1};

// => false: 別々のオブジェクトは等しいと判定されない。
o === p

// 2つの別々の空の配列。
var a = [], b = [];

// => false: 別々の配列は等しいと判定されない。
a === b

 オブジェクトは、JavaScript の基本型と区別するために、参照型と呼ばれることもあります。この言葉を使えば、オブジェクトの値は参照であり、オブジェクトは参照で比較すると言います。つまり、2つのオブジェクト値は、両方が同じオブジェクトを参照している場合のみ同一と判定されます。以下の例を見てください。

// 空の配列を参照する変数 a 。
var a = [];

// これで b も同じ配列を参照する。
var b = a;

// 変数 b を使って配列を参照し更新する。
b[0] = 1;

// => 1: 変更された内容は変数 a からも確認できる。
a[0]

// => true: a と b は同じオブジェクトを参照するので、等しいと判定される。
a === b

 この例からもわかるように、変数にオブジェクト(配列)を代入することは、単に参照を代入しているだけです。オブジェクトの新たなコピーを作成するわけではありません。オブジェクトや配列の新たなコピーを作成したい場合は、オブジェクトのプロパティや、配列の要素を明示的にコピーしなければなりません。以下の例では、for ループを使ってコピーしています。

// コピーしたい配列。
var a = ['a','b','c'];

// コピー先になる別の配列。
var b = [];

// a[] のインデックスごとに、
for(var i =0; i < a.length; i++) {
  // a の要素を b にコピーする。
  b[i] = a[i];
}

 同様に、2つのオブジェクトや配列を比較したいのであれば、プロパティや要素を比較する必要があります。以下のコードでは、2つの配列を比較する関数を定義しています。

function equalArrays(a,b) {
  // 大きさの異なる配列は等しくない。
  if (a.length != b.length) return false;

  // すべての要素を巡回する。
  for(var i = 0; i < a.length; i++)

  // 1つでも違っていれば、等しくない。
  if (a[i] !== b[i]) return false;

  // すべて同じであれば、等しい。
  return true;
}