浅克隆表示只克隆引用对象的第一层,第二层后的对象依然使用原先对象的堆栈。比如 Object.assign , ... , slice:
function a(){
const obj = {
foo : {
bar : 'bar'
}
}
const obj2 = Object.assign({} , obj)
obj2.foo.bar = '1'
console.log(obj);
}
a() // { foo: { bar: '1' } }
function b(){
const arr = [
{
bar : 'bar'
}
]
const arr2 = [...arr]
arr2[0].bar = '1'
console.log(arr);
}
b() // [ { bar: '1' } ]
function c(){
const arr = [
{
bar : 'bar'
}
]
const arr2 = arr.slice(0)
arr2[0].bar = '1'
console.log(arr);
}
c() // [ { bar: '1' } ]
深克隆表示不使用任何原先的堆栈,比如 JSON.parse(JSON.stringify()) ,它将对象转换为字符串再解析为一个新的对象:
function a(){
const obj = {
foo : {
bar : 'bar'
}
}
const obj2 = JSON.parse(JSON.stringify(obj))
obj2.foo.bar = '1'
console.log(obj);
}
a() // { foo: { bar: 'bar' } }
手写一个深克隆方法是一个经典的面试题,主要考察递归算法:
function deepClone(val) {
if (typeof val !== 'object') {
return val
}
const res = {}
for (let key in val) {
res[key] = deepClone(val[key])
}
return res;
}
const obj = {
foo: {
bar: 'bar'
}
}
const obj2 = deepClone(obj)
obj2.foo.bar = '1'
console.log(obj2); // { foo: { bar: '1' } }
console.log(obj); // { foo: { bar: 'bar' } }
上面这个例子是有问题的,它涉及到这个考题的另一个考点:原型链。还是刚才这个例子,如果我们要深克隆的 obj 对象的原型链上存在一个对象 {a : '1'} ,当克隆完成后,原型链上的对象会被转换为普通对象:
const obj = {
foo: {
bar: 'bar'
}
}
Object.setPrototypeOf(obj, { a: '1' })
const obj2 = deepClone(obj)
obj2.foo.bar = '1'
console.log(obj2); // { foo: { bar: '1' }, a: '1' }
console.log(obj); // { foo: { bar: 'bar' } }
之所以会出现这种情况是因为 for in 方法不会区分是否为原型链上的元素,原型链上的 {a : '1'} 同样具有一个迭代器,它会被 for in 方法处理。
为了不处理原型链上的元素,我们需要对被 for in 遍历的元素加上一些判断,Object.prototype.hasOwnProperty() 方法帮助我们判断某个属性是否是该对象自身持有的属性,而非从原型链上继承来的属性:
function deepClone(val) {
if (typeof val !== 'object') {
return val
}
const res = {}
for (let key in val) {
if(val.hasOwnProperty(key)){
res[key] = deepClone(val[key])
}
}
return res;
}
现在我们的实例可以正常通过了:
function deepClone(val) {
if (typeof val !== 'object') {
return val
}
const res = {}
for (let key in val) {
if(val.hasOwnProperty(key)){
res[key] = deepClone(val[key])
}
}
return res;
}
const obj = {
foo: {
bar: 'bar'
}
}
Object.setPrototypeOf(obj, { a: '1' })
const obj2 = deepClone(obj)
obj2.foo.bar = '1'
console.log(obj2); // { foo: { bar: '1' }}
console.log(obj); // { foo: { bar: 'bar' } }
参考: