1.对象的深浅拷贝(含数组)
- 深浅拷贝都是对于JS中的引用类型而言的
- 浅拷贝->只复制指针,不复制值,如果拷贝后的对象发生变化,原对象也会发生变化
- 深拷贝->在计算机中开辟了一块新的内存地址用于存放复制的对象。
实现深拷贝的方法不多,主要有以下2种:
1.) 利用 JSON 对象中的 parse 和 stringify (若对象中含有函数,该方法不能使用。undefined、function、symbol 会在转换过程中被忽略。。。)
var obj = {
name:'zf',
age:'18'
}
let deepColone = JSON.parse(JSON.stringify(obj))
console.log(deepColone) //{name:'zf',age:'18'}
console.log(obj) //{name:'zf',age:'18'}
obj.name='lzf'
console.log(deepColone) //{name:'zf',age:'18'}
console.log(obj) //{name:'lzf',age:'18'}
2.)利用递归的方法,简单粗暴看代码
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){ // 遍历目标
if(source.hasOwnProperty(keys)){ //判断目标的自身属性为true
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {}; //再次判断复制的目标是数组还是对象
targetObj[keys] = deepClone(source[keys]); //进行递归
}else{ // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
ES6也引入了Object.assign和...展开运算符,实现对对象的拷贝,他们是深拷贝还是浅拷贝呢?
1.)Object.assign()是浅拷贝
let originObj = {
name:'zf',
age:18
}
let coloneObj = Object.assign(originObj)
console.log(originObj === coloneObj) //true
originObj.name='lzf'
console.log(originObj) //{name: "lzf", age: 18}
console.log(coloneObj) //{name: "lzf", age: 18}
2.)...展开运算符,第一层深拷贝
//简单对象
let originObj = {
name:'zf',
age:18
}
let coloneObj = {...originObj}
console.log(originObj === coloneObj) //false
originObj.name='lzf'
console.log(originObj) //{name:'lzf',age:18}
console.log(coloneObj) //{name:'zf',age:18}
//复杂数组
let originArr = [1,2,3,[4,5,6],{a:'zf'}]
let coloneArr = [...originArr]
console.log(originArr === coloneArr) //false
originArr.push(5)
originArr[3].push(7)
originArr[4].b='lzf'
console.log(originArr) //[1,2,3,[4,5,6,7],{a:'zf',b:'lzf'},5]
console.log(coloneArr) //[1,2,3,[4,5,6,7],{a:'zf',b:'lzf'}]
数组除了扩展运算符还有2种方法concat和slice对原数组进行拷贝,这2种方法不会改变原数组,而是会返回一个新的数组。
1.)arr.concat()第一层深拷贝
// 复杂数组
let originArr = [1,2,3,[4,5,6],{a:'zf'}]
let coloneArr = originArr.concat()
console.log(originArr === coloneArr) //false
originArr[0]='bb'//第一层浅拷贝
originArr[3].push(10)//第二层深拷贝
originArr[4].a='lzf'//第二层深拷贝
看结果。。。
console.log(originArr) //['bb',2,3,[4,5,6,10],{a:'lzf'}]
console.log(coloneArr) //[1,2,3,[4,5,6,10],{a:'lzf'}]
2.)arr.slice()第一层深拷贝
let originArr = [1,2,3,[4,5,6],{a:'zf'}]
let coloneArr = originArr.slice()
console.log(originArr === coloneArr) //false
originArr.push(5)//第一层深拷贝
originArr[3].push(7)//第二层浅拷贝
originArr[4].b='lzf'//第二层浅拷贝
看结果。。。
console.log(originArr) //[1,2,3,[4,5,6,7],{a:'zf',b:'lzf'},5]
console.log(coloneArr) //[1,2,3,[4,5,6,7],{a:'zf',b:'lzf'}]
总结:Object.assign()是浅拷贝, 展开运算符,slice, concat,对第一层是深拷贝,第二层是浅拷贝,只拷贝了引用,不拷贝值。
2. es6对象的扩展
1.)属性的简洁表示法
let birth = '2000/01/01';
const Person = {
name: '张三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
2.)属性名表达式
let propKey = 'foo';
let obj = {
[propKey]: true,
['a' + 'bc']: 123
};
3.)属性的课枚举性和遍历
- Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
// value: 123,
// writable: true,
// enumerable: true, //是否可枚举
// configurable: true
// }
for in,Object.keys(),JSON.stringify(),Object.assign(),会忽略enumerable为false的属性。 for in会返回自身+继承的属性,其他三个会忽略继承属性。。对象原型的toString方法,以及数组的length属性,就通过“可枚举性”,从而避免被for...in遍历到,我们只关心对象自身的属性。所以,尽量不要用for...in循环,而用Object.keys()代替
Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable
// false
Object.getOwnPropertyDescriptor([], 'length').enumerable
// false