淺析JavaScript解析賦值、淺拷貝和深拷貝的區別

文章首發于sau交流學習社區

一、賦值(Copy)

賦值是將某一數值或對象賦給某個變量的過程,分為:

1、基本數據類型:賦值,賦值之后兩個變量互不影響

2、引用數據類型:賦**址**,兩個變量具有相同的引用,指向同一個對象,相互之間有影響

對基本類型進行賦值操作,兩個變量互不影響。

// saucxs
let a = "saucxs";
let b = a;
console.log(b);  // saucxsa = "change";
console.log(a);   // changeconsole.log(b);    // saucxs

對引用類型進行賦**址**操作,兩個變量指向同一個對象,改變變量?a?之后會影響變量?b,哪怕改變的只是對象?a?中的基本類型數據。

// saucxs
let a = {name: "saucxs",book: {title: "You Don't Know JS",price: "45"}
}
let b = a;
console.log(b);
// {
// 	name: "saucxs",
// 	book: {title: "You Don't Know JS", price: "45"}
// } a.name = "change";
a.book.price = "55";
console.log(a);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } console.log(b);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// }

通常在開發中并不希望改變變量?a?之后會影響到變量?b,這時就需要用到淺拷貝和深拷貝。

?

二、淺拷貝(Shallow?Copy)

1、什么是淺拷貝

創建一個新對象,這個對象有著原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值,如果屬性是引用類型,拷貝的就是內存地址?,所以如果其中一個對象改變了這個地址,就會影響到另一個對象。

?

?

?

上圖中,`SourceObject`?是原對象,其中包含基本類型屬性?`field1`?和引用類型屬性?`refObj`。淺拷貝之后基本類型數據?`field2`?和?`filed1`?是不同屬性,互不影響。但引用類型?`refObj`?仍然是同一個,改變之后會對另一個對象產生影響。

簡單來說可以理解為淺拷貝只解決了第一層的問題,拷貝第一層的**基本類型值**,以及第一層的**引用類型地址**。

?

2、淺拷貝使用場景

2.1?Object.assign()

`Object.assign()`?方法用于將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它將返回目標對象。

有些文章說`Object.assign()`?是深拷貝,其實這是不正確的。

// saucxs
let a = {name: "saucxs",book: {title: "You Don't Know JS",price: "45"}
}
let b = Object.assign({}, a);
console.log(b);
// {
// 	name: "saucxs",
// 	book: {title: "You Don't Know JS", price: "45"}
// } a.name = "change";
a.book.price = "55";
console.log(a);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } console.log(b);
// {
// 	name: "saucxs",
// 	book: {title: "You Don't Know JS", price: "55"}
// }

上面代碼改變對象?a?之后,對象?b?的基本屬性保持不變。但是當改變對象?a?中的對象?`book`?時,對象?b?相應的位置也發生了變化。

?

2.2 展開語法?`Spread`

// saucxs
let a = {name: "saucxs",book: {title: "You Don't Know JS",price: "45"}
}
let b = {...a};
console.log(b);
// {
// 	name: "saucxs",
// 	book: {title: "You Don't Know JS", price: "45"}
// } a.name = "change";
a.book.price = "55";
console.log(a);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } console.log(b);
// {
// 	name: "saucxs",
// 	book: {title: "You Don't Know JS", price: "55"}
// }

?

2.3 Array.prototype.slice方法

slice不會改變原數組,`slice()`?方法返回一個新的數組對象,這一對象是一個由?`begin`和?`end`(不包括`end`)決定的原數組的**淺拷貝**。

// saucxs
let a = [0, "1", [2, 3]];
let b = a.slice(1);
console.log(b);
// ["1", [2, 3]]a[1] = "99";
a[2][0] = 4;
console.log(a);
// [0, "99", [4, 3]]console.log(b);
//  ["1", [4, 3]]

可以看出,改變?`a[1]`?之后?`b[0]`?的值并沒有發生變化,但改變?`a[2][0]`?之后,相應的?`b[1][0]`?的值也發生變化。

說明?`slice()`?方法是淺拷貝,相應的還有`concat`等,在工作中面對復雜數組結構要額外注意。

?

三、深拷貝(Deep Copy)

3.1 什么是深拷貝?

深拷貝會拷貝所有的屬性,并拷貝屬性指向的動態分配的內存。當對象和它所引用的對象一起拷貝時即發生深拷貝。深拷貝相比于淺拷貝速度較慢并且花銷較大。拷貝前后兩個對象互不影響。

?

?

3.2 使用深拷貝的場景

3.2.1 JSON.parse(JSON.stringify(object))

// saucxs
let a = {name: "saucxs",book: {title: "You Don't Know JS",price: "45"}
}
let b = JSON.parse(JSON.stringify(a));
console.log(b);
// {
// 	name: "saucxs",
// 	book: {title: "You Don't Know JS", price: "45"}
// } a.name = "change";
a.book.price = "55";
console.log(a);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } console.log(b);
// {
// 	name: "saucxs",
// 	book: {title: "You Don't Know JS", price: "45"}
// }

