传值还是传引用
传值还是传引用?很基础的问题,对于刚刚接触编程的同学来说,却是一个大坑。在js中,所有的对象都是通过引用来传递的。请默念一百遍。
var foo = {
bar: "bar"
}
var foo2 = foo
foo2.bar = "baz"
console.log(foo.bar) // "baz"
上面的代码中,我们改变foo2的属性的时候,其实就是改变了foo。因为在内存中,foo和foo2指向同一个instance。不信?试试就知道
console.log(foo === foo2) //"true"
拷贝
如果我们想要一个和foo一模一样的对象,但是并不指向内存中的同一个instance,怎么办呢?我们只需要重新创建一个空对象,然后把foo的属性全部复制过去就可以啦。
function copy(obj){
var res = {}
for (var i in obj){
res[i] = obj[i]
}
return res
}
var foo2 = copy(foo)
foo2.bar = "baz"
console.log(foo2.bar)
console.log(foo.bar)
下面我们的foo对象变得复杂了起来
var foo = {
bar: {
baz: "baz"
}
}
var foo2 = copy(foo)
foo2.bar.baz = "newbaz"
console.log(foo2.bar.baz) //newbaz
console.log(foo.bar.baz) //newbaz
问题出现了,foo对象中bar属性对应一个对象,我们在复制foo的过程中并没有真的复制bar对象,而是把它的引用取了过来,虽然现在foo和foo2不再是同一个对象,但是foo.bar和foo2.bar依然是同一个对象。
console.log(foo === foo2) //false
console.log(foo.bar === foo2.bar) //true
深拷贝
我们需要对对象进行深拷贝,也就是说,如果对象中的某个属性是一个对象,我们就要深入其中,将其复制,而不是取其引用。通过深度优先遍历对象树,将对象彻底的复制。
function deepcopy(obj,res){
var res = res || {};
for (var i in obj){
if (typeof obj[i] === 'object'){
res[i] = (obj[i] instanceof Array) ? [] : {};
deepcopy(obj[i],res[i]);
}else{
res[i] = obj[i]
}
}
return res;
}
var foo = {
bar: {
baz: "baz"
}
}
var foo2 = deepcopy(foo)
foo2.bar.baz = "newbaz"
console.log(foo2.bar.baz) //"newbaz"
console.log(foo.bar.baz) //"baz"
console.log(foo === foo2) //false
console.log(foo.bar === foo2.bar) //false