深復(fù)制與淺復(fù)制
let obj = { a: 1, arr: [1, 2]};let obj1 = obj; //淺復(fù)制
obj1.a = 2console.log(obj) // { a:2, arr: [1,2] };
//同樣的方式
let obj = { a: 1, arr: [1, 2]};
let obj2 = deepCopy(obj); //深復(fù)制
obj2.a = 2console.log(obj) // { a:1, arr: [1,2] };
因為JavaScript存儲對象都是存地址的,所以淺復(fù)制會導(dǎo)致 obj 和 obj1指向同一塊內(nèi)存地址演痒,大概的示意圖如下亲轨。而深復(fù)制一般都是開辟一塊新的內(nèi)存地址,將原對象的各個屬性逐個復(fù)制出去鸟顺。
es6-Object.assign()方法
深復(fù)制只有一層惦蚊,之后為淺復(fù)制(除非再次使用Object.assign嵌套方式賦值)
let obj = { a: 1, arr: [1, 2]};
let obj1 = Object.assign({}, obj);
obj1.a = 2//不變
console.log(obj) // { a:1, arr: [1,2] };
let obj = {
a: {
b: 20
},
arr: [1, 2]};
let obj1 = Object.assign({}, obj);
obj1.a.b = 2;//除非再次使用Object.assign嵌套方式賦值
//變化
console.log(obj) // { a:{b:2}, arr: [1,2] };
為什么使用不可變(immutable)的數(shù)據(jù)?
(pureRender結(jié)合immutable讯嫂,見末尾)
下面是項目中實際的一個例子
第一種方式
//recduer.js(cart)第一種方式
case types.CART_PUT_MAIN + '_SUCCESS':
//更新數(shù)據(jù)
carts = state.main.carts; // carts 選中的id數(shù)組
id = action.param.id;
newState = {
...state,
main:{
...state.main,
itemObj:{
...state.main.itemObj,
[id]:{
...state.main.itemObj[id],
quantity:action.param.quantity
}
}
}
};
sum = sumCommon(carts, newState.main.itemObj);
newState = {
...newState,
main:{
...newState.main,
...sum
}
};
return newState;
讓我們來看一下對數(shù)據(jù)層的變化:
componentWillReceiveProps(nextProps){
console.log(nextProps);
//next:顧名思義是接收到的next->props蹦锋,輸出的是上面方法中的newState的值
console.log(this.props);
//cur:是當前的props的值,因為使用的是類immutable的方式欧芽,所以數(shù)據(jù)不變莉掂;
}
第二種方式
//recduer.js(cart)第一種方式
case types.CART_PUT_MAIN + '_SUCCESS':
newState = Object.assign({}, state);
carts = newState.main.carts; // carts 選中的id數(shù)組
id = action.param.id;
//淺復(fù)制
newState.main.itemObj[id].quantity = action.param.quantity;;
sum = sumCommon(carts, newState.main.itemObj);
newState = Object.assign({}, newState, {
main: Object.assign({}, newState.main, sum)
});
return newState;
讓我們來再來看一下對數(shù)據(jù)層的變化:
componentWillReceiveProps(nextProps){
console.log(nextProps);
//next:顧名思義是接收到的next->props,輸出的是上面方法中的newState的值
console.log(this.props);
//cur:是當前的props的值千扔,因為使用的是類immutable的方式憎妙,所以數(shù)據(jù)不變;
}
為了讓數(shù)據(jù)變化更加可測曲楚,我們應(yīng)當使用深復(fù)制相關(guān)厘唾,讓我們自己的數(shù)據(jù)更加安全
處理方法一:es7 ... 的方式
直接{...obj}賦值屬于淺復(fù)制,在修改值時{...obj,a:1}就起到了類深復(fù)制的效果更新一個 Object 洞渤,則:
let obj = { a: 0, b: 20,}
obj = {...obj, a: obj.a + 1}
而不是:
obj.a = obj.a + 1
同樣的為了避免對 Object 的 in-place editing阅嘶,數(shù)組也是一樣:
let arr = [ { id: 1,a: 1}]
arr = [...arr, { id: 2,a: 2} ]
而不是:
let arr = [ { id: 1, a:1}]
arr.push({ id: 2, a,2});
以這樣的方式,無需 Immutable.js ,我們可以讓應(yīng)用程序狀態(tài)是 不可變(Immutable) 的炭懊。
...注意事項及要求
let obj = {
a: 20,
arr: [1, 2]
};
let obj1 = { ...obj }; //于obj1=obj一樣
obj1.a = 2 //不能使用這樣賦值
//變化 淺復(fù)制
console.log(obj) // { a:2, arr: [1,2] };
//...必須改用這樣的賦值形式
obj1 = { ...obj1 , a:2 }
//深復(fù)制
console.log(obj) // { a:20, arr: [1,2] };
console.log(obj1) // { a:2, arr: [1,2] };
...與Object.assign屬于一個道理(這里和層級相關(guān))
//你可以將其轉(zhuǎn)化為
let obj = {
a: {
b: 20
},
arr: [1, 2]
};
let obj1 = obj
obj1 = Object.assign({}, obj1, {
a: Object.assign({}, obj1.a,{b:2})
});
console.log(obj) //{ a:{b:20}, arr: [1,2] }
console.log(obj) //{ a:{b:2}, arr: [1,2] }
所以盡量使用...代替Object.assign
處理方法二:使用immutable.js
為什么需要使用immutable.js
之前方式的多層嵌套
//深復(fù)制(類immutable)
newState = {
...state,
main:{
...state.main,
itemObj:{
...state.main.itemObj,
[id]:{
...state.main.itemObj[id],
prop:action.param.props_str,
product_id:action.param.product_id,
price:action.param.price
}
}
}
};
//淺復(fù)制
newState.main.itemObj[id].prop = action.param.props_str;
//immutable.js方式
...參考immutable的api殖告!
PureRenderMixin使用請參考以下內(nèi)容
簡單的說就是數(shù)據(jù)變化,比較前后兩次的數(shù)據(jù)是否相同坚踩,判斷是否重新render;否則你的父容器一改變數(shù)據(jù),所有的子組件都重新渲染了惋耙,為了增加性能請使用pureRender;
(封裝好的PureRender如下:)
'use strict';
import { is } from 'immutable';
let hasOwnProperty = Object.prototype.hasOwnProperty;
function shallowEqual(objA, objB) {
if (objA === objB || is(objA, objB)) {
return true;
}
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}
let keysA = Object.keys(objA);
let keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
let bHasOwnProperty = hasOwnProperty.bind(objB);
for (let i = 0; i < keysA.length; i++) {
if (!bHasOwnProperty(keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
function shallowCompare(instance, nextProps, nextState) {
return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState);
}
function shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
}
function pureRenderDecorator(component) {
component.prototype.shouldComponentUpdate = shouldComponentUpdate;
}
module.exports = pureRenderDecorator;
/*使用方式*/
import pureRender from 'pure-render-decorator';
//babel配置中引入一個transform-decorators-legacy插件
@pureRender
class XXX extends React.Component {
//...
}
PureRender的使用要求:對于子組件需要什么參數(shù)傳遞什么熊昌,不要把一大塊無用的數(shù)據(jù)引入绽榛,否則兩次傳入的this.props可能始終會不一樣,導(dǎo)致PureRender無效