不論是react仍是vue,父級組件與子組件的通訊都是經過props來實現的,在vue中父組件的props遵循的是單向數據流,用官方的話說就是,父級的props的更新會向下流動到子組件中,反之則不行。也就是說,子組件不該該去修改props。但實際開發過程當中,可能會有一些狀況試圖去修改props數據:vue
一、這個props只是傳遞一個初始值,子組件把它當作一個局部變量來使用,這種狀況通常定義一個本地的data屬性,將props的值賦值給它。以下:react
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
二、這個props的值以原始數據傳入,可是子組件對其須要轉換。這種狀況,最好使用computed來定義一個計算屬性,以下:jquery
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
以上兩種狀況,傳遞的值都是基本數據類型,可是大多數狀況下,咱們須要向子組件傳遞一個引用類型數據,那麼問題就來了。json
JavaScript 中對象和數組是經過引用傳入的,因此對於一個數組或對象類型的 prop 來講,在子組件中改變這個對象或數組自己將會影響到父組件的狀態。數組
好比,在父組件中有一個列表,雙擊其中一個元素進行編輯,該元素的數據做爲props傳遞給一個子組件,在子組件中須要對該數據進行編輯,你會發現如上所說,編輯後父組件的值也發生了變化。實際上咱們想父組件影響子組件,可是子組件修改不要影響父組件。vue官網上貌似沒說明這種狀況應該如何處理。this
這裏狀況相對簡單點,在傳遞props時用Object.assign拷貝一份數據(這裏數據是一個單層級對象),而後在子組件裏面對其進行編輯。Object.assign能實現對象的合併,可是它是淺拷貝,也就是說若是對象的熟悉也是對象就不行。spa
因而查閱了相關資料,再次鞏固下JS中深拷貝與淺拷貝的相關知識。3d
一、基本數據類型和引用數據類型的存儲位置code
基本數據類型是存儲在棧內存中,好比 var a=1;orm
當進行復制操做b=a時,會在棧內存中再開一個內存,以下
變量a和變量b的存儲互補影響,若是此時修改b的值不會影響a的值。
引用類型數據存儲在堆內存中,引用類型的名是存儲在棧內存中,值是存儲在堆內存中,可是棧內存會提供引用地址指向堆內存中的值。
當進行b=a的複製操做時,複製的是引用地址,而不是堆內存中的值。
而當咱們a[0]=1時進行數組修改時,因爲a與b指向的是同一個地址,因此天然b也受了影響,這就是所謂的淺拷貝了。
而實際上咱們但願的效果應該是這樣:
好,到這裏,到底什麼是深淺拷貝:
對於僅僅是複製了引用(地址),換句話說,複製了以後,原來的變量和新的變量指向同一個東西,彼此之間的操做會互相影響,爲 淺拷貝。
而若是是在堆中從新分配內存,擁有不一樣的地址,可是值是同樣的,複製後的對象與原來的對象是徹底隔離,互不影響,爲 深拷貝。
回顧下JS裏實現拷貝的方法有哪些:
針對數組有這些方法:
Array.slice()
var a=[1,2,3]; var b=a.slice();
b[0]=4;
console.log(b);//[4,2,3]
console.log(a);//[1,2,3]
Array.concat
var a=[1,2,3]; var b=a.concat(); b[0]=4; console.log(b);//[4,2,3] console.log(a);//[1,2,3]
固然,也能夠遍歷數組賦值。
可是以上兩種只對單級結構的數組有效,若是數組的元素是一個引用類型,就不行了,好比:
let a=[0,1,[2,3],4], b=a.slice(); a[0]=1; a[2][0]=1; console.log(a,b);
修改二維數組的元素仍是會影響原數組,也就是說slice和concat其實是淺拷貝。
針對對象:
Object.assign()
var a={ "name":"張三" }; b=Object.assign({},a); b.name="李四"; console.log(b.name);//李四 console.log(a.name);//張三
一樣該方法也是淺拷貝,若是對象屬性值是引用類型也不行;
那麼到底有哪些辦法能夠實現深拷貝呢
一、遞歸
function deepClone(obj){ let objClone = Array.isArray(obj)?[]:{}; if(obj && typeof obj==="object"){ for(key in obj){ if(obj.hasOwnProperty(key)){ //判斷ojb子元素是否爲對象,若是是,遞歸複製 if(obj[key]&&typeof obj[key] ==="object"){ objClone[key] = deepClone(obj[key]); }else{ //若是不是,簡單複製 objClone[key] = obj[key]; } } } } return objClone; } let a=[1,2,3,4], b=deepClone(a); a[0]=2; console.log(a,b);
二、jquery中的$.extend();
var obj = {name:'xixi',age:20,company : { name : '騰訊', address : '深圳'} }; var obj_extend = $.extend(true,{}, obj); //extend方法,第一個參數爲true,爲深拷貝,爲false,或者沒有爲淺拷貝。 console.log(obj === obj_extend); obj.company.name = "ali"; obj.name = "hei"; console.log(obj); console.log(obj_extend);
三、JSON對象的JSON.parse()和JSON.stringify();
var obj = {name:'xixi',age:20,company : { name : '騰訊', address : '深圳'} }; var obj_json = JSON.parse(JSON.stringify(obj)); console.log(obj === obj_json); obj.company.name = "ali"; obj.name = "hei"; console.log(obj); console.log(obj_json);
四、Lodash中的_.cloneDeep()
var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false
雖然經過拷貝props數據解決了問題,可是拷貝後修改新數據的屬性並不會觸發vue的更新機制,須要強制更新$forceUpdate(),總以爲很奇怪,不知道你們有什麼更好的辦法沒有,歡迎你們留言討論。
參考文章:
https://zhuanlan.zhihu.com/p/26282765
https://zhuanlan.zhihu.com/p/26282765