首先,我們還是舉個例子來說明對象賦值操作的問題吧:
ps: 本文默認約定log = console.log
function A(){}
A.prototype.x = 10;
var a1 = new A();
A.prototype = {
x: 20,
y: 20
};
var a2 = new A();
log([a1.x, a1.y, a2.x, a2.y]); // [10, undefined, 20, 20]
js中對象賦值操作我們可以通過c語言中得指針概念來解釋。
對象的淺拷貝
直接通過賦值操作符“=”將變量a中的對象賦值給變量b,此時我們更改a、b其中一個,另一個也會隨之更改。
var a = [1, 1],
b = a;
b[0] = 2;
log(a[0]); // 2
因為我們在將a的對象賦值給b時,js引擎內部的操作只是簡單的將a所指的對象的地址賦值給b,此時a與b指向內存中同一個對象,所以才會出現這種情況。這種賦值方式稱為對象的淺拷貝。
我們可以從另一個方面論證我們的觀點:
log(a === b); // true,毫無疑問
var c = [2, 1];
log(a === c); // false
出現這種情況的原因就是變量c指向的是另一個和變量a指向的對象的值相同的對象,僅僅只是兩個指向的對象的值相同,但兩個對象在內存的地址是不一樣的,所以是false。
另外,我們知道js數組中有一些方法可以實現數組的完全復制,即兩個變量分別指向兩個對象:
b = a.concat([]),
c = a.slice(0),
d = a.splice(0, a.length);
log(b === a); // false
但是我們要針對對象進行復制的話,只能手動進行模擬,即所謂的對象深拷貝。
對象的深拷貝
顧名思義,就是利用“=”對於基本類型的操作不存在上述問題,通過for-in深度遍歷對象的屬性,然后將其賦值給另一個新的對象。
function cloneObj(obj) {
var tempObj = {};
if (obj instanceof Array) {
tempObj = [];
}
for (var prop in obj) {
if (typeof prop === 'Object') {
cloneObj(prop);
}
tempObj[prop] = obj[prop];
}
return tempObj;
}
var myCountry = {
name: 'China',
birth: 1949
},
country = cloneObj(myCountry);
log(country === myCountry); // false
結束語
最后,我們回到第一個例子,會發現,引擎內部對於new對象的原型鏈也是通過簡單的賦值操作的方式,即對象的淺拷貝。而ES5新增的Object.create()
也是淺拷貝的一種封裝:
var myCountry = {
name: 'China',
birth: 1949
},
country = Object.create(myCountry);
console.log(country.__proto__ === myCountry); // true