黄子涵

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
posted @ 2022-05-25 10:14  黄子涵  阅读(27)  评论(0编辑  收藏  举报