深浅拷贝与react之immutable.js

2,919 阅读3分钟

不洗碗工作室--nikouml

转载请标明出处

深,浅拷贝探究竟

深拷贝和浅拷贝只针对像Object,Array这样的复杂对象,对于基本的数据类型,则不存在深拷贝和浅拷贝的区别。浅拷贝只是复制了对象的一层属性,而深拷贝则是递归复制了所有的层级。

浅拷贝只能拷贝引用,指向的还是原来的对象,自然也会影响原来的对象。深拷贝则是在堆(数据结构老师讲过的哦~)中重新分配了内存,与原来的对象是分隔开的,互不影响。也就是浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

注意基本数据类型,不存在深浅拷贝的区别:

let a = 123
let b = a
a = 1234
console.log(a) // 1234
console.log(b) //123

简单的浅拷贝实现:

function simpleCopy(initalObj) {
 var obj = {};
 for ( var i in initalObj) {
   obj[i] = initalObj[i];
 }
 return obj;


 }
   var obj = {
     a: "hello",
     b:{
         a: "world",
         b: 18
       },
     c:["feng", "zheng", "li"],
     d:function() {
         alert("hello world");
       }
   }
   var cloneObj = simpleCopy(obj);
   console.log(cloneObj.b);
   console.log(cloneObj.c);
   console.log(cloneObj.d);
   
   cloneObj.b.a = "changed";
   cloneObj.c = [1, 2, 3];
   cloneObj.d = function() { alert("changed"); };
   console.log(obj.b);
   console.log(obj.c);
   console.log(obj.d);

以上代码是浅拷贝的简单实现,如下是运行结果:

{ a: 'world', b: 18 }
[ 'feng', 'zheng', 'li' ]
[Function: d]
{ a: 'changed', b: 18 }
[ 'feng', 'zheng', 'li' ]
[Function: d]

另外,使用ES6提供的扩展运算符(...)也能实现浅拷贝:

 let arr1 = [1, 2, 3]
 let arr2 = [...arr1]
 arr1.push(4,5,6)
 console.log(arr1) // [1, 2, 3, 4, 5, 6]
 console.log(arr2) // [1, 2, 3]

简单的实现深拷贝:

主要有JSON.parse()、JSON.stringify(),jQuery的$.extend(true, {}, obj),lodash的_.cloneDeep(obj)和_.clone(obj,true)。

下面以用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象的方法为例:

var obj1 = { body: { a: 18 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);   //{ body: { a: 18 } }
console.log(obj2);   //{ body: { a: 20 } }

React中的深浅拷贝

在React中具有保持state不变的这个原则,是我们尝试使用局部更新的方法,这样可以有效地提高react的渲染效率。那么,在保持数据不变性的前提下,我们怎么对state进行更新呢?

我们很容易想到,保持state的不变性应该很简单吧,只要我们深复制一个state,然后我们就得到了一个clone的state,我们尽管大胆地对这个clone者做任何操作好了。但是我们不要忘记了,深拷贝是递归复制了原对象的所有的层级,这样势必会使操作运行的效率低下,这种方法只是满足了state不变性的原则,但是却降低程序性能,并没有实践意义。

state的内部数据要变,我们就创建state的引用,对于不变化的数据,我们就无需对这个引用做更新。但是在实际操作中深浅拷贝的方式都不够简便,于是就有了immutable.js这个专门处理不变性数据的库来简化我们的操作。

举一个简单的例子:我们要“吃鸡”

用 chicken/EAT_CHICKEN这个reducer来说明。

运用浅拷贝的reducer:

...
case 'chicken/EAT_CHICKEN':
    newState = Object.assign({}, state, {
        chicken: [
            ...state.chicken.slice(0, action.payload),
            Object.assign({}, state.chicken[action.payload], { isEaten: true }),
            ...state.chicken.slice(action.payload + 1)
        ]
    })
    return newState;
...

运用immutable.js这个库的reducer:

import { fromJS } from 'immutable';
...
case 'chicken/EAT_CHICKEN':
    return fromJS(state).setIn(['chicken',action.payload,'isEaten'], true).toJS();
...

用了这个库之后,代码变得简洁明了,一下子便轻松解决了reducer哦!