JavaScript中的原始值(undefined、null、布尔值、数字和字符串)与对象(包括数组和函数)有着根本区别。原始值是不可更改的:任何方法都无法更改(或“突变”)一个原始值。对数字和布尔值来说显然如此——改变数字的值本身就说不通,而对字符串来说就不那么明显了,因为字符串看起来像由字符组成的数组,我们期望可以通过指定索引来修改字符串中的字符。实际上,JavaScript是禁止这样做的。字符串中所有的方法看上去返回了一个修改后的字符串,实际上返回的是一个新的字符串值。例如:
- var s = "hello"; // 定义一个由小写字母组成的文本
- s.toUpperCase(); // 返回"HELLO",但并没有改变s的值
- s // => "hello": 原始字符串的值并未改变
原始值的比较是值的比较:只有在它们的值相等时它们才相等。这对数字、布尔值、null和undefined来说听起来有点儿难懂,并没有其他办法来比较它们。同样,对于字符串来说则并不明显:如果比较两个单独的字符串,当且仅当它们的长度相等且每个索引的字符都相等时,JavaScript才认为它们相等。
对象和原始值不同,首先,它们是可变的——它们的值是可修改的:
- var o = { x:1 }; // 定义一个对象
- o.x = 2; // 通过修改对象属性值来更改对象
- o.y = 3; // 再次更改这个对象,给它增加一个新属性
- var a = [1,2,3] // 数组也是可修改的
- a[0] = 0; // 更改数组的一个元素
- a[3] = 4; // 给数组增加一个新元素
对象的比较并非值的比较:即使两个对象包含同样的属性及相同的值,它们也是不相等的。各个索引元素完全相等的两个数组也不相等。
- var o = {x:1}, p = {x:1}; // 具有相同属性的两个对象
- o === p // => false: 两个单独的对象永不相等
- var a = [], b = []; // 两个单独的空数组
- a === b // => false: 两个单独的数组永不相等
我们通常将对象称为引用类型(reference type),以此来和JavaScript的基本类型区分开来。依照术语的叫法,对象值都是引用(reference),对象的比较均是引用的比较:当且仅当它们引用同一个基对象时,它们才相等。
- var a = []; // 定义一个引用空数组的变量a
- var b = a; // 变量b引用同一个数组
- b[0] = 1; // 通过变量b来修改引用的数组
- a[0] // => 1: 变量a也会修改
- a === b // => true:a和b引用同一个数组,因此它们相等
就像你刚看到的如上代码,将对象(或数组)赋值给一个变量,仅仅是赋值的引用值:对象本身并没有复制一次。如果你想得到一个对象或数组的副本,则必须显式复制对象的每个属性或数组的每个元素。下面这个例子则是通过循环来完成数组复制(见5.5.3节):
- var a = ['a','b','c']; // 待复制的数组
- var b = []; // 复制到的目标空数组
- for(var i = 0; i < a.length; i++) { // 遍历a[]中的每个元素
- b[i] = a[i]; // 将元素值复制到b中
- }
同样的,如果我们想比较两个单独的对象或者数组,则必须比较它们的属性或元素。下面这段代码定义了一个比较两个数组的函数:
- function equalArrays(a,b) {
- if (a.length != b.length) return false; // 两个长度不同的数组不相等
- for(var i = 0; i < a.length; i++) // 循环遍历所有元素
- if (a[i] !== b[i]) return false; // 如果有任意元素不等,则数组不相等
- return true; // 否则它们相等
- }