文章開始以前,讓咱們先思考一下這幾個問題:前端
好了,問題出來了,那麼下面就讓咱們帶着這幾個問題去探究一下吧!git
若是文章中有出現紕漏、錯誤之處,還請看到的小夥伴多多指教,先行謝過github
如下↓web
在開始瞭解 淺拷貝
與 深拷貝
以前,讓咱們先來回顧一下 JavaScript
的數據類型(能夠參考這裏 JavaScript中的數據類型)segmentfault
在 JavaScript
中,咱們將數據分爲 基本數據類型(原始值)
與 引用類型
框架
因爲數據類型的訪問方式不一樣,它們的比較方式也是不同的函數
var a = 100;
var b = 100;
a === b // true
var c = {a: 1, b: 2};
var d = {a: 1, b: 2};
c == d // false 兩個不一樣的對象
複製代碼
鑑於以上數據類型的特色,咱們能夠初步想到:所謂 淺拷貝
與 深拷貝
可能就是對於值的拷貝和引用的拷貝(簡單數據類型都是對值的拷貝,不進行區分)性能
通常來講,咱們所涉及的拷貝對象,也都是針對引用類型的。因爲引用類型屬性層級可能也會有多層,這樣也就引出了咱們所要去了解的 淺拷貝
與 深拷貝
學習
顧名思義,所謂淺拷貝就是對對象進行淺層次的複製,只複製一層對象的屬性,並不包括對象裏面的引用類型數據測試
想象一下,若是讓你本身去實現這個功能,又會有怎麼的思路呢
首先,咱們須要知道被拷貝對象有哪些屬性吧,而後還須要知道這些屬性都對應了那些值或者地址的引用吧。那麼,答案已經呼之欲出了,是的,循環
var person = {
name: 'tt',
age: 18,
friends: ['oo', 'cc', 'yy']
}
function shallowCopy(source) {
if (!source || typeof source !== 'object') {
throw new Error('error');
}
var targetObj = source.constructor === Array ? [] : {};
for (var keys in source) {
if (source.hasOwnProperty(keys)) {
targetObj[keys] = source[keys];
}
}
return targetObj;
}
var p1 = shallowCopy(person);
console.log(p1)
複製代碼
在上面的代碼中,咱們建立了一個 shallowCopy
函數,它接收一個參數也就是被拷貝的對象。
for...in
循環傳進去的對象,爲了不循環到原型上面會被遍歷到的屬性,使用 hasOwnProperty
限制循環只在對象自身,將被拷貝對象的每個屬性和值添加到建立的對象當中經過測試,咱們拿到了和 person
對象幾乎一致的對象 p1
。看到這裏,你是否是會想那這個結果和 var p1 = person
這樣的賦值操做又有什麼區別呢?
咱們再來測試一波
var p2 = person;
// 這個時候咱們修改person對象的數據
person.name = 'tadpole';
person.age = 19;
person.friends.push('tt')
p2.name // tadpole
p2.age // 19
p2.friends // ["oo", "cc", "yy", "tt"]
p1.name // tt
p1.age // 18
p1.friends // ["oo", "cc", "yy", "tt"]
複製代碼
上面咱們建立了一個新的變量 p2
,將 person
賦值給 p2
,而後比較兩個變量
-- | 和原數據是否指向同一對象 | 第一層數據爲基本數據類型 | 原數據中包含子對象 |
---|---|---|---|
賦值 | 是 | 改變會使原數據一同改變 | 改變會使原數據一同改變 |
淺拷貝 | 否 | 改變不會使原數據一同改變 | 改變會使原數據一同改變 |
瞭解完淺拷貝,相信小夥伴們對於深拷貝也應該瞭然於胸了
淺拷貝因爲只是複製一層對象的屬性,當遇到有子對象的狀況時,子對象就會互相影響。因此,深拷貝是對對象以及對象的全部子對象進行拷貝
實現方式就是遞歸調用淺拷貝
function deepCopy(source){
if(!source || typeof source !== 'object'){
throw new Error('error');
}
var targetObj = source.constructor === Array ? [] : {};
for(var keys in source){
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepCopy(source[keys]);
}else{
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
var obj1 = {
arr: [1, 2, 3],
key: {
id: 22
},
func: function() {
console.log(123)
}
}
var obj2 = deepCopy(obj1);
obj1.arr.push(4);
obj1.arr // [1, 2, 3, 4]
obj2.arr // [1, 2, 3]
obj1.key === obj2.key // false
obj1.func === obj2.func // true
複製代碼
對於深拷貝的對象,改變源對象不會對獲得的對象有影響。只是在拷貝的過程當中源對象的方法丟失了,這是由於在序列化 JavaScript
對象時,全部函數和原型成員會被有意忽略
還有一種實現深拷貝的方式是利用 JSON
對象中的 parse
和 stringify
,JOSN
對象中的 stringify
能夠把一個 js
對象序列化爲一個 JSON
字符串,parse
能夠把 JSON
字符串反序列化爲一個 js
對象,經過這兩個方法,也能夠實現對象的深複製
// 利用JSON序列化實現一個深拷貝
function deepCopy(source){
return JSON.parse(JSON.stringify(source));
}
var o1 = {
arr: [1, 2, 3],
obj: {
key: 'value'
},
func: function(){
return 1;
}
};
var o2 = deepCopy(o1);
console.log(o2); // => {arr: [1,2,3], obj: {key: 'value'}}
複製代碼
Array.prototype.slice()
Array.prototype.concat()
Object.assign
...
不少框架或者庫都提供了深拷貝的方式,好比 jQuery
、 lodash
函數庫等等,基本實現方式也就和咱們前面介紹的大同小異
根據需求的不一樣,好比有時候咱們須要一個全新的對象,在修改它的時候不去影響到源對象,那麼這個時候咱們就可能須要深拷貝;反之,淺拷貝就能實現咱們的需求
只是,咱們須要注意到一點,那就是由於實現深拷貝使用遞歸的方式,就增長了性能的消耗
相信在不斷使用的過程當中,你必定會對它愈來愈熟悉
最後,推薦一波前端學習歷程,不按期分享一些前端問題和有意思的東西歡迎 star
關注 傳送門