1. 回顾
JavaScript中有6种数据类型:数字(number)、字符串(string)、布尔值(boolean)、undefined、null、对象(Object)。其中对象类型包括:数组(Array)、函数(Function)、还有两个特殊的对象:正则(RegExp)和日期(Date)。
对于引用类型来说其存放于堆内存,当其被做一个 LSH 引用时,其只是引用了堆内存的地址,而并非跟值类型的数据一样开辟一块新内存,所以如果想要拷贝引用类型的数据往往比较繁琐,数组、函数、对象、Map 都属于引用类型。
2. 浅拷贝
浅拷贝相当于仅对最外层元素做了拷贝,假如对象中的某个值仍是一个引用类型的值,那么嵌套的这个引用类型将不会被拷贝。
数组的浅拷贝可以使用 slice()
来实现,而对象的浅拷贝可以通过遍历对象实现,也可以通过 Object.assign
来实现:
1 2 3 4 5 6 7 8 9
| function simpleCopy(obj) { if (obj instanceof Array) { return obj.slice(); } else if (obj instanceof Object) { return Object.assign({}, obj); } else { return obj } }
|
3. 深拷贝
为了弥补浅拷贝的缺点,那么我们就需要对元素进行深拷贝,最简单粗暴的方式就是使用 JSON.stringify()
这个方式来将对象转换为字符串,再通过 JSON.parse()
来转换。但是这样的话 undefined
就会被忽略掉,同时原型链也会丢失,举一个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function Dog(name) { this.name = name; } Dog.prototype.brak = function() { console.log("wangwangwang"); };
let obj = { a: 1, b: { c: 2, d: 3 }, e: undefined, f: null, g: new Dog("huahua") };
let newObj = JSON.parse(JSON.stringify(obj)); console.log("newObj: ", newObj); newObj.g.brak();
|
输出:
1 2
| newObj: { a: 1, b: { c: 2, d: 3 }, f: null, g: { name: 'huahua' } } TypeError: newObj.g.brak is not a function
|
为了解决这个问题,我们可以使用递归来解决,只要判断每一个值如果是 Object
类型,就对其递归进行深拷贝。如下的深拷贝即可实现对原型链方法的拷贝:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function deepClone(obj) { let newObj; let type = Object.prototype.toString.call(obj).slice(8, -1); console.log('type: ', type); if (type === "Array") { newObj = []; } else if (type === "Object") { newObj = {}; } else { return obj; } for (let key in obj) { if (obj[key] instanceof Object) { newObj[key] = deepClone(obj[key]); } else { newObj[key] = obj[key]; } } return newObj; }
|