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);