1、JS中的變量類型
1)基本類型
Number、Boolean、String、undefined、Null。變量是直接按值存放的,存放在棧內(nèi)存中的簡單數(shù)據(jù)段,可以直接訪問。
2)引用類型
存放在堆內(nèi)存中的對象,變量保存的是一個(gè)指針,這個(gè)指針指向另一個(gè)位置。當(dāng)需要訪問引用類型(如對象,數(shù)組等)的值時(shí),首先從棧中獲得該對象的地址指針,然后再從堆內(nèi)存中取得所需的數(shù)據(jù)。
理解:
// 簡單類型都放在棧(圖左)(stack)里
// 引用類型都放在堆(圖右)(heap)里
var a = 20;
var b = 'abc';
var c = true;
var d = {m: 20} // 地址假設(shè)為0x0012ff7c

//淺拷貝,雙向改變,指向同一片內(nèi)存空間
var arr1 = [1, 2, 3];
var arr2 = arr1;
arr1[0] = 'change';
console.log('shallow copy: ' + arr1 + " ); //shallow copy: change,2,3
console.log('shallow copy: ' + arr2 + " ); //shallow copy: change,2,3
2、淺拷貝的實(shí)現(xiàn)
1)引用復(fù)制
function shallowClone(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 = shallowClone(x);
console.log(y.b.f === x.b.f); // true
2)Object.assign()
Object.assign() 方法可以把任意多個(gè)的源對象自身的可枚舉屬性拷貝給目標(biāo)對象,然后返回目標(biāo)對象。
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
3、深拷貝的實(shí)現(xiàn)
1)Array的slice和concat方法
Array的slice和concat方法不修改原數(shù)組,只會(huì)返回一個(gè)淺復(fù)制了原數(shù)組中的元素的一個(gè)新數(shù)組。之所以把它放在深拷貝里,是因?yàn)樗雌饋硐袷巧羁截悺6鴮?shí)際上它是淺拷貝。原數(shù)組的元素會(huì)按照下述規(guī)則拷貝:
- 如果該元素是個(gè)對象引用 (不是實(shí)際的對象),slice 會(huì)拷貝這個(gè)對象引用到新的數(shù)組里。兩個(gè)對象引用都引用了同一個(gè)對象。如果被引用的對象發(fā)生改變,則新的和原來的數(shù)組中的這個(gè)元素也會(huì)發(fā)生改變。
- 對于字符串、數(shù)字及布爾值來說(不是 String、Number 或者 Boolean 對象),slice 會(huì)拷貝這些值到新的數(shù)組里。在別的數(shù)組里修改這些字符串或數(shù)字或是布爾值,將不會(huì)影響另一個(gè)數(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,“看起來”像深拷貝
console.log(array === array_concat); //false,“看起來”像深拷貝
但是Array的concat和slice并不是真正的深復(fù)制,數(shù)組中的對象元素(Object,Array等)只是復(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中對象元素的值
console.log(array[2].name); //array_slice
console.log(array_concat[2].name); //array_slice
2)JSON對象的parse和stringify
JSON對象是ES5中引入的新的類型(支持的瀏覽器為IE8+),JSON對象parse方法可以將JSON字符串反序列化成JS對象,stringify方法可以將JS對象序列化成JSON字符串,借助這兩個(gè)方法,也可以實(shí)現(xiàn)對象的深拷貝。
//例1
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
//例2
var source = { name:function(){console.log(1);}, child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
//例3
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 {}
這種方法使用較為簡單,可以滿足基本的深拷貝需求,而且能夠處理JSON格式能表示的所有數(shù)據(jù)類型,但是對于正則表達(dá)式類型、函數(shù)類型等無法進(jìn)行深拷貝(而且會(huì)直接丟失相應(yīng)的值)。還有一點(diǎn)不好的地方是它會(huì)拋棄對象的constructor。也就是深拷貝之后,不管這個(gè)對象原來的構(gòu)造函數(shù)是什么,在深拷貝之后都會(huì)變成Object。同時(shí)如果對象中存在循環(huán)引用的情況也無法正確處理。
3)jQuery.extend()方法實(shí)現(xiàn)
- 合并 object1 ... objectN 到 target 對象,如果只有一個(gè)參數(shù),則該 target 對象會(huì)被合并到 jQuery 對象中。
//jQuery.extend(target [, object1] [, objectN])
var obj1 = { name: 'Tom', age: 21}
var obj2 = {name: 'Jerry', sex: 'boy'}
$.extend(obj1, obj2); // {name: "Jerry", age: 21, sex: "boy"}
obj1 // {name: "Jerry", age: 21, sex: "boy"}
obj2 // {name: "Jerry", sex: "boy"}
- 上述代碼展示的是將 obj2 對象合并到 obj1 對象中,這種方法會(huì) 改變 obj1 對象的結(jié)構(gòu)。如果你 不想改變 合并目標(biāo)對象的結(jié)構(gòu),你可以這么做。
var obj1 = { name: 'Tom', age: 21}
var obj2 = { name: 'Jerry', sex: 'boy'}
$.extend({}, obj1, obj2); // { name: "Jerry", age: 21, sex: "boy" }
obj1 // { name: "Tom", age: 21 }
obj2 // { name: "Jerry", sex: "boy" }
參考鏈接