JS中的兩種數(shù)據(jù)類型及實(shí)現(xiàn)引用類型的深拷貝的方法
一.前言
我們知道,在JS中數(shù)據(jù)類型按照訪問方式和存儲方式的不同可分為基本類型和引用類型。
基本類型
基本類型有String、Boolean、Number,Undefined、Null,這些基本類型都是按值傳遞的,也稱為值類型。
引用類型
引用類型有對象、數(shù)組、函數(shù),它們都是按引用訪問的。
二.存儲方式區(qū)別
基本類型和引用類型由于兩者在內(nèi)存中存儲的方式不同,造成兩者訪問的方式也不同。其中,基本類型存儲在內(nèi)存的棧中,是按值訪問;引用類型存儲在內(nèi)存的堆中,是按引用訪問??扇缦聢D所示:
當(dāng)有
var n1 = 10; var n2 = 10; var arr1 = [1,2,3,4] var arr2 = [1,2,3,4]
其在內(nèi)存中存儲方式如圖:

值類型是在棧中直接保存的變量的實(shí)際值,而引用類型在棧中僅僅保存的是變量指向堆中的地址,從上圖中可以看出,值類型的變量n1和n2都是10,但是在棧中卻為這兩個變量分別開辟了兩塊空間來存儲,而引用類型的變量arr1和arr2也相同,但是在堆中僅僅開辟了一塊內(nèi)存來存儲,而在棧中存儲的是這兩個變量指向堆中的地址,這兩個變量都指向堆中的同一片地址。
三.拷貝區(qū)別
正是由于兩者在存儲方式上的不同,造成了兩者在拷貝時的差異。首先,先看兩段代碼:
var n1 = 10; //將n1拷貝給n2 var n2 = n1; n1 = 12; console.log(n1); console.log(n2);
先定義變量n1=10,然后將n1拷貝給n2,再接著改變n1的值為12,分別打印n1和n2的值,打印結(jié)果為:

從結(jié)果中我們可以看到,n1變?yōu)?2了,但是n2不受影響,依舊是10。
再看另外一段代碼:
var arr1 = [1,2,3,4] //將arr1拷貝給arr2 var arr2 = arr1; //向arr1中尾部添加一個元素5 arr1.push(5); console.log(arr1); console.log(arr2);
先定義數(shù)組arr1,然后將arr1拷貝給arr2,再接著向arr1中尾部追加一個元素,分別打印arr1和arr2的值,打印結(jié)果為:

從結(jié)果中我們可以看到:我們先將arr1拷貝給了arr2,但是當(dāng)我們改變arr1時,arr2也跟著一起改變了。這印證了前文所說的,arr1和arr2實(shí)際上是指向了內(nèi)存中的同一片地址,當(dāng)arr1發(fā)生變化時,實(shí)際上是將指向的這片內(nèi)存地址中的數(shù)據(jù)變化了,而arr2也指向的是這片地址,所以arr2也會跟著變化。
上面代碼中的arr2=arr1,就是我們俗稱的淺拷貝,淺拷貝僅僅拷貝的是變量名,其在內(nèi)存的存儲沒有被拷貝,指向的還是同一片內(nèi)存地址,這就是造成了一個變化另外一個也跟著變化,這在日常開發(fā)中不是我們想要的。
那如何實(shí)現(xiàn)真正的拷貝呢?
四.實(shí)現(xiàn)深拷貝
真正的拷貝,就是拷貝過后,arr1和arr2指向的不再是同一片內(nèi)存地址,而是分別指向各自的地址,這樣發(fā)生變化的時候就不會出現(xiàn)同時變化,這就是深拷貝。
下面我們封裝一個深拷貝函數(shù),來實(shí)現(xiàn)引用類型的深拷貝:
//參數(shù)p為原對象
//參數(shù)c為原對象的類型,若原對象為數(shù)組,則傳入c為[],若原對象是對象傳入c為{},也可不傳默認(rèn)為{}
function deepCopy(p,c){
var c = c || {};
for(var i in p){
if(typeof p[i] === "object"){
c[i] = (p[i].constructor === Array)?[]:{};
deepCopy(p[i],c[i])
}else{
c[i] = p[i]
}
}
return c;
}
五.測試
//參數(shù)p為原對象
//參數(shù)c為原對象的類型,若原對象為數(shù)組,則傳入c為[],若原對象是對象傳入c為{},也可不傳默認(rèn)為{}
function deepCopy(p,c){
var c = c || {};
for(var i in p){
if(typeof p[i] === "object"){
c[i] = (p[i].constructor === Array)?[]:{};
deepCopy(p[i],c[i])
}else{
c[i] = p[i]
}
}
return c;
}
//定義數(shù)組arr1
var arr1 = [1,2,3,4]
//將arr1拷貝給arr2
var arr2 = deepCopy(arr1,[]);
//向arr1中尾部添加一個元素5
arr1.push(5);
console.log('arr1:',arr1);
console.log('arr2:',arr2);
測試結(jié)果:

結(jié)果中可以看到,arr1變化沒有引起arr2的變化,實(shí)現(xiàn)了真正的拷貝。
總結(jié)
以上所述是小編給大家介紹的JS中的兩種數(shù)據(jù)類型及實(shí)現(xiàn)引用類型的深拷貝的方法,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復(fù)大家的!
相關(guān)文章
javascript的回調(diào)函數(shù)應(yīng)用示例
回調(diào)函數(shù)就是一個通過函數(shù)指針調(diào)用的函數(shù)。下面以示例的方式為大家介紹下其具體的使用2014-02-02
JavaScript數(shù)學(xué)對象(Math)方法舉例詳解
這篇文章主要給大家介紹了關(guān)于JavaScript數(shù)學(xué)對象(Math)方法的相關(guān)資料,Math(數(shù)學(xué))對象的作用是執(zhí)行普通的算數(shù)任務(wù),文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03
JavaScript實(shí)現(xiàn)前端倒計(jì)時效果
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)前端倒計(jì)時效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-02-02
如何在TypeScript使用模塊化以及注意事項(xiàng)詳解
在TypeScript中就像在EC5中一樣,任何包含頂級import或export的文件都被認(rèn)為是一個模塊,下面這篇文章主要給大家介紹了關(guān)于如何在TypeScript使用模塊化以及注意事項(xiàng)的相關(guān)資料,需要的朋友可以參考下2022-10-10

