淺拷貝:複製一層對象的屬性,並不包括對象裏面的爲引用類型的數據,當改變拷貝的對象裏面的引用類型時,源對象也會改變。javascript
深拷貝:從新開闢一個內存空間,須要遞歸拷貝對象裏的引用,直到子屬性都爲基本類型。兩個對象對應兩個不一樣的地址,修改一個對象的屬性,不會改變另外一個對象的屬性。html
基本類型:undefined null number boolen string symbol 變量是直接按值存放的,存放在棧內存中的簡單數據段,能夠直接訪問前端
引用類型:Object, Array,Function,Date,存放在堆內存中的對象,變量保存的是一個指針,這個指針指向另外一個位置。當須要訪問引用類型(如對象,數組等)的值時,首先從棧中得到該對象的地址指針,而後再從堆內存中取得所需的數據。java
基本類型和引用類型的區別:1:內存空間(棧內存和堆內存) 2:對值的操做 3:變量的複製jquery
JavaScript存儲對象都是存地址的,因此淺拷貝會致使 obj1 和obj2 指向同一塊內存地址。改變了其中一方的內容,都是在原來的內存上作修改會致使拷貝對象和源對象都發生改變,而深拷貝是開闢一塊新的內存地址,將原對象的各個屬性逐個複製進去。對拷貝對象和源對象各自的操做互不影響。正則表達式
eg: 數組的拷貝 json
// 淺拷貝,指向的是同一片內存 let arr1 = [1,2,3,4]; let arr2 = arr1; arr2[0] = 5; console.log(arr1); // [5,2,3,4] console.log(arr2); // [5,2,3,4]
深複製和淺複製只針對像 Object, Array,Date 這樣的複雜對象的。簡單來講,淺複製只複製一層對象的屬性,而深複製則遞歸複製了全部層級。數組
function copy(obj) { var newObj = {}; for (let key in obj) { newObj[key] = obj[key]; } return newObj; } var obj = {name: 'TOM', arr: [1,2,3]}; var end = copy(obj);
由於淺複製只會將對象的各個屬性進行依次複製,並不會進行遞歸複製,而 JavaScript 存儲對象都是存地址的,因此淺複製會致使 obj.arr 和 end.arr 指向同一塊內存地址瀏覽器
end.arr[0] = 5; console.log(obj.arr); // [5,2,3] console.log(end.arr); // [5,2,3]
所以修改了end對象 可是obj對象也跟着變了函數
function copy(copyObj) { var obj = {}; for ( var i in copyObj) { obj[i] = copyObj[i]; } return obj; } var x = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ] }; var y = copy(x); console.log(y.b.f === x.b.f); // true
Object.assign() 方法能夠把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,而後返回目標對象。
var x = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ] }; var y = Object.assign({}, x); console.log(y.b.f === x.b.f); // true
Array的slice和concat方法不修改原數組,只會返回一個淺複製了原數組中的元素的一個新數組。之因此把它放在深拷貝里,是由於它看起來像是深拷貝。而實際上它是淺拷貝。原數組的元素會按照下述規則拷貝:
若是向兩個數組任一中添加了新元素,則另外一個不會受到影響。例子以下:
var array = [1,2,3]; var array_shallow = array; var array_concat = array.concat(); var array_slice = array.slice(0); console.log(array === array_shallow); //true console.log(array === array_slice); //false,「看起來」像深拷貝 console.log(array === array_concat); //false,「看起來」像深拷貝
能夠看出,concat和slice返回的不一樣的數組實例,這與直接的引用複製是不一樣的。而從另外一個例子能夠看出Array的concat和slice並非真正的深複製,數組中的對象元素(Object,Array等)只是複製了引用。以下:
//code from http://caibaojian.com/javascript-object-clone.html#comments var array = [1, [1,2,3], {name:"array"}]; var array_concat = array.concat(); var array_slice = array.slice(0); array_concat[1][0] = 5; //改變array_concat中數組元素的值 console.log(array[1]); //[5,2,3] console.log(array_slice[1]); //[5,2,3] array_slice[2].name = "array_slice"; //改變array_slice中對象元素的值 console.log(array[2].name); //array_slice console.log(array_concat[2].name); //array_slice
JSON對象是ES5中引入的新的類型(支持的瀏覽器爲IE8+),JSON對象parse方法能夠將JSON字符串反序列化成JS對象,stringify方法能夠將JS對象序列化成JSON字符串,藉助這兩個方法,也能夠實現對象的深拷貝。
var source = { name:"source", child:{ name:"child" } } var target = JSON.parse(JSON.stringify(source));
這種方法使用較爲簡單,能夠知足基本的深拷貝需求,並且可以處理JSON格式能表示的全部數據類型,可是對於正則表達式類型、函數類型等沒法進行深拷貝(並且會直接丟失相應的值)。還有一點很差的地方是它會拋棄對象的constructor。也就是深拷貝以後,無論這個對象原來的構造函數是什麼,在深拷貝以後都會變成Object。同時若是對象中存在循環引用的狀況也沒法正確處理。
Object.prototype.clone = function () { var o = {}; for (var i in this) { o[i] = this[i]; } return o; }; Array.prototype.clone = function () { var arr = []; for (var i = 0; i < this.length; i++) if (typeof this[i] !== 'object') { arr.push(this[i]); } else { arr.push(this[i].clone()); } return arr; }; //測試1 Object var obj1 = { name: 'Rattz', age: 20, hello: function () { return "I'm " + name; } }; var obj2 = obj1.clone(); obj2.age++; console.log(obj1.age); //測試2 Array var fun = function(log) {console.log}, arr1 = [1, 2, [3, 4], {a: 5, b: 6}, fun], arr2 = arr1.clone(); console.log(arr1, arr2); arr2[2][1]= 'Mike'; arr2[3].a = 50; arr2[4] = 10; console.log(arr1, arr2);
var arr1 = [1, 2, [3, 4], {a: 5, b: 6}, 7], arr2 = $.extend(true, [], arr1); console.log(arr1, arr2); arr2[1] = 10; console.log(arr1, arr2);
來源:前端開發博客