完全改變變量?a?之后對?b?沒有任何影響,這就是深拷貝的魔力。

我們看下對數組深拷貝效果如何。

// saucxs
let a = [0, "1", [2, 3]];
let b = JSON.parse(JSON.stringify( a.slice(1) ));
console.log(b);
// ["1", [2, 3]]a[1] = "99";
a[2][0] = 4;
console.log(a);
// [0, "99", [4, 3]]console.log(b);
//  ["1", [2, 3]]

對數組深拷貝之后,改變原數組不會影響到拷貝之后的數組。

但是該方法有以下幾個問題:

(1)會忽略?`undefined`

(2)會忽略?`symbol`

(3)不能序列化函數

(4)不能解決循環引用的對象

(5)不能正確處理`new?Date()`

(6)不能處理正則

?

其中(1)(2)(3) `undefined`、`symbol`?和函數這三種情況,會直接忽略。

// saucxs
let obj = {name: 'saucxs',a: undefined,b: Symbol('saucxs'),c: function() {}
}
console.log(obj);
// {
// 	name: "saucxs", 
// 	a: undefined, 
//  b: Symbol(saucxs), 
//  c: ? ()
// }let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "saucxs"}

?

其中(4)循環引用會報錯

// saucxs
let obj = {a: 1,b: {c: 2,d: 3}
}
obj.a = obj.b;
obj.b.c = obj.a;let b = JSON.parse(JSON.stringify(obj));
// Uncaught TypeError: Converting circular structure to JSON

?

其中(5)*?`new?Date`?情況下,轉換結果不正確。

// saucxs
new Date();
// Mon Dec 24 2018 10:59:14 GMT+0800 (China Standard Time)JSON.stringify(new Date());
// ""2018-12-24T02:59:25.776Z""JSON.parse(JSON.stringify(new Date()));
// "2018-12-24T02:59:41.523Z"

解決方法轉成字符串或者時間戳就好了。

// saucxs
let date = (new Date()).valueOf();
// 1545620645915JSON.stringify(date);
// "1545620673267"JSON.parse(JSON.stringify(date));
// 1545620658688

?

其中(6)正則情況下

// saucxs
let obj = {name: "saucxs",a: /'123'/
}
console.log(obj);
// {name: "saucxs", a: /'123'/}let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "saucxs", a: {}}

?

PS:為什么會存在這些問題可以學習一下?JSON。

除了上面介紹的深拷貝方法,

常用的還有`jQuery.extend()`?和?`lodash.cloneDeep()`,后面文章會詳細介紹源碼實現。

?

四、總結

??和原數據是否指向同一對象?第一層數據為基本數據類型?原數據中包含子對象
?賦值是??改變會使原數據一起改變?改變會使原數據一起改變
?淺拷貝否??改變不會使原數據一起改變?改變會使原數據一起改變
?深拷貝否??改變不會使原數據一起改變?改變不會使原數據一起改變

?

五、參考

1、深拷貝和淺拷貝

2、MDN之Object.assign()

3、MDN之展開語法

4、MDN之Array.stypetype.slice()

轉載于:https://www.cnblogs.com/chengxs/p/10788442.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386924.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386924.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386924.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RUNOOB python練習題27 遞歸逆向輸出字符串

用來練手的python練習題,原題鏈接: python練習實例27 題干: 利用遞歸函數調用方式,將所輸入的5個字符,以相反順序打印出來。 實際要實現這個功能很簡單,我們只需要反向遍歷字符串即可。源代碼如下: def p_inverse_boucle(my_st…

ELK 6.2.4搭建

開源實時日志分析ELK平臺能夠完美的解決我們上述的問題,ELK由ElasticSearch、Logstash和Kiabana三個開源工具組成。 官方網站:https://www.elastic.co/products Elasticsearch是個開源分布式搜索引擎,它的特點有:分布式&#xff…

vim打開退出命令

打開文件方法:cd /Users/liuchang/.jenkins/secrets && vim initialAdminPassword 退出方法:先按ESC,再輸入冒號,在輸入命令時,直接輸入"q!",如圖。 轉載于:https://www.cnblogs.com/sma…

RUNNOOB python練習題 28 遞歸 數列

用來練手的python練習題其28,原題鏈接:python練習實例28 題干: 有5個人坐在一起,問第五個人多少歲?他說比第4個人大2歲。問第4個人歲數,他說比第3個人大2歲。問第三個人,又說比第2人大兩歲。問第2個人,說比…

使用Spring Task完成定時任務

1. 前言 上一篇我們學習了Quartz作為定時任務的框架的使用, 這一篇我們來學習Spring全家桶的SpringTask, 對于主張簡單易用的Spring家族來說, SpringTask無疑也是一個輕量級的框架,他比Quartz更容易上手. 2. pom.xml依賴 <dependencies> <dependency> …

python 讀寫文件

https://www.cnblogs.com/evablogs/p/6725242.html 文件的打開讀寫關閉&#xff08;文件使用完畢后必須關閉&#xff0c;因為文件對象會占用操作系統的資源&#xff09; 123456789#寫文件with open(rD:\Test\1.txt,w) as f: #with比<strong>try....finally<…

RUNOOB python練習題29

用來練手的python練習題其29&#xff0c;原題鏈接:python練習實例29 題干 : 給一個不多于5位的正整數&#xff0c;要求&#xff1a;一、求它是幾位數&#xff0c;二、逆序打印出各位數字。 實際這個正整數無論位數&#xff0c;在python3中都很容易實現。源代碼如下: def ent…

定時任務(Spring Cloud Task)

引入依賴 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.sprin…

P3357 最長k可重線段集問題 網絡流

P3357 最長k可重線段集問題 題目描述 給定平面 x-O-yx?O?y 上 nn 個開線段組成的集合 II&#xff0c;和一個正整數 kk 。試設計一個算法&#xff0c;從開線段集合 II 中選取出開線段集合 S\subseteq IS?I ,使得在 xx 軸上的任何一點 pp&#xff0c;SS 中與直線 xpxp 相交的開…

服務被人當肉雞了,叫一路賺錢 xig

網上看了一下&#xff0c;說有專門人研究服務 個人懷疑是阿里云內部人干的&#xff0c;因為買了服務器后&#xff0c;沒有安裝對外使用的地址性質的網站&#xff0c;IP開通了之后只有阿里的人知道&#xff0c;上面還有阿里云盾。 看了下進程地址&#xff0c;上面的啟動命令 x…

RUNOOB python練習題30 回文數

用來練手的python練習題 30。原題鏈接:python練習實例30 題干 : 一個5位數&#xff0c;判斷它是不是回文數。即12321是回文數&#xff0c;個位與萬位相同&#xff0c;十位與千位相同。 與上一個例題類似&#xff0c;判斷一個數是不是回文數&#xff0c;我們使用字符串類型更加…

高并發與負載均衡-keepalived-概念介紹

keepalived是用戶空間的程序&#xff0c;這個程序會同時在主的lvs和備用的lvs啟動 轉載于:https://www.cnblogs.com/LXL616/p/10793790.html

asp.net2.0跨域問題

什么叫跨域&#xff1f; 簡單理解就是不同服務器&#xff0c;不同域名之間的訪問。 1 如何設置asp.net web程序的跨域&#xff1f; 在web.config中添加如下代碼 1 <system.webServer> <httpProtocol> <customHeaders> <add name&qu…

RUNOOB python練習題31 根據已輸入的字符判斷星期幾

用來練手的python練習題31&#xff0c; 原題鏈接 : python練習實例31 題干 : 請輸入星期幾的第一個字母來判斷一下是星期幾&#xff0c;如果第一個字母一樣&#xff0c;則繼續判斷第二個字母。 一個條件語句練習題&#xff0c;非常簡單了可以說&#xff0c;就是把所有的條件都…

解決FTPClient上傳文件為空,顯示0字節

JAVA使用FTPClient上傳文件時總是為空&#xff0c;而使用FileZilla客戶端時卻不會。 后來查了下資料&#xff0c;FTP服務器有被動模式和主動模式。&#xff08;具體查另外資料&#xff09; 在JAVA中將FTPClient設置為被動模式即可解決問題。 import org.apache.commons.net.f…

軟件工程——結對編程第二次作業

目錄 1. 題目及要求2. 功能的設計3. GUI&#xff08;圖形用戶界面&#xff09;的設計4. 容錯機制的設計4.1 選擇運算符的容錯處理4.2 最大值和題目數輸入的容錯處理4.3 打開文件容錯處理4.4 打印的容錯處理5. 程序的運行效果6. 對領航員的評價7. 總結本次作業所開發的程序已上傳…

RUNOOB python練習題 32 列表的中括號符號小tips

用來練手的python練習題&#xff0c;原題鏈接: python練習實例32 題干: 按相反的順序輸出列表的值 拿到題目首先寫下如下代碼: a [1,2,3,4] for i in range(len(a)):print(a[len(a)-i-1])輸出結果如下: 使用一個簡單的循環就可以完成這個操作。但其實python有利用中括號操…

redis啟動后出現WARNING you have Transparent Huge Pages (THP) support enabled in your kernel問題...

問題描述&#xff1a;啟動redis后出現&#xff1a;WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command echo never > /sys/kernel/mm/trans…

Anaconda安裝第三方包(whl文件)

先說下環境 Anaconda 對應Python3.5的版本 win7,64位系統。 step1&#xff1a;下載whl文件 step2&#xff1a;打開‘Anaconda Command Prompt‘&#xff0c; 如下圖&#xff1a; step3&#xff1a;命令行窗口pip安裝&#xff0c;代碼如下&#xff1a; pip install 路徑whl…

RUNOOB python練習題33 使用join方法實現用逗號分隔列表

用來練手的python練習題&#xff0c;原題鏈接:python練習實例33 題干: 按逗號分隔列表 用逗號分隔列表&#xff0c;我們就想到了join方法。 str.join(sequence)可以用自定的str字符串分隔一個序列&#xff0c;這個序列可以是字符串&#xff0c;列表&#xff0c;元組&#xff…