js對象淺拷貝和深拷貝詳解

1.淺拷貝

拷貝就是把父對像的屬性,全部拷貝給子對象。

下面這個函數,就是在做拷貝:

var Chinese = {
  nation:'中國'
}
var Doctor = {
  career:'醫生'
}  
function extendCopy(p) {
    var c = {};
    for (var i in p) { 
      c[i] = p[i];
    }
    c.uber = p;
    return c;
 }

使用的時候,這樣寫:

var Doctor = extendCopy(Chinese);
Doctor.career = '醫生';
alert(Doctor.nation); // 中國

但是,這樣的拷貝有一個問題。那就是,如果父對象的屬性等於數組或另一個對象,那麼實際上,子對象獲得的只是一個內存地址,而不是真正拷貝,因此存在父對象被篡改的可能。

請看,現在給Chinese添加一個”出生地”屬性,它的值是一個數組。

Chinese.birthPlaces = ['北京','上海','香港'];

通過extendCopy()函數,Doctor繼承了Chinese。

var Doctor = extendCopy(Chinese);

然後,我們爲Doctor的”出生地”添加一個城市:

Doctor.birthPlaces.push('廈門');

看一下輸入結果

alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門
alert(Chinese.birthPlaces); //北京, 上海, 香港, 廈門

結果是兩個的出生地都被改了。

所以,extendCopy() 只是拷貝了基本類型的數據,我們把這種拷貝叫做“淺拷貝”。

2.深拷貝

因爲淺深拷有如此弊端所以我們接下來看一下深拷貝

所謂”深拷貝”,就是能夠實現真正意義上的數組和對象的拷貝。它的實現並不難,只要遞歸調用”淺拷貝”就行了。

function deepCopy(p, c) {
    var c = c || {};
    for (var i in p) {
      if (typeof p[i] === 'object') {
        c[i] = (p[i].constructor === Array) ? [] : {};
        deepCopy(p[i], c[i]);
      } else {
         c[i] = p[i];
      }
    }
    return c;
  }

看一下使用方法:

var Doctor = deepCopy(Chinese);

現在,給父對象加一個屬性,值爲數組。然後,在子對象上修改這個屬性:

Chinese.birthPlaces = ['北京','上海','香港'];
Doctor.birthPlaces.push('廈門');

alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門
alert(Chinese.birthPlaces); //北京, 上海, 香港

這樣就完成了拷貝;

$.extend()

jQuery 中$.extend()如同。

$.extend( [deep ], target, object1 [, objectN ] )

  • deep 
    類型: Boolean 
    如果是true,合併成爲遞歸(又叫做深拷貝)。
  • target 
    類型: Object 
    對象擴展。這將接收新的屬性。
  • object1 
    類型: Object 
    一個對象,它包含額外的屬性合併到第一個參數.
  • objectN 
    類型: Object 
    包含額外的屬性合併到第一個參數

當我們提供兩個或多個對象給$.extend(),對象的所有屬性都添加到目標對象(target參數)。

如果只有一個參數提供給$.extend(),這意味着目標參數被省略。在這種情況下,jQuery對象本身被默認爲目標對象。這樣,我們可以在jQuery的命名空間下添加新的功能。這對於插件開發者希望向 jQuery 中添加新函數時是很有用的。

請記住,目標對象(第一個參數)將被修改,並且將通過$.extend()返回。然而,如果我們想保留原對象,我們可以通過傳遞一個空對象作爲目標對象:

var object = $.extend({}, object1, object2);

在默認情況下,通過$.extend()合併操作不是遞歸的;如果第一個對象的屬性本身是一個對象或數組,那麼它將完全用第二個對象相同的key重寫一個屬性。這些值不會被合併。可以通過檢查下面例子中 banana 的值,就可以瞭解這一點。然而,如果將 true 作爲該函數的第一個參數,那麼會在對象上進行遞歸的合併。

警告:不支持第一個參數傳遞 false 。

1. 合併兩個對象,並修改第一個對象。

var object1 = {
  apple: 0,
  banana: { weight: 52, price: 100 },
  cherry: 97
};
var object2 = {
  banana: { price: 200 },
  durian: 100
};

// Merge object2 into object1
$.extend( object1, object2 );

// Assuming JSON.stringify - not available in IE<8
console.log( JSON.stringify( object1 ) );
//{"apple":0,"banana":{"price":200},"cherry":97,"durian":100}

2. 採用遞歸方式合併兩個對象,並修改第一個對象。

var object1 = {
  apple: 0,
  banana: { weight: 52, price: 100 },
  cherry: 97
};
var object2 = {
  banana: { price: 200 },
  durian: 100
};

// Merge object2 into object1, recursively
$.extend( true, object1, object2 );

// Assuming JSON.stringify - not available in IE<8
console.log( JSON.stringify( object1 ) );
//{"apple":0,"banana":{"weight":52,"price":200},"cherry":97,"durian":100}

3. 合併 defaults 和 options 對象,並且不修改 defaults 對象。這是常用的插件開發模式。

var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };

// Merge defaults and options, without modifying defaults
var settings = $.extend( {}, defaults, options );


console.log(JSON.stringify( defaults ));
console.log(JSON.stringify( options  ));
console.log(JSON.stringify( settings  ));
//defaults -- {"validate":false,"limit":5,"name":"foo"}
//options -- {"validate":true,"name":"bar"}
//settings -- {"validate":true,"limit":5,"name":"bar"}

Javascript 判斷對象是否相等

JavaScript中相等運算包括”==”,”===”全等,兩者不同之處,不必多數,本篇文章我們將來講述如何判斷兩個對象是否相等? 你可能會認爲,如果兩個對象有相同的屬性,以及它們的屬性有相同的值,那麼這兩個對象就相等。那麼下面我們通過一個實例來論證下:

