js中实现对象拷贝有哪些方法!
首先说下浅拷贝与深拷贝区别
- 浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
- 深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
一json方法
- JSON对象是深度克隆,方法是首先
- JSON.stringfy()转换为json字符串,然后在JSON.parse()转为json数组
缺点 如果你拷贝的对象有函数,函数无法被拷贝下来
无法发拷贝copyObj对象原型链上的属性和方法
var obj1={
x:1,
y:{
a:1,
b:0,
c:[1,2,3]
}
}
var obj2=obj1;
console.log(obj2==obj1)
// 相同引用 结果为true 复制对象指针 还指向同一对象
var obj3=JSON.parse(JSON.stringify(obj1))
console.log(obj3==obj1)
console.log(obj3)
// 不同引用 结果为false 通过json方法复制地址不一样
二JQuery extend()方法
- 拓展对象本身 在这个空间上增加新的函数
var obj1={
x:1,
y:{
a:1,
b:0,
c:[1,2,3]
}
}
var obj2=$.extend({},obj1)
console.log(obj2==obj1)
// 复制后地址不一样
console.log(obj2)
------------结果------------
false
{x: 1, y: {…}}
三 object.create()方法
- 复制对象存在在obj原型prototype中
var obj1={ x:1, y:{ a:1, b:0, c:[1,2,3] } } var obj2=Object.create(obj1) console.log(obj2==obj1) // 复制后地址不一样 console.log(obj2) -------------结果----------- false {} __proto__: Object
四for循环遍历方法
- 浅拷贝:
只是拷贝了基本类型的数据;然而引用类型数据, 只是复制了指针,复制后也是会发生引用。
除了这个是浅拷贝,本文章介绍的其他方法都是深拷贝。
var obj2={}
for(var i in obj1){
// for in 会遍历对象属性 包括实例原型的属性需要访问 可枚举属性
obj2[i]=obj1[i]
}
console.log(obj2)
obj2.y.c.push(4)
// 给obj2新数组添加一个元素2 会同步反应到旧数组中
console.log(obj2.y.c)
console.log(obj1.y.c)
// 浅拷贝 就是复制地址 修改内存数据
--------结果---------
{x: 1, y: {…}}
(4) [1, 2, 3, 4]
(4) [1, 2, 3, 4]
- 深拷贝
- 深拷贝, 就是遍历那个被拷贝的对象。判断对象里每一项的数据类型。如果不是对象类型, 就直接赋值, 如果是对象类型, 就再次调用递归的方法去赋值。
var obj1 = {
x: 1,
y: {
a: 1,
b: 0,
c: [1, 2, 3]
}
}
function getClass(o) {
// 判断数据类型
return Object.prototype.toString.call(o).slice(-8, 1)
}
function deepCopy(obj1) {
var result, oClass = getClass(obj1);
// 声明一个result oClass=getclass(obj1)判断结果
if (oClass == "Object") result = {};
// 如果说是判断是对象 继续遍历
else if (oClass == "Array") result = {}
// 如果传入判断是数组 继续遍历
else return obj1
// 如果是基本类型 直接返回
for(var i in obj1){
var copy=obj1[i]
if(getClass(copy)=="Object") result[i]=deepCopy(copy)
//递归方法 对象继续变量obj[i] 下一级还是对象 就obj1[i][i]
else if(getClass(copy)=="Array") result[i]=deepCopy(copy)
else result[i]=copy
// 基本数据类型赋值给属性
}
return result
}
var obj2=deepCopy(obj1)
console.log(obj2)
--------结果---------------
{x: 1, y: {…}}x: 1y: {a: 1, b: 0, c: Array(3)}__proto__: Object
五. 原型链继承方法
function Father(){ this.say="hi" this.fn=function(){ return this.say } } Father.prototype.eat=function(){ console.log("吃") } function Son(){ this.play=function(){ console.log("play game") } } //通过原型 继承 父类 公共属性 Son.prototype=new Father() var s=new Son() console.log(s) console.log(s.say) console.log(s.fn()) --------------结果------------ Son {play: ƒ} hi hi
六Object.assign()方法
语法:Object.assign(target, …sources)
(1)target—目标对象
(2)sources—源对象如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。
Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
String类型和 Symbol 类型的属性都会被拷贝。
在出现错误的情况下,例如,如果属性不可写,会引发TypeError,如果在引发错误之前添加了任何属性,则可以更改target对象。
注意:Object.assign 不会在那些source对象值为 null 或 undefined 的时候抛出错误。
const targetObj = { a1: 1 };
const sourceObj1 = { a2: 2 };
const sourceObj2 = { a3: 3 };
Object.assign(targetObj, sourceObj1, sourceObj2);
target // {a1:1, a2:2, a3:3}
Object.assign()拷贝的是属性值,假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
obj2.b.c = 3; // 此时两个对象下b指向的是一个位置。
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
// 可以实现深拷贝
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1)); // 此时相当于重建了对象
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}
合并具有相同属性的对象,属性被后续参数中具有相同属性的其他对象覆盖
// 普通合并对象
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。
// 此时对象之间有相同值
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };
const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 } 后边的覆盖前边的
继承属性和不可枚举属性是不能拷贝的
const obj = Object.create({foo: 1}, { // 根据Object.create()foo是个继承属性。
bar: {
value: 2 // 根据这个写法,bar 是个不可枚举属性 。
},
baz: {
value: 3,
enumerable: true // baz 是个自身可枚举属性。
}
});
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
五、for循环遍历方法
1.浅拷贝:
只是拷贝了基本类型的数据;然而引用类型数据, 只是复制了指针,复制后也是会发生引用。 除了这个是浅拷贝,本文章介绍的其他方法都是深拷贝。
var obj = {
x: 1,
y: {
a: 1,
b: 0,
c: [1, 2, 3]
}
};
var obj2 = {};
for (var i in obj) { //for in 会遍历对象的属性,包括实例中和原型中的属性。(需要可访问,可枚举属性)
obj2[i] = obj[i];
}
console.log(obj2);
obj2.y.c.push(4); //给新数组添加一个元素4,会同步反映在新旧数组中
console.log(obj2.y.c); // [1,2,3,4]
console.log(obj.y.c); // [1,2,3,4] 浅拷贝只是复制了地址,修改是内存中的数据
2.深拷贝:
遍历那个被拷贝的对象。判断对象里每一项的数据类型。如果不是对象类型, 就直接赋值, 如果是对象类型,就再次调用递归的方法去赋值。
var obj = {
x: 1,
y: {
a: 1,
b: 0,
c: [1, 2, 3]
}
};
function getClass(o) { //判断数据类型
return Object.prototype.toString.call(o).slice(8, -1);
}
function deepCopy(obj) {
var result, oClass = getClass(obj);
if (oClass == "Object") result = {}; //判断传入的如果是对象,继续遍历
else if (oClass == "Array") result = []; //判断传入的如果是数组,继续遍历
else return obj; //如果是基本数据类型就直接返回
for (var i in obj) {
var copy = obj[i];
if (getClass(copy) == "Object") result[i] = deepCopy(copy); //递归方法 ,如果对象继续变量obj[i],下一级还是对象,就obj[i][i]
else if (getClass(copy) == "Array") result[i] = deepCopy(copy); //递归方法 ,如果对象继续数组obj[i],下一级还是数组,就obj[i][i]
else result[i] = copy; //基本数据类型则赋值给属性
}
return result;
}
var obj2 = deepCopy(obj);
console.log(obj2);