对象API集合

305 阅读3分钟

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