一、js 數據類型
javaScritp的數據類型有:數值類型、字符串類型、布爾類型、null、undefined、對象(數組、正則表達式、日期、函數),大致分成兩種:基本數據類型和引用數據類型,
其中:
(1)基本數據類型:數值、字符串、布爾、null、undefined (值類型)
(2)復雜(復合)數據類型:對象 (引用類型)
基本數據類型保存在棧內存,引用類型保存在堆內存中。根本原因在於保存在棧內存的必須是大小固定的數據,引用類型的大小不固定,只能保存在堆內存中,但是可以把它的地址寫在棧內存中以供我們訪問
如果是基本數據類型,則按值訪問,操作的就是變量保存的值;如果是引用類型的值,我們只是通過保存在變量中的引用類型的地址來操作實際對象
舉例:
var a = 1;//定義了一個number類型 var obj1 = {//定義了一個object類型 name:'obj' };

1、基本類型的復制
var a = 1; var b = a;//復制 console.log(b)//1 a = 2;//改變a的值 console.log(b)//1
賦值的時候,在棧內存中重新開辟內存,存放變量b,所以在棧內存中分別存放着變量a、b各自的值,修改時互不影響
2、引用類型的復制
var color1 = ['red','green']; var color2 = color1;//復制 console.log(color2)//['red','green']; color1.push('black') ;//改變color1的值 console.log(color2)//['red','green','black']
color1與color2指向堆內存中同一地址的同一對象,復制的只是引用地址

因此,對於引用類型的復制,簡單賦值無用,需要拷貝。拷貝存在兩種類型:深拷貝與淺拷貝
二、深淺拷貝
淺拷貝只復制指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存。但深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象
1、淺拷貝
淺拷貝只是拷貝基本類型的數據,如果父對象的屬性等於數組或另一個對象,那么實際上,子對象獲得的只是一個內存地址,因此存在父對象被篡改的可能,淺拷貝只復制指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存
var Nation = { nation: '中國' };function extendCopy(p) { var c = {}; for (var i in p) { c[i] = p[i]; } return c; } var Doctor = extendCopy(Nation); Doctor.career = '醫生'; Doctor.nation = '美國'; console.log(Doctor.nation); // 美國 console.log(Nation.nation); // 中國 console.log(Doctor.career); // 醫生 console.log(Doctor.__proto_ === Nation.__proto_) // true
// 這里涉及到使用拷貝父對象的屬性實現繼承
var obj = { a: "hello", b:{ a: "world", b: 21 }, c:["Bob", "Tom", "Jenny"], d:function() { alert("hello world"); } } var obj1 = simpleClone(obj); console.log('obj1=>>>',obj1); // 1、 obj1.c = ['mm', "Tom", "Jenny"]; // 一層,作為整體,重寫,全改變;改變屬性值,不改變原對象 console.log('obj=>>>',obj); //obj.c => ["Bob", "Tom", "Jenny"]
// 2、
obj1.c[0] = 'mm'; // 淺拷貝時,改變屬性的屬性值,改變原對象
console.log('obj=>>>',obj); //obj.c => ["mm", "Tom", "Jenny"]
淺拷貝函數:
function simpleClone(initalObj) { var obj = {}; for ( var i in initalObj) { obj[i] = initalObj[i]; } return obj; }
2、深拷貝
深拷貝就是能夠實現真正意義上的數組和對象的拷貝。遞歸調用"淺拷貝"。(深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象)
深拷貝函數:
寫法一: function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用對象導致死循環,如initalObj.a = initalObj的情況 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : {}; arguments.callee(prop, obj[i]); } else { obj[i] = prop; } } return obj; } 寫法二: function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用對象導致死循環,如initalObj.a = initalObj的情況 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop); } else { obj[i] = prop; } } return obj; }
三、深拷貝的應用實例
// jquery 有提供一個$.extend可以用來做 Deep Copy。 var $ = require('jquery'); var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; var obj2 = $.extend(true, {}, obj1); console.log(obj1.b.f === obj2.b.f); // false // 函數庫lodash,有提供_.cloneDeep用來做 Deep Copy。 var _ = require('lodash'); var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; var obj2 = _.cloneDeep(obj1); console.log(obj1.b.f === obj2.b.f); // false
