JavaScript对象引用和复制
对象引用和复制
与原始类型相比,对象的根本区别之一是对象是“通过引用”被存储和复制的,与原始类型值相反:字符串,数字,布尔值等 —— 始终是以“整体值”的形式被复制的。
如果我们稍微看一下复制值时发生了什么,就很容易理解了。
让我们从原始类型开始,例如一个字符串。
这里我们将 message 复制到 phrase:
let message = "Hello!"; let phrase = message;
结果我们就有了两个独立的变量,每个都存储着字符串 "Hello!"。
但是,对象不是这样的。当一个对象变量被复制的时候引用被复制,而该对象并没有被复制。
let user = { name: "John" }; let admin = user; // 这样的操作只会复制引用
现在我们有了两个变量,它们保存的都是对同一个对象的引用:
<!DOCTYPE HTML> <html> <body> <script> let user = { name: 'John' }; let admin = user; admin.name = 'Pete'; // 通过 "admin" 引用来修改 alert(user.name); // 'Pete',修改能通过 "user" 引用看到 user.name='www'; alert(admin.name);//输出'www',通过修改user后,admin也同样改变了 </script> </body> </html>
所以let admin = user;的操作相当于两个变量都有了钥匙,都能修改同一个对象,改了其中一个,另一个也跟着变化了,因为他们都是在同一个位置获取结果。
通过引用来比较
仅当两个对象为同一对象时,两者才相等。
例如,这里 a 和 b 两个变量都引用同一个对象,所以它们相等:
let a = {}; let b = a; // 复制引用 alert( a == b ); // true,都引用同一对象 alert( a === b ); // true
而这里两个独立的对象则并不相等,即使它们看起来很像(都为空):
let a = {}; let b = {}; // 两个独立的对象 alert( a == b ); // false
对于类似 obj1 > obj2 的比较,或者跟一个原始类型值的比较 obj == 5,对象都会被转换为原始值。我们很快就会学到对象是如何转换的,但是说实话,很少需要进行这样的比较,通常是在编程错误的时候才会出现这种情况。
克隆与合并,Object.assign
但是,如果我们想要复制一个对象,那该怎么做呢?我们可以使用 Object.assign 方法来达到目的。
语法是:
Object.assign(dest, [src1, src2, src3...])
第一个参数 dest 是指目标对象。
更后面的参数 src1, ..., srcN(可按需传递多个参数)是源对象。
该方法将所有源对象的属性拷贝到目标对象 dest 中。换句话说,从第二个开始的所有参数的属性都被拷贝到第一个参数的对象中。
调用结果返回 dest。
例如,我们可以用它来合并多个对象:
let user = { name: "John" }; let permissions1 = { canView: true }; let permissions2 = { canEdit: true }; // 将 permissions1 和 permissions2 中的所有属性都拷贝到 user 中 Object.assign(user, permissions1, permissions2); // 现在 user = { name: "John", canView: true, canEdit: true }
如果被拷贝的属性的属性名已经存在,那么它会被覆盖:
let user = { name: "John" }; Object.assign(user, { name: "Pete" }); alert(user.name); // 现在 user = { name: "Pete" }
我们也可以用 Object.assign 进行简单克隆:
let user = { name: "John", age: 30 }; let clone = Object.assign({}, user);
它将 user 中的所有属性拷贝到了一个空对象中,并返回这个新的对象保存在clone中。