不洗碗工作室--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哦!