var obj1 = {
    name: "Benjamin",
    sex : "male"
}

var obj2 = {
    name: "Benjamin",
    sex : "male"
}

//Outputs: false
console.log(obj1 == obj2);

//Outputs: false
console.log(obj1 === obj2);

通過上面的例子可以看到,無論使用”==”還是”===”,都返回false。主要原因是基本類型string,number通過值來比較,而對象(Date,Array)及普通對象通過指針指向的內存中的地址來做比較。看下面一個例子:

var obj1 = {
    name: "Benjamin",
    sex : "male"
};

var obj2 = {
    name: "Benjamin",
    sex : "male"
};

var obj3 = obj1;

//Outputs: true
console.log(obj1 == obj3);

//Outputs: true
console.log(obj1 === obj3);

//Outputs: false
console.log(obj2 == obj3);

//Outputs: false
console.log(obj2 === obj3);

上例返回true,是因爲obj1和ob3的指針指向了內存中的同一個地址。和麪向對象的語言(Java/C++)中值傳遞和引用傳遞的概念相似。 因爲,如果你想判斷兩個對象是否相等,你必須清晰,你是想判斷兩個對象的屬性是否相同,還是屬性對應的值是否相同,還是怎樣?

function person(name) {  
    this.name=name;  
}  

var p1 = new person("p1");  
var p2 = new person("p2");  

console.log(p1 == p2); //false  

person.prototype.sayHi = function() {  
    // do sayHi here  
}  

console.log(p1.sayHi() == p2.sayHi()); //true  
console.log(p1.sayHi() === p2.sayHi()); //true

一、數組的深淺拷貝

在使用JavaScript對數組進行操作的時候,我們經常需要將數組進行備份,事實證明如果只是簡單的將它賦予其他變量,那麼我們只要更改其中的任何一個,然後其他的也會跟着改變,這就導致了問題的發生。

var arr = ["One","Two","Three"];

var arrto = arr;
arrto[1] = "test";
document.writeln("數組的原始值:" + arr + "<br />");//Export:數組的原始值:One,test,Three
document.writeln("數組的新值:" + arrto + "<br />");//Export:數組的新值:One,test,Three

像上面的這種直接賦值的方式就是淺拷貝,很多時候,這樣並不是我們想要得到的結果,其實我們想要的是arr的值不變,不是嗎? 
方法一:js的slice函數 

複製代碼

對於array對象的slice函數,
返回一個數組的一段。(仍爲數組)
arrayObj.slice(start, [end])
參數
arrayObj
必選項。一個 Array 對象。
start
必選項。arrayObj 中所指定的部分的開始元素是從零開始計算的下標。
end
可選項。arrayObj 中所指定的部分的結束元素是從零開始計算的下標。
說明
slice 方法返回一個 Array 對象,其中包含了 arrayObj 的指定部分。
slice 方法一直複製到 end 所指定的元素,但是不包括該元素。如果 start 爲負,將它作爲 length + start處理,此處 length 爲數組的長度。如果 end 爲負,就將它作爲 length + end 處理,此處 length 爲數組的長度。如果省略 end ,那麼 slice 方法將一直複製到 arrayObj 的結尾。如果 end 出現在 start 之前,不復制任何元素到新數組中。

複製代碼

 

var arr = ["One","Two","Three"];

var arrtoo = arr.slice(0);
arrtoo[1] = "set Map";
document.writeln("數組的原始值:" + arr + "<br />");//Export:數組的原始值:One,Two,Three
document.writeln("數組的新值:" + arrtoo + "<br />");//Export:數組的新值:One,set Map,Three

 

方法二:js的concat方法 

concat() 方法用於連接兩個或多個數組。
該方法不會改變現有的數組,而僅僅會返回被連接數組的一個副本。
語法
arrayObject.concat(arrayX,arrayX,......,arrayX)
說明
返回一個新的數組。該數組是通過把所有 arrayX 參數添加到 arrayObject 中生成的。如果要進行 concat() 操作的參數是數組,那麼添加的是數組中的元素,而不是數組。

 

var arr = ["One","Two","Three"];

var arrtooo = arr.concat();
arrtooo[1] = "set Map To";
document.writeln("數組的原始值:" + arr + "<br />");//Export:數組的原始值:One,Two,Three
document.writeln("數組的新值:" + arrtooo + "<br />");//Export:數組的新值:One,set Map To,Three

 

二、對象的深淺拷貝

複製代碼

var a={name:'yy',age:26};
var b=new Object();


b.name=a.name;
b.age=a.age;
a.name='xx';
console.log(b);//Object { name="yy", age=26}
console.log(a);//Object { name="xx", age=26}

複製代碼

就是把對象的屬性遍歷一遍,賦給一個新的對象。

複製代碼

var deepCopy= function(source) { 
var result={};
for (var key in source) {
      result[key] = typeof source[key]===’object’? deepCoyp(source[key]): source[key];
   } 
   return result; 
}

 

這個函數可以深拷貝 對象和數組

var cloneObj = function(obj){

var str, newobj = obj.constructor === Array ? [] : {};

if(typeof obj !== 'object'){

return;

} else if(window.JSON){

str = JSON.stringify(obj), //系列化對象

newobj = JSON.parse(str); //還原

} else {

for(var i in obj){

newobj[i] = typeof obj[i] === 'object' ? cloneObj(obj[i]) : obj[i];

}

}

return newobj;

};

 


 

 
發佈了38 篇原創文章 · 獲贊 139 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章