对象的扩展与深、浅层拷贝
分类:javascript
先看一下周董的大图,遍听歌边写代码难道不享受嘛
1、对象的扩展
这部分只介绍开发中用的比较多的地方
对象属性名的简写
//当key、value相同的时候我们可以简写成这样
const name = '1212'
let obj = { name }
现在我们有一个对象和字符串,我们怎么让这个字符串作为对象的key呢
let s = 'school'
let obj = {
name: 1212,
age: 3434,
[school]: '我是学校',
//es6中对象方法的简写
stydy(){
console.log(this.name)
}
}
Object.is
Object.is主要用来判断2个对象是否相等
let obj1 = {name:12},obj2 = {name:12}
console.log(obj1 == obj2)//false
console.log(Object.is(NAN,NAN))//true
console.log(Object.is(obj1,obj2))//false
上述代码最后一行为什么会返回个false呢???
因为这里我们定义了2个对象,我们知道对象在js中是引用数据类型的,在栈内存中存放的是引用地址,这就相当于( 也可以说new Object() )我们在内存中开辟了2块新地址来存放这两个对象并且指向2个不同的堆内存地址;所以对于对象判断是否相等我们主要是判断他的堆内存地址是否相等
那现在我们就想要哲两个对象相等该怎么办,可以这样:这里我们把obj1赋值给了obj2,就指向了同一个地址
let obj2 = obj1
console.log(Object.is(obj1,obj2))//true
Object.assign
let a = {name:12}
let b = {age:34}
//把a合并到b对象中,如果有属性相同的话,后面的会覆盖前面的值
Object.assign(b,a)
console.log(b)//{name:12,age:34}
遍历对象
let obj = {name:12,age:34,sex:2}
//第一种
for(let key in obj){
console.log(obj[key])// 12 34 2
}
//第二种:Object.keys返回是一个数组,里面是每个对象的key
Object.keys(obj).map(item => {
console.log(item)
})
//第三种
Object.getOwnPropertyNames(obj).forEach(element => {
console.log(element)
})
//第四种
Reflect.ownKeys(obj).forEach(e => {
console.log(e)
})
2、深层拷贝和浅层拷贝
深层拷贝就是假设我们定义了2变量,将a的值赋值给b,当a变化的时候,b的值没有变化,2个值互相不影响
let a = 1
let b = a
a = 2
console.log(a,b)
简单的数据类型使用Object.assign是可以进行浅层拷贝的
let target = {a:5}
let source = {a:1,b:2}
Object.assign(target,source)
console.log(target)
现在来一个稍微复杂一点的数据结构,合并后在target对象里面应该有d这个属性,但是却没有,这个就是Object.assign的一个缺点,他只是一个浅拷贝
let target = {
a: {
b: {
c: 6
},
e: 7,
f: 8,
d: 9
}
}
let source = {
a: {
b: {
c: 1
},
e: 2,
f: 3
}
}
Object.assign(target,source)
console.log('我是用assign拷贝出来的',target)
这里我们有两种方式来实现深层拷贝
1、JSON.stringify()、JSON.parse()
现在这两个变量也是互不干扰的
let obj = {name: 12, age: 34}
let str = JSON.stringify(obj)
let obj1 = JSON.parse(str)
console.log(obj1)
2、手动实现
里面注释写的很清楚就不解释了
// 检查数据类型 let checkDataType = data => { //这里使用typeof进行检查是不严谨的,他只能判断基本数据类型,对引用数据类型的会有问题(比如typeof Array ====> object) //slice是对返回的结果进行截取,去掉中括号和前面返回的 return Object.prototype.toString.call(data).slice(8,-1) } //暂时只考虑数组和对象 let deepClone = target => { //先检查数据类型 let targetType = checkDataType(target) let result //对数据类型进行初始化 if (targetType === 'Object') { result = {} } else if (targetType === 'Array') { result = [] } else { //如果是基本的数据类型直接return return target } //遍历 for (let i in target) { let value = target[i] //判断value里面的数据类型(里面是否有数组、对象),并进行递归(就是调用当前函数的本身) let valueType = checkDataType(value) if (valueType === 'Object' || valueType === 'Array') { result[i] = deepClone(value) } else {//代表是一个基本的数据类型 result[i] = value } } return result } let arr = [1,3,{name:'xxx',age:'yyy'}] let arr2 = deepClone(arr) arr2[1] = 5 console.log(arr) console.log(arr2)
或者也可以使用下面这个方法
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
if (typeof obj !== "object") return obj;
// 是对象的话就要进行深拷贝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}