js中常会出现数组和对象的拷贝,拷贝一般是对引用值而言的。
1.浅拷贝
特点: 浅拷贝只是将栈内存里的值拷贝给新对象,对于引用值而言它存在栈内存的只是它的存储数据的堆内存地址。所以浅拷贝后引用类型的值会相互影响。
实现浅拷贝的方法
(1)常规遍历赋值
function clone(source){
let target = Object.prototype.toString.call(source)="[object Object]"?{}:[]
for(let key in source){
target[key] = source[key]
}
return target
}
(2)Object.assign()(适用于对象)
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
let source = {key:"test"};
let target = Object.assign({},source)
(3)Array.prototype.concat()(适用于数组拷贝)
var arr = [1,test:{a:"1"}];
var arr1 = arr.concat()
(4)Array.prototype.slice()(适用于数组)
var arr = [1,test:{a:"1"}];
var arr1 = arr.slice()
2.深度拷贝
特点: 深度拷贝是值的完全拷贝,拷贝前后两个引用值互不影响
(1)乞丐版深度拷贝
let obj = {a:b{c:1}};
let obj1 = JSON.parse(JSON.stringify(obj))
不足点:
a.无法实现对函数 、Date、RegExp等特殊对象的克隆
b.会抛弃对象的constructor,所有的构造函数会指向Object
c.对象有循环引用,会报错
(2)只考虑普通对象和数组的深度拷贝
// 深度拷贝
function deepClone(source){
if(source===null||typeof source!=="object"||typeof source!=="function"){
return source
}
var target = source instanceof Array?[]:{}
for(key in source){
if(source.hasOwnProperty(key)){
target[key] = typeof source[key] === "object"?deepClone[source[key]]:source[key]
}
}
return source;
}
(3)考虑特殊对象的深拷贝
// 获取当前属性值的类型
function getType(ele){
var template = {
"[object Array]":"array",
"[object Object]":"object",
"[object Number]":"number",
"[object Undefined]":"undefined",
"[object Boolean]":"boolean",
"[object String]":"string",
"[object Date]":"Date",
"[object RegExp]":"RegExp"
}
if(targ === null){
return "null";
}else if(typeof targ == "object"){
var str = Object.prototype.toString.call(targ);
return template[str];
}else{
return typeof targ;
}
}
// 获取正则表达式的flags(可能有组合的flag)
function getRegFlag(reg){
var flags = '';
if (reg.global) flags += 'g';
if (reg.ignoreCase) flags += 'i';
if (reg.multiline) flags += 'm';
return flags;
}
// 深度拷贝
function deepClone(oldObj){
var newObj;
var type = getType(oldObj)
switch(type){
case "boolean":
case "number":
case "string":
case "undefined":
case "null":{
return oldObj;
break;
}
case "symbol":{
return Symbol(Symbol.keyFor(oldObj).toString());
break;
}
case "regExp":{
newObj = new RegExp(oldObj.source, getRegFlag(oldObj));
if (oldObj.lastIndex) newObj.lastIndex = oldObj.lastIndex;
break;
}
case "date":{
newObj = new Date(oldObj.getTime());
break;
}
case "function":{
newObj = eval(oldObj.tostring());
}
case "array":{
newObj = [];
for(var prop in oldObj){
if(oldObj[prop]!==oldObj){ // 處理循環引用情況
newObj[prop]=deepClone(old[prop]);
}else{ // 若循環引用則將新對象直接賦值給子元素
newObj[prop] = newObj;
}
}
break;
}
default:{
var proto = Object.getPrototypeOf(oldObj);
newObj = Object.create(proto);
for(var prop in oldObj){
if(oldObj[prop]!==oldObj){ // 處理循環引用情況
newObj[prop]=deepClone(old[prop]);
}else{ // 若循環引用則將新對象直接賦值給子元素
newObj[prop] = newObj;
}
}
break;
}
}
return newObj;
}