3.7 不可变的原始值和可变的对象引用
JavaScript 中的原始值(undefined,null、布尔值、数字和字符串)与对象(包括数组和函数)有着根本区别。原始值是不可更改的:任何方法都无法更改(或“突变”)一个原始值。对数字和布尔值来说显然如此一改变数字的值本身就说不通,而对字符串来说就不那么明显了,因为字符串看起来像由字符组成的数组,我们期望可以通过指定索引来修改字符串中的字符。实际上,JavaScript 是禁止这样做的。字符串中所有的方法看上去返回了一个修改后的字符串,实际上返回的是一个新的字符串值。例如:
> var s = "hello"; //定义一个由小写字母组成的文本
> s.toUpperCase(); //返回"HELLO",但并没有改变s的值
> s // => "hello":原始字符串的值并未改变
var h = "hzh";
console.log("转换为大写字母:");
console.log(h.toUpperCase());
console.log("");
console.log("输出hzh变量:");
console.log(h);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
转换为大写字母:
HZH
输出hzh变量:
hzh
[Done] exited with code=0 in 0.174 seconds
原始值的比较是值的比较:只有在它们的值相等时它们才相等。这对数字、布尔值、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 hzh1 = { x:1 }; // 定义一个对象
hzh1.x = 2; // 通过修改对象属性值来更改对象
hzh1.y = 3; // 再次更改这个对象,给它增加一个新属性
var a = [1,2,3] // 数组也是可修改的
a[0] = 0; // 更改数组的一个元素
a[3] = 4; // 给数组增加一个新元素
console.log("检查hzh1对象的x属性是否发生变化:");
console.log("hzh1.x = " + hzh1.x);
console.log("");
console.log("检查hzh1对象是否新增y属性:");
console.log("hzh1.y = " + hzh1.y);
console.log("");
console.log("检查a[0]的值是否发生变化:");
console.log("a[0] = " + a[0]);
console.log("");
console.log("检查是否新增a[3]元素:");
console.log("a[3] = " + a[3]);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
检查hzh1对象的x属性是否发生变化:
hzh1.x = 2
检查hzh1对象是否新增y属性:
hzh1.y = 3
检查a[0]的值是否发生变化:
a[0] = 0
检查是否新增a[3]元素:
a[3] = 4
[Done] exited with code=0 in 0.17 seconds
对象的比较并非值的比较:即使两个对象包含同样的属性及相同的值,它们也是不相等的。各个索引元素完全相等的两个数组也不相等。
var o = {x:1}, p = {x:1}; // 具有相同属性的两个对象
o === p // => false: 两个单独的对象永不相等
var a = [], b =[]; // 两个单独的空数组
a === b // => false: 两个单独的数组永不相等
var h = { hzh1: "黄子涵" };
var H = { hzh1: "黄子涵" };
console.log("判断对象h和对象H是否严格相等:");
console.log( h === H );
console.log("");
var z = [];
var Z = [];
console.log("判断数组z和数组Z是否严格相等:");
console.log( z === Z );
[Running] node "e:\HMV\JavaScript\JavaScript.js"
判断对象h和对象H是否严格相等:
false
判断数组z和数组Z是否严格相等:
false
[Done] exited with code=0 in 0.173 seconds
我们通常将对象称为引用类型(reference type),以此来和JavaScript的基本类型区分开来。依照术语的叫法,对象值都是引用(reference),对象的比较均是引用的比较:当且仅当它们引用同一个基对象时,它们才相等。
var a = []; // 定义一个引用空数组的变量a
var b = a; // 变量b引用同一个数组
b[0] = 1; // 通过变量b来修改引用的数组
a[0] // => 1: 变量a也会修改
a === b // => true:a和b引用同一个数组,因此它们相等
var a = []; // 定义一个引用空数组的变量a
console.log("输出a:");
console.log(a);
console.log("");
var b = a; // 变量b引用同一个数组
console.log("输出b:");
console.log(b);
b[0] = 1; // 通过变量b来修改引用的数组
console.log("");
console.log("看看a[0]有没有被修改:");
console.log(a[0]);
console.log("");
console.log("判断a和b是否相等:");
console.log(a === b);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
输出a:
[]
输出b:
[]
看看a[0]有没有被修改:
1
判断a和b是否相等:
true
[Done] exited with code=0 in 0.2 seconds
就像你刚看到的如上代码,将对象(或数组)赋值给一个变量,仅仅是赋值的引用值:对象本身并没有复制一次。如果你想得到一个对象或数组的副本,则必须显式复制对象的每个属性或数组的每个元素。下面这个例子则是通过循环来完成数组复制:
var a = ['a','b','c']; //待复制的数组
var b = []; //复制到目标空数组
for(var i = 0; i < a.length; i++) { //遍历a[]中的每个元素
b[i] = a[i]; //将元素值复制到b中
}
var a = ['a','b','c']; //待复制的数组
console.log("输出数组a:");
console.log(a);
console.log("");
var b = []; //复制到目标空数组
for(var i = 0; i < a.length; i++) { //遍历a[]中的每个元素
b[i] = a[i]; //将元素值复制到b中
}
console.log("输出数组b:");
console.log(b);
console.log("");
console.log("判断数组a和数组b是否相等:");
console.log(a == b);
console.log("");
console.log("判断数组a和数组b是否严格相等:");
console.log(a === b);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
输出数组a:
[ 'a', 'b', 'c' ]
输出数组b:
[ 'a', 'b', 'c' ]
判断数组a和数组b是否相等:
false
判断数组a和数组b是否严格相等:
false
[Done] exited with code=0 in 0.193 seconds
同样的,如果我们想比较两个单独的对象或者数组,则必须比较它们的属性或元素。下面这段代码定义了一个比较两个数组的函数:
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; //否则它们相等
}
var hzh1 = ["黄", "子", "涵"];
var hzh2 = ["黄", "子", "涵"];
var hzh3 = ["黄", "春", "钦"];
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; //否则它们相等
}
console.log("判断hzh1和hzh2是否相等:");
console.log(equalArrays(hzh1, hzh2));
console.log("");
console.log("判断hzh1和hzh2是否相等:");
console.log(equalArrays(hzh1, hzh3));
[Running] node "e:\HMV\JavaScript\JavaScript.js"
判断hzh1和hzh2是否相等:
true
判断hzh1和hzh2是否相等:
false
[Done] exited with code=0 in 0.176 seconds