React 数据为什么要使用 immutable 方式?浅复制与深复制思考

1,881 阅读3分钟
原文链接: segmentfault.com

深复制与浅复制

let obj = {
    a: 1,
    arr: [1, 2]
};
let obj1 = obj;            //浅复制
obj1.a = 2

console.log(obj) // { a:2, arr: [1,2] };

//同样的方式
let obj = {
    a: 1,
    arr: [1, 2]
};
let obj2 = deepCopy(obj);  //深复制
obj2.a = 2
console.log(obj) // { a:1, arr: [1,2] };

因为JavaScript存储对象都是存地址的,所以浅复制会导致 obj 和 obj1
指向同一块内存地址,大概的示意图如下。而深复制一般都是开辟一块新的内存地址,将原对象的各个属性逐个复制出去。

es6-Object.assign()方法

深复制只有一层,之后为浅复制(除非再次使用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)的数据?

下面是项目中实际的一个例子

第一种方式

//recduer.js(cart)第一种方式
case types.CART_PUT_MAIN + '_SUCCESS':
    //更新数据
    carts = state.main.carts; // carts 选中的id数组
    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;

让我们来看一下对数据层的变化:

componentWillReceiveProps(nextProps){
    console.log(nextProps); 
    //next:顾名思义是接收到的next->props,输出的是上面方法中的newState的值
    console.log(this.props);
    //cur:是当前的props的值,因为使用的是类immutable的方式,所以数据不变;
}

第二种方式

//recduer.js(cart)第一种方式
case types.CART_PUT_MAIN + '_SUCCESS':
    newState = Object.assign({}, state);
    carts = newState.main.carts; // carts 选中的id数组
    id = action.param.id;
    //浅复制
    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;

让我们来再来看一下对数据层的变化:

componentWillReceiveProps(nextProps){
    console.log(nextProps); 
    //next:顾名思义是接收到的next->props,输出的是上面方法中的newState的值
    console.log(this.props);
    //cur:是当前的props的值,而这个由于浅复制,这个值被改变了
}

为了让数据变化更加可测,我们应当使用深复制相关,让我自己的数据更加安全

处理方法一:es7 ... 的方式

更新一个 Object ,则:

let obj = {
    a: 0,
    b: 20,
}
obj = {...obj, a: obj.a + 1}

而不是:

obj.a = obj.a + 1

同样的为了避免对 Object 的 in-place editing,数组也是一样:

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 ,我们可以让应用程序状态是 不可变(Immutable) 的。

...主要事项及要求

let obj = {
    a: 20,
    arr: [1, 2]
};
let obj1 = { ...obj }; //于obj1=obj一样

obj1.a = 2 //不能使用这样赋值
//变化 浅复制
console.log(obj) // { a:2, arr: [1,2] };
//...必须改用这样的赋值形式
obj1 = { ...obj1 , a:2 }
//深复制
console.log(obj) // { a:20, arr: [1,2] };
console.log(obj1) // { a:2, arr: [1,2] };

...与Object.assign属于一个道理(这里和层级相关)

//你可以将其转化为
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

之前方式的多层嵌套

//深复制(类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
            }
        }
    }
};
//浅复制
newState.main.itemObj[id].prop = action.param.props_str;
//immutable.js方式
...参考immutable的api吧,暂时就不提供了--!