js实现深拷贝
自己创建一个新的对象,来接收你要重新复制或引用的对象值。如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,如果其中一个对象改变了这个内存中的地址,肯定会影响到另一个对象。
简单来说:一个对象obj1的属性是引用类型,把这个对象赋值个另一个变量obj2,如果修改obj2的属性(引用类型),则obj1的属性也会被修改
js中的浅拷贝方法
浅拷贝方法有:object.assign()、拓展运算符、数组的concat()、数组的slice(),这些都是浅拷贝,一旦出现了修改变量的引用类型的属性的值,另一个变量也会随之更改,浅拷贝只能拷贝一层对象。如果存在对象的嵌套,那么浅拷贝将无能为力
object.assign()
object.assign()方法:是将所有可枚举(Object.propertyIsEnumerable() 返回 true)和自有(Object.hasOwnProperty() 返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象。
object.assign 的语法为:Object.assign(target, …sources)。该方法的第一个参数是拷贝的目标对象,后面的参数是拷贝的来源对象(也可以是多个来源)。
let obj1={interest:{study:'English'}}
let obj2=Object.assign({},obj1)
console.log(obj2) // {interest:{study:'English'}}
obj2.interest.study='PE'
console.log(obj2.interest.study) // 'PE'
console.log(obj1.interest.study) // 'PE'
object.assign 方法注意点:
● 它不会拷贝对象的继承属性;
● 它不会拷贝对象的不可枚举的属性;
● 可以拷贝 Symbol 类型的属性。
let obj={
name:"lala",
[Symbol('key1')]:"yyy"
}
Object.defineProperty(obj,'notShow',{
value:"不可枚举属性",
enumerable:false
})
let obj2=Object.assign({},obj)
console.log(obj)//{ name: 'lala', [Symbol(key1)]: 'yyy' }
console.log(obj2) //{name: 'lala', Symbol(key1): 'yyy'} 没有不可枚举属性
拓展运算符
拓展运算符可以将数组或者对象转为用逗号分隔的参数序列
语法:let cloneObj={…obj}
let obj={
name:"lala",
[Symbol('key1')]:"yyy"
}
Object.defineProperty(obj,'notShow',{
value:"不可枚举属性",
enumerable:false
})
let obj2={...obj}
console.log(obj2) //{name: 'lala', Symbol(key1): 'yyy'} 没有不可枚举属性
缺点:
● 它不会拷贝对象的继承属性;
● 它不会拷贝对象的不可枚举的属性;
● 可以拷贝 Symbol 类型的属性。
js中深拷贝的原理和实现
1.项目中常见的JSON.parse(JSON.stringify())来处理
let obj1 = { a:1, b:[1,2,3] }
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log(obj2); //{a:1,b:[1,2,3]}
obj1.a = 2;
obj1.b.push(4);
console.log(obj1); //{a:2,b:[1,2,3,4]}
console.log(obj2); //{a:1,b:[1,2,3]}
手写实现深拷贝
function deepClone(obj){
if(typeof obj !=='object'){
return obj
}
let result=Array.isArray(obj)?[]:{}
for (const key in obj) {
result[key]=typeof obj[key] !=='object'?obj[key]:deepClone(obj[key])
}
return result
}
let obj={a:{b:1}}
let obj1=deepClone(obj)
obj.a.b=2
console.log(obj) //{a: {b: 2}}
console.log(obj1) // {a: {b: 1}}
但是还是存在以下问题:
1.这个深拷贝函数并不能复制不可枚举的属性以及 Symbol 类型;
2.Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;
3.对象的属性里面成环,即循环引用没有解决。
手写实现深拷贝改进版
问题 | 解决方案 |
---|---|
不能复制不可枚举的属性以及 Symbol 类型; | 使用 Reflect.ownKeys 方法获取 |
拷贝 Date 引用类型会变成日期字符串; | 直接生成一个新的实例返回 |
拷贝 RegExp 引用类型会变成空对象; | 直接生成一个新的实例返回 |
不能拷贝对象的原型链 | 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性,以及对应的特性(枚举、value等),结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链; |
对象的属性里面成环,即循环引用问题 | 利用 WeakMap 类型作为 Hash 表,因为 WeakMap 是弱引用类型,可以有效防止内存泄漏,作为检测循环引用很有帮助,如果存在循环,则引用直接返回 WeakMap 存储的值。 |
先了解几个方法:
语法:Object.create(proto, propertiesObject)
Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype),第一个参数为新创建对象的原型对象,第二个参数为新创建的对象添加指定的属性值和对应的属性描述符
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.constructor=Rectangle
Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors() 用来获取一个对象的所有自身属性(非继承属性)的描述符。
语法:Object.getOwnPropertyDescriptors(obj)
返回值:所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
let obj={name:"啦啦啦",age:18,Symbol('key':"yyy")}
Object.defineProperty(obj,'innumerable', {
enumerable: false,
value: '不可枚举属性',
})
console.log(Object.getOwnPropertyDescriptors(obj))
//打印结果
//{
// name: {
// value: '啦啦啦',
// writable: true,
// enumerable: true,
// configurable: true
// },
// age: { value: 18, writable: true, enumerable: true, configurable: true },
// key: {
// value: 'yyy',
// writable: true,
// enumerable: true,
// configurable: true
// },
// innumerable: {
// value: '不可枚举属性',
// writable: false,
// enumerable: false,
// configurable: false
// }
//}