js 什麼是深拷貝問題?

1、什麼是值類型?redux

2、什麼是引用類型?數組

3、使用ES Next新特性帶來的 Object.assign 方法 和 擴展運算符;工具

4、Object.assign 方法 和 擴展運算符的 「深刻淺出」 問題 —— 淺拷貝;性能

5、解決深拷貝問題常見的三種方法。spa

 


 

在 JavaScript 中,數據類型包括:3d

一、基本數據(值)類型,如 undefined、null、number、boolean、string等code

二、引用類型:如Object、Array、Function等。對象

 

1、什麼是值類型?blog

var a = 1; var b = a; b = b + 1 console.log(a, b) // 1, 2

值類型賦值時,會在棧內存空間中分配全新的地址,並獲得相應的值。代碼中 b 是從 a 建立來的,當 b 變化時,a 不會受到影響。這是符合常識的。遞歸

 

2、什麼是引用類型?

var data = { id: 0, book: 'learn redux', avaliable: false }
var newData = data

newData.id
= 1; console.log(data.id, newData.id) // 1 1

再來看看引用類型(以Object對象爲例),對象實際是存在於堆內存空間中。當咱們要訪問一個對象的時候,其實是從棧內存中獲取引用地址,而後根據這個引用地址再從堆內存中獲取所須要的值。

而引用類型賦值時,其實是獲取其引用地址,而不是直接獲取值。因此,值發生改變時,源對象也會隨着改變。

 

3、使用ES Next新特性帶來的 Object.assign 方法 和 擴展運算符

使用 Object.assign({}, ...) 來解決 "單層"對象 引用問題

let data = { id: 0, book: 'learn redux', avaliable: false }
let newData
= Object.assign({}, data)
newData.id
= 1; console.log(data.id, newData.id) // 0 1

 或者使用對象的擴展運算符

var data = { id: 0, book: 'learn redux', avaliable: false } var newData = {title: 'fuck', ...data}; newData.id = 1; console.log(data.id, newData.id) // 0 1

(PS:JavaScript的數組也是引用類型,同理能夠使用 Array.concat 方法 和 數組的擴展運算符來解決引用問題,這裏就省略了)

 

4、Object.assign 方法 和 擴展運算符的 「深刻淺出」 問題 —— 淺拷貝

須要注意的是,使用 Object.assign 及 擴展運算符都屬於淺操做。若是往對象的外面再套一層的話,那問題就會浮現出來了。

var data = { title: '123', item: { id: 0, book: 'learn redux', avaliable: false } } var newData = Object.assign({}, data) newData.item.id = 1; console.log(data.item.id, newData.item.id) // 1 1

上述的結果代表,原始數據仍是被更改了。

 

 

 

5、解決深拷貝問題常見的三種方法

一、jQuery的深拷貝工具: $.extend(true, ...)

let data = { title: '123', item: { id: 0, book: 'learn redux', avaliable: false } }
// 首參必須設置爲 true 纔算是深拷貝 let newData
= $.extend(true, {}, data); newData.item.id = 1 console.log(data.item.id, newData.item.id) // 0 1

爲何$.extend默認不使用深拷貝,還須要手動傳參true纔開啓呢?

這是由於深拷貝在實戰中是比較消耗性能的,如無必要請使用淺拷貝便可。稍後咱們會實現一個本身的深拷貝工具。你能夠從中瞭解詳情。

 

 

二、巧用序列化:JSON.parse(JSON.stringify(...))

let data = { title: '123', item: { id: 0, book: 'learn redux', avaliable: false } } let newData = JSON.parse(JSON.stringify(data)) newData.item.id = 1 console.log(data.item.id, newData.item.id) // 0 1

原理很簡單,就是先將對象轉換爲字符串,再從新序列化爲對象,這樣理所固然不會有引用問題。值得一提的是,這些操做對數組也是有效的。

 

 

三、遞歸拷貝(深拷貝)

在咱們封裝一個本身的深拷貝工具以前,咱們須要先跳過一些可能的拷貝錯誤認知 —— 「不要單純的認爲,只要是拷貝嵌套對象就會產生深拷貝問題」。

(1)就算是拷貝嵌套對象,若是操做的只是基本數據類型,不會有影響的。

let data = { title: '123', item: { id: 0, book: 'learn redux', avaliable: false } } let newData = Object.assign({}, data); newData.title = 'fuck' console.log(data.title, newData.title) // 123 fuck

(2)就算是嵌套對象,若是隻拷貝其中一層對象,是不會產生問題的。

let data = { title: '123', item: { id: 0, book: 'learn redux', avaliable: false } } let newData = Object.assign({}, data.item); newData.id = 1; console.log(data.item.id, newData.id) // 0 1

 

有了以上兩個認知,實際上咱們能夠實現手動深拷貝了。

let data = { title: '123', item: { id: 0, book: 'learn redux', avaliable: false } } // 建立一個空對象
let newData = {} // 基本數據類型,能夠直接拿
newData.title = data.title // data.item對象,其實能夠使用 Object.assign 來拿 // newData.item = Object.assign({}, data.item)

// 但爲了體現手動深拷貝細節,仍是一步一步來吧。 // 經過對data.item拆解,item對象被轉換爲一個個的基本數據類型直接賦值給newData.item的每一項 // 若是 item 還有對象的話,那就只能依樣畫葫蘆繼續拆解。直到變成基本數據類型爲止。
newData.item = {} newData.item.id = data.item.id newData.item.book = data.item.book newData.item.avaliable = data.item.avaliable newData.item.id = 1 console.log(data.item.id, newData.item.id) // 0 1

經過對data.item拆解,item對象就轉換爲一個個的基本數據類型了,因此能夠直接賦值給newData.item的每一項了。

若是 item 還有對象的話,那就只能依樣畫葫蘆繼續拆解。直到變成基本數據類型爲止。

 

知道了如何手動深拷貝,如今咱們把他轉換爲自動化深拷貝,常見的的實現方式是遞歸拷貝了:

// 遞歸深拷貝
var
deepExtend = function(out) { out = out || {}; for (var i = 1; i < arguments.length; i++) { var obj = arguments[i]; if (!obj) continue; for (var key in obj) { if (obj.hasOwnProperty(key)) { if (typeof obj[key] === 'object') out[key] = deepExtend(out[key], obj[key]); else out[key] = obj[key]; } } } return out; };
// demo let data
= { title: '123', item: { id: 0, book: 'learn redux', avaliable: false } } var newData = deepExtend({}, data); newData.item.id = 1 console.log(data.item.id, newData.item.id) // 0 1

 

相關文章
相關標籤/搜索