通过复制属性继承

让我们来看一下另外一种继承模式——通过复制属性继承。在这种模式中,一个对象通过简单地复制另一个对象来获得功能。下面是一个简单的实现这种功能的extend()函数:

  1. function extend(parent, child) {
  2. var i;
  3. child = child || {};
  4. for (i in parent) {
  5. if (parent.hasOwnProperty(i)) {
  6. child[i] = parent[i];
  7. }
  8. }
  9. return child;
  10. }

这是一个简单的实现,仅仅是遍历了父对象的成员然后复制它们。在这个实现中,child是可选参数,如果它没有被传入一个已有的对象,那么一个全新的对象将被创建并返回:

  1. var dad = {name: "Adam"};
  2. var kid = extend(dad);
  3. kid.name; // "Adam"

上面给出的实现叫作对象的“浅拷贝”(shallow copy),与之相对,“深拷贝”是指检查准备复制的属性本身是否是对象或者数组,如果是,也遍历它们的属性并复制。如果使用浅拷贝的话(因为在JavaScript中对象是按引用传递),如果你改变子对象的一个属性,而这个属性恰好是一个对象,那么你也会改变父对象。实际上这对方法来说可能很好(因为函数也是对象,也是按引用传递),但是当遇到其它的对象和数组的时候可能会有些意外情况。考虑这种情况:

  1. var dad = {
  2. counts: [1, 2, 3],
  3. reads: {paper: true}
  4. };
  5. var kid = extend(dad);
  6. kid.counts.push(4);
  7. dad.counts.toString(); // "1,2,3,4"
  8. dad.reads === kid.reads; // true

现在让我们来修改一下extend()函数以便实现深拷贝。你需要做的事情只是检查一个属性的类型是否是对象,如果是,则递归遍历它的属性。另外一个需要做的检查是这个对象是真的对象还是数组,可以使用第三章讨论过的数组检查方式。最终深拷贝版的extend()是这样的:

  1. function extendDeep(parent, child) {
  2. var i,
  3. toStr = Object.prototype.toString,
  4. astr = "[object Array]";
  5. child = child || {};
  6. for (i in parent) {
  7. if (parent.hasOwnProperty(i)) {
  8. if (typeof parent[i] === "object") {
  9. child[i] = (toStr.call(parent[i]) === astr) ? [] : {};
  10. extendDeep(parent[i], child[i]);
  11. } else {
  12. child[i] = parent[i];
  13. }
  14. }
  15. }
  16. return child;
  17. }

现在测试时这个新的实现给了我们对象的真实拷贝,所以子对象不会修改父对象:

  1. var dad = {
  2. counts: [1, 2, 3],
  3. reads: {paper: true}
  4. };
  5. var kid = extendDeep(dad);
  6. kid.counts.push(4);
  7. kid.counts.toString(); // "1,2,3,4"
  8. dad.counts.toString(); // "1,2,3"
  9. dad.reads === kid.reads; // false
  10. kid.reads.paper = false;
  11. kid.reads.web = true;
  12. dad.reads.paper; // true

通过复制属性继承的模式很简单且应用很广泛。例如Firebug(JavaScript写的Firefox扩展)有一个方法叫extend()做浅拷贝,jQuery的extend()方法做深拷贝。YUI3提供了一个叫作Y.clone()的方法,它创建一个深拷贝并且通过绑定到子对象的方式复制函数。(本章后面将有更多关于绑定的内容。)

这种模式并不高深,因为根本没有原型牵涉进来,而只跟对象和它们的属性有关。