Javascript基本类型不涉及浅拷贝深拷贝的概念,都是存储在栈中的。除了基本类型数据,其他的都是引用类型,是保存在堆中的。栈只是存储了引用地址,指向堆内存中的地址。
浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
引用类型的 赋值不等于浅拷贝和 浅拷贝是一层数据的拷贝,深拷贝是多层的拷贝。https://segmentfault.com/q/1010000020437355
const a = { name:'a' }
const b = a //赋值是将某一数值或对象赋给某个变量的过程,这不是浅拷贝
b.name = 'b' // 影响了原来的对象
console.log(b.name) // b
- 对象 ```javascript const ob2 = { …obj1 }
const obj2 = Object.assign({},obj1);
- 数组
```javascript
const arr2 = [...arr1]
const arr2 = arr1.slice()
const arr2 = arr1.concat()
const arr2 = Array.from(arr1)
深拷贝
JSON.stringify
let newObj = JSON.parse(JSON.stringify(obj));
缺点:
- 1.如果json里面有时间对象,则序列化的结果为字符串的形式;
- 2.如果json里有
RegExp
、Error
对象,则序列化的结果将只得到空对象**{}**
; - 3.如果json里有
function
,undefined
,则序列化的结果会把 function,undefined 属性丢弃; - 4.如果json里有
NaN
、Infinity
和-Infinity
,则序列化的结果会变**null**
; - 5.如果json里有对象是由
构造函数
生成的,则序列化的结果会丢弃对象的 constructor; - 6.如果对象中存在
循环引用
的情况也无法实现深拷贝(报错) - 参考:https://segmentfault.com/a/1190000020297508
基础版本
function clone(source) {
if (typeof source === 'object') {
let target = Array.isArray(source) ? [] : {};
for (const key in source) { // 数组也可以用in
target[key] = clone(source[key]);
}
return target;
}
return source;
};
- 下一步要解决循环引用问题
用map存一下,把当前对象当key,克隆对象当value
function clone(source, map = new Map()) {
if (typeof source === 'object') {
if (map.get(source)) {
return map.get(source); // map
}
let target = Array.isArray(source) ? [] : {};
map.set(source, target);// map
for (const key in source) {
target[key] = clone(source[key], map);
}
return target;
}
return source;
};
可以使用 WeakMap 存储,WeakMap的键是弱引用的,其键是对象。
for..in 循环遍历数组的效率过低,可以改用 while 循环
function clone(source, map = new Map()) {
if (typeof source === 'object') {
if (map.get(source)) {
return map.get(source);
}
let target = Array.isArray(source) ? [] : {};
map.set(source, target);
let keys = Object.keys(source);
let length = keys.length;
let index = 0;
while (index < length) {
target[keys[index]] = clone(source[keys[index]], map);
index++;
}
return target;
}
return source;
};
// 仅遍历对象自身可枚举的属性(包括字符串属性和 Symbol 属性)
Reflect.ownKeys(input).forEach(key => {
if (input.propertyIsEnumerable(key)) {
output[key] = copy(input[key])
}
})
处理其他数据类型
// 可以继续遍历的类型
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]';
// 不可以继续遍历的类型
//基本类型
const boolTag = '[object Boolean]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]'; // 特殊处理
// 引用类型
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]'; // 特殊处理
const funcTag = '[object Function]'; //特殊处理
// 可以继续遍历的类型
const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
判断是否为对象
function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
}
获取类型
function getType(target) {
return Object.prototype.toString.call(target);
}
```javascript function getInit(target) { const Ctor = target.constructor; return new Ctor(); }
// Symbol function cloneSymbol(target) { // symbol 基本类型也要复制,为了保证唯一性 return Object(Symbol.prototype.valueOf.call(target)); }
// 正则 function cloneReg(target) { const flags = /\w$/; ///w代表匹配字母或数字或下划线,零次或多次 const result = new target.constructor(target.source, flags.exec(target)); result.lastIndex = target.lastIndex; return result; }
function cloneOtherType(target, type) {
switch (type) {
case boolTag:
case numberTag:
case stringTag: // 可能为包装类,这三个类型也要处理
case errorTag:
case dateTag:
return new source.constructor(target);
case regexpTag:
return cloneReg(target);
case symbolTag:
return cloneSymbol(target);
case funcTag:
return cloneFunction(target);
default:
return null;
}
}
```javascript
function clone(target, map = new WeakMap()) {
// 基本类型
if (!isObject(target)) {
return target;
}
// 初始化
const type = getType(target);
let cloneTarget;
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type);
} else {
return cloneOtherType(target, type);
}
// 防止循环引用
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
// 克隆set
if (type === setTag) {
target.forEach(value => {
cloneTarget.add(clone(value, map));
});
return cloneTarget;
}
// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value, map));
});
return cloneTarget;
}
// 对象和数组
const keys = Object.keys(target);
let length = keys.length;
let index = 0;
while (index < length) {
const key = keys[index]
cloneTarget[key] = clone(target[key], map);
index++;
}
return cloneTarget;
};