对象的拷贝

课后整理 2020-12-20

有如果需要将一个对象的所有属性,拷贝到另一个对象,需要手动实现。例如:

var extend = function (to, from) {
  for (var property in from) {
    to[property] = from[property];
  }
  return to;
}
extend({}, {
  a: 1
})
// {a: 1}

上面方法的问题在于,如果遇到存取器定义的属性,只会拷贝值。例如:

extend({}, {
  get a() { return 1 }
})
// {a: 1}

为了解决这个问题,我们可以通过Object.defineProperty方法来拷贝属性。

var extend = function (to, from) {
  for (var property in from) {
    Object.defineProperty(
      to,
      property,
       Object.getOwnPropertyDescriptor(from, property)
    );
  }
  return to;
}
extend({}, { get a(){ return 1 } })
// { get a(){ return 1 } })

这段代码还是有问题,拷贝某些属性时会失效。

extend(document.body.style, {
  backgroundColor: "red"
});

上面代码的目的是,设置document.body.style.backgroundColor属性为red,实际上网页的背景色并不会变红。如果用第一种简单拷贝的方法,反而能够达到目的。因此可以把两种方法结合起来,对于简单属性,就直接拷贝,对于那些通过属性描述对象设置的属性,使用Object.defineProperty方法拷贝。例如:

var extend = function (to, from) {
  for (var property in from) {
    var descriptor =  Object.getOwnPropertyDescriptor(from, property);
    if (descriptor && (  !descriptor.writable
      || !descriptor.configurable
      || !descriptor.enumerable
      || descriptor.get
      || descriptor.set)) {
      Object.defineProperty(to,  property, descriptor);
    } else {
      to[property] =  from[property];
    }
  }
}

上面的这段代码,可以很好地拷贝对象所有可遍历(enumerable)的属性。