种类
深拷贝数组
- 直接遍历
- slice()
- concat() 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
深拷贝对象
- ES6的Object.assign
- 1.JSON.parse(JSON.stringify(XXXX))
- 递归
方法一
const getType = (obj)=> {
var toString = Object.prototype.toString;
var map = {
'[object Boolean]' : 'boolean',
'[object Number]' : 'number',
'[object String]' : 'string',
'[object Function]' : 'function',
'[object Array]' : 'array',
'[object Date]' : 'date',
'[object RegExp]' : 'regExp',
'[object Undefined]': 'undefined',
'[object Null]' : 'null',
'[object Object]' : 'object',
'[object Symbol]' : 'symbol'
};
if(obj instanceof Element) {//因为对不同标签,toString会返回对应不同标签的构造函数
return 'element';
}
return map[toString.call(obj)];
}
// ES6 flags 代替此方法
// const getRegExp = re => {
// var flags = '';
// if (re.global) flags += 'g';
// if (re.ignoreCase) flags += 'i';
// if (re.multiline) flags += 'm';
// return flags;
// };
/**
* deep clone
* @param {[type]} parent object 需要进行克隆的对象
* @return {[type]} 深克隆后的对象
*/
const deepClone = oldObj => {
// 维护两个储存循环引用的数组
const oldObjArr = [];
const newObjArr = [];
const clone = oldObj => {
let newObj, proto;
const type = getType(oldObj);
switch(type){
case 'boolean':
case 'number':
case 'string':
case 'null':
case 'undefined':
case 'function':{
return oldObj;
break;
}
case 'symbol':{
return Symbol(Symbol.keyFor(oldObj).toString());
break;
}
case 'array':{
newObj = [];
break;
}
case 'regExp':{
newObj = new RegExp(oldObj.source, oldObj.flags);
if (oldObj.lastIndex) newObj.lastIndex = oldObj.lastIndex;
break;
}
case 'date':{
newObj = new Date(oldObj.getTime());
break;
}
//case 'obj':
default:{
// 处理对象原型
proto = Object.getPrototypeOf(oldObj);
// 利用Object.create切断原型链
newObj = Object.create(proto);
break;
}
}
// 处理循环引用
const index = oldObjArr.indexOf(oldObj);
if (index != -1) {// 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
return newObjArr[index];
}
oldObjArr.push(oldObj);
newObjArr.push(newObj);
/*数组和对象都可以用forin语句,虽然数组使用forin会有一个问题(具体看最下面)。
但是这里不会影响,所以这么用
*/
for (let i in oldObj) {// 递归
newObj[i] = clone(oldObj[i]);
}
return newObj;
};
return clone(oldObj);
}
方法二
const isObjFun = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function(obj, hash=weakMap()) {
if (hash.has(obj)) {
return hash.get(obj)
}
let type = [Date, RegExp, Set, Map, WeakMap, WeakSet]
if (type.includes(obj.constructor)) {
return new Obj.constructor(obj)
}
let allDesc = Object.getOwnPropertyDescriptors(obj)
// allDesc 解决 function symbol
let cloneObj = Object.create(Object.getPrototypeOf(obj, allDesc))
hash.set(obj,cloneObj)
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = isObjFun(obj[key]) ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}
核弹扩展
1 Map和WeakMap的主要区别:
Map对象的键可以是任何类型,但WeakMap对象中的键只能是对象引用
WeakMap不能包含无引用的对象,否则会被自动清除出集合(垃圾回收机制)。
WeakSet对象是不可枚举的,无法获取大小。
2 Object.getOwnPropertyDescriptors() 方法用来获取一个对象的所有自身属性的描述
a: {
value: 1
writable: true
enumerable: true
configurable: true
__proto__: Object
__proto__: Object
}
复制
3 for in 对比 Reflect.ownKeys(obj)
1 静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组
2 for in 取不到 不可枚举的key 和 symbol