之前的代码有个很大的缺点,每次继承的时候,我们都要输入重复的代码。所以,我们可以把继承的代码独立开来,代码如下: 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';
看懂了吗,只是简单的再次调用父类的构造函数,只是传入的参数不同.是不是有点投机取巧的感觉.
本文暂写到这里,之后会对这些继承的方式做个总结.