现在的位置: 首页 > 综合 > 正文

面向对象的JavaScript 五 —– Javascript实现继承的方式(2)

2013年10月11日 ⁄ 综合 ⁄ 共 2589字 ⁄ 字号 评论关闭
四,封装继承
之前的代码有个很大的缺点,每次继承的时候,我们都要输入重复的代码。所以,我们可以把继承的代码独立开来,代码如下: function extend(Child, Parent) {  
var F = function(){};  
F.prototype = Parent.prototype;  
Child.prototype = new F();  
Child.prototype.constructor = Child;  
Child.uber = Parent.prototype;
}
使用继承的时候,只要像这样:
>>>extend(TwoDShape, Shape);
>>>extend(Triangle, TwoDShape);
这样继承使用起来更加便捷了。
其实这就是YUI实现继承的方式.比如你使用YUI,可以这样调用:
>>>YAHOO.lang.extend(Triangle, Shape) 

五.拷贝继承
另外还有一个方法实现继承,我们可以简单的赋值属性,代码如下:
function extend2(Child, Parent) {
var p = Parent.prototype;  
var c = Child.prototype;  
for (var i in p) {
     c[i] = p[i];  
}  
c.uber = p;
}
看出代码的特点了吧,这种实现继承的方式只是简单的拷贝属性,所以效率要比之前的继承方法差。但是拷贝继承后,继承的对象有属性的实例(其实拷贝的是引用)。我们写段代码解释下:
>>> var A = function(){}, B = function(){};
>>> A.prototype.stuff = [1,2,3];       
        [1, 2, 3]
>>> A.prototype.name = 'a';
        "a"
>>> extend2(B, A);
>>> B.prototype.hasOwnProperty('name')
      true
>>> B.prototype.hasOwnProperty('stuff')
      true
>>> B.prototype.stuff
     [1, 2, 3]
>>> B.prototype.stuff === A.prototype.stuff (注意,这里是全等号)
 true
修改b不影响A
>>> B.prototype.name += 'b'       
      "ab" 
>>> A.prototype.name
        "a"
 但是修改stuff回影响A
>>> B.prototype.stuff.push(4,5,6);
>>> A.prototype.stuff
        [1, 2, 3, 4, 5, 6]
看到区别了吧,如果是array等对象,实质拷贝的是引用,所以修改B的Array会同样影响A中的Array.而name是基本类型,所以不会受到影响.
另外,在提供一个不是用构造函数的继承方法,代码如下:
function extendCopy(p) {
  var c = {}; 
  for (var i in p) {
    c[i] = p[i];
  }
  c.uber = p;
  return c;
}
很容易理解,返回一个对象,其属性已经将父类的属性赋值给新对象了.
早期的

jQuery和Prototype就是这样实现继承的.

这些都是浅拷贝实现继承的方法,那么深拷贝呢?代码如下:
function deepCopy(p, c) {
  var c = c || {};
  for (var i in p) {
    if (typeof p[i] === 'object') {
      c[i] = (p[i].constructor === Array) ? [] : {}; [] : {};
      deepCopy(p[i], c[i]);
    } else {
      c[i] = p[i]; c[i] = p[i];
    }
  }
  return c;
}
看懂了吗?其实就是,如果发现某个属性是对象,就递归调用来拷贝这个属性里的每一个子属性.这样就弥补了之前浅拷贝遇到的问题了.

六 多重继承
虽然多重继承并不是一个好的设计架构,但是我们还是要实现一下,毕竟其他面向对象的语言也实现了多重继承.
function multi() {
  var n = {}, stuff, j = 0, len = arguments.length;
  for (j = 0; j < len; j++) {
    stuff = arguments[j];
    for (var i in stuff) {
      n[i] = stuff[i];
    }
  }
  return n;
}
其实代码很简单,只是将继承的父类作为参数传入,然后一一拷贝即可以了.

七 寄生继承
这个名字不是很好听,但还是很形象的,代码如下:
function object(o) {
  var n;
  function F() {}
  F.prototype = o;
  n = new F();
  n.uber = o;
  return n;
}

var twoD = {
  name: '2D shape',
  dimensions: 2
};
function triangle(s, h) {
  var that = object(twoD);//注意这里
  that.name ='Triangle';
  that.getArea = function(){return this.side * this.height / 2;};
  that.side = s;
  that.height = h;
  return that;
}

var t = triangle(5, 10);
>>> t.dimensions
确实很像寄生,子类包含了一个that属性,该属性继承了父类.

八,借用构造函数实现继承
你也许认为实现继承的方式太多了,最后在介绍一种继承(我保证是最后一种了).代码如下:
function Shape(id) {
  this.id = id;
}
Shape.prototype.name = 'shape';
Shape.prototype.toString = function(){return this.name;};
function Triangle() {
    Shape.apply(this, arguments);
}
Triangle.prototype.name = 'Triangle';
看懂了吗,只是简单的再次调用父类的构造函数,只是传入的参数不同.是不是有点投机取巧的感觉.

本文暂写到这里,之后会对这些继承的方式做个总结.

抱歉!评论已关闭.