javaScript的變量類(lèi)型
基本類(lèi)型:
5種基本數(shù)據(jù)類(lèi)型Undefined、Null、Boolean、Number 和 String,變量是直接按值存放的,存放在棧內(nèi)存中的簡(jiǎn)單數(shù)據(jù)段,可以直接訪問(wèn)。引用類(lèi)型:
存放在堆內(nèi)存中的對(duì)象,變量保存的是一個(gè)指針,這個(gè)指針指向另一個(gè)位置。當(dāng)需要訪問(wèn)引用類(lèi)型(如對(duì)象,數(shù)組等)的值時(shí),首先從棧中獲得該對(duì)象的地址指針,然后再?gòu)亩褍?nèi)存中取得所需的數(shù)據(jù)。
JavaScript存儲(chǔ)對(duì)象都是存地址的,所以淺拷貝會(huì)導(dǎo)致obj1 和obj2 指向同一塊內(nèi)存地址。改變了其中一方的內(nèi)容,都是在原來(lái)的內(nèi)存上做修改會(huì)導(dǎo)致拷貝對(duì)象和源對(duì)象都發(fā)生改變;而深拷貝是開(kāi)辟一塊新的內(nèi)存地址,將原對(duì)象的各個(gè)屬性逐個(gè)復(fù)制進(jìn)去。對(duì)拷貝對(duì)象和源對(duì)象各自的操作互不影響。
淺復(fù)制的實(shí)現(xiàn)
- 簡(jiǎn)單的引用復(fù)制
var data = {a: 1, b: 2};
var c = data;
- Object.assign()
只會(huì)對(duì)第一層變量實(shí)現(xiàn)深復(fù)制;var data = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ] }; var objData = Object.assign({}, data); console.log(objData === data); //false console.log(objData.b.f === data.b.f); //true
-
Array的slice和concat方法(與Object.assign()方法是一樣的)
Array的slice和concat方法不修改原數(shù)組,只會(huì)返回一個(gè)淺復(fù)制了原數(shù)組中的元素的一個(gè)新數(shù)組。它看起來(lái)像是深拷貝,而實(shí)際上它是淺拷貝。
例子如下: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,“看起來(lái)”像深拷貝 console.log(array === array_concat); //false,“看起來(lái)”像深拷貝可以看出,
concat和slice返回的不同的數(shù)組實(shí)例,這與直接的引用復(fù)制是不同的。
原數(shù)組的元素會(huì)按照下述規(guī)則拷貝:
-
如果該元素是個(gè)對(duì)象引用 (不是實(shí)際的對(duì)象),
slice會(huì)拷貝這個(gè)對(duì)象引用到新的數(shù)組里。兩個(gè)對(duì)象引用都引用了同一個(gè)對(duì)象。如果被引用的對(duì)象發(fā)生改變,則新的和原來(lái)的數(shù)組中的這個(gè)元素也會(huì)發(fā)生改變。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中數(shù)組元素的值 console.log(array[1]); //[5,2,3] console.log(array_slice[1]); //[5,2,3] array_slice[2].name = "array_slice"; //改變array_slice中對(duì)象元素的值 console.log(array[2].name); //array_slice console.log(array_concat[2].name); //array_slice可以看出Array的
concat和slice并不是真正的深復(fù)制,數(shù)組中的對(duì)象元素(Object,Array等)只是復(fù)制了引用。 對(duì)于字符串、數(shù)字及布爾值來(lái)說(shuō)(不是
String、Number或者Boolean對(duì)象),slice會(huì)拷貝這些值到新的數(shù)組里。在別的數(shù)組里修改這些字符串或數(shù)字或是布爾值,將不會(huì)影響另一個(gè)數(shù)組。-
如果向兩個(gè)數(shù)組任一中添加了新元素,則另一個(gè)不會(huì)受到影響。
var array = [1, 2, 3]; var array_shallow = array; var array_concat = array.concat(); var array_slice = array.slice(0); array.push(4); console.log(array_concat); //[1, 2, 3]
深復(fù)制的實(shí)現(xiàn)
JSON對(duì)象的parse和stringify
JSON對(duì)象是ES5中引入的新的類(lèi)型(支持的瀏覽器為IE8+),JSON對(duì)象parse方法可以將JSON字符串反序列化成JS對(duì)象,stringify方法可以將JS對(duì)象序列化成JSON字符串,借助這兩個(gè)方法,也可以實(shí)現(xiàn)對(duì)象的深拷貝。
var source = { name:"source", child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
target.name = "target"; //改變target的name屬性
console.log(source.name); //source
console.log(target.name); //target
target.child.name = "target child"; //改變target的child
console.log(source.child.name); //child
console.log(target.child.name); //target child
var source = { name:function(){console.log(1);}, child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
var source = { name:function(){console.log(1);}, child:new RegExp("e") }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
console.log(target.child); //Object {}
這種方法使用較為簡(jiǎn)單,可以滿(mǎn)足基本的深拷貝需求,而且能夠處理JSON格式能表示的所有數(shù)據(jù)類(lèi)型,但是對(duì)于正則表達(dá)式類(lèi)型、函數(shù)類(lèi)型等無(wú)法進(jìn)行深拷貝(而且會(huì)直接丟失相應(yīng)的值)。
還有一點(diǎn)不好的地方是它會(huì)拋棄對(duì)象的constructor。也就是深拷貝之后,不管這個(gè)對(duì)象原來(lái)的構(gòu)造函數(shù)是什么,在深拷貝之后都會(huì)變成Object。
如果對(duì)象中存在循環(huán)引用的情況也無(wú)法正確處理。
自定義方法實(shí)現(xiàn)深拷貝
function clone(data) {
if (Array.isArray(data)) {
let newArr = [];
for (let i = 0; i < data.length; i++) {
newArr[i] = clone(data[i]);
}
return newArr;
} else if (data instanceof Object) {
let obj = {};
for (let key in data) {
obj[key] = clone(data[[key]]);
}
return obj;
}else {
return data;
}
}
var data = {
a: 1,
b: { f: { g: 1 } },
c: [ 1, 2, 3 ]
};
var objData = clone(data);
console.log(objData === data); //false
console.log(objdata.b.f === data.b.f); //false