论坛首页 Web前端技术论坛

由 OO 继承来谈谈 javascript 继承

浏览 7576 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (1) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-11-15  
在大多数面向对象语言中,基本上的都支持继承,首先来宽泛的谈谈大多数 OO 语言的继承方式,之后具体到 javascript 来看看其继承有什么不同之处。

1. 实现继承:实现继承是指派生类(子类)继承了基类(父类)的所有属性和方法,并且有且只有一个基类。

优点是可以直接使用基类的所有属性和方法,缺点不言而喻,基类的一些不必要的方法也会被子类所继承。

比如:基类定义了果树类,里面有开花,结果等方法。派生类继承基类,但如果派生类的中的果树不会开花,只会结果(如:无花果),那么开花对子类就没用,但子类确实继承了基类开花的方法。

在设计模式中,我们更多强调的是面向接口的继承。上面的例子中,果树有两个接口,一个是开花,一个是结果。如果我的果树只能结果,不会开花的话,那么只要我的果树实现结果的接口就行了。与此同时不会把开花带入到我的派生类(子类)中。

2. 接口继承:派生类继承了接口的方法签名,它不同于实现继承的是,接口继承允许多继承,同时派生类只继承了方法签名而没有方法实现。具体的实现必须在派生类中完成。这种继承又称为“接口实现”。

谈完了 OO 语言继承的分类,下面对比上述两种方式来看看 javascript 是怎么来完成它独有的继承的。

先看第二点——接口继承,接口继承要求派生类继承基类的方法签名。

方法签名:返回值类型+方法名+参数列表

而在 javascript 中,任何的函数,方法,究其本质都会转变成变量来解析,如下:

//定义式
function a(){
    alert("pluto");
}

//变量式
var a = function(){
    alert("Pluto");
}


两种声明方式除了写法不同,执行顺序不同(定义式在编译时会自动提前)之外,其余都相同。并且由第二种方法可以看出,函数其实是一个命了名的变量而已。

由于函数没有签名,所以接口继承的方式在 javascript 中就不复存在了。

下面着重来谈谈第一点——实现继承。

实现继承主要是依靠 javascript 中的原型链来实现,并且将原型链作为实现继承的主要方法。

回顾一下构造函数,原型,实例之间的关系:构造函数都有一个原型对象(prototype),原型对象(prototype)都有一个回指构造函数的指针(constructor),而实例包含一个指向原型对象的指针(__proto__)。如果将“Fruit”的实例赋值给另外一个构造函数 “NotFruit” 的原型对象:即:

function Fruit(){
    this.fruit = true;
}
			
Fruit.prototype.isFruit = function(){
    return this.fruit;
}
			
function NotFruit(){
    this.notFruit = false;
}
			
NotFruit.prototype = new Fruit();//将  Fruit()的实例赋值给另外一个构造函数 (NotFruit) 的原型对象 (NotFruit.prototype)
			
NotFruit.prototype.isNotFruit = function(){
    return this.notFruit;
}
			
var dog = new NotFruit(); // 创建派生类(子类)的实例 dog
			
alert(dog.isFruit());    // 输出 true
alert(dog.isNotFruit()); // 输出 false
alert(dog.notFoundFruit()); // 报错  dog.notFoundFruit is not a function


则此时的原型对象包含了一个指向另外一个原型对象的指针。如下图诠释:



如上所示,便完成了 javascript 通过原型对象的方式的继承。

原型链表明实例化的对象首先会在构造函数的实例中搜索该属性,如果没有找到,则会继续搜索实例的原型。通过原型链实现继承,搜索过程会沿着原型链向上搜索,拿上面的例子来说,调用 dog.isFruit() 会经历三个搜索步骤:1)搜索实例(未找到) --> 2)搜索 NotFruit.prototype (未找到) --> 3) 搜索 Fruit.prototype,上面这个例子在调用 dog.isFruit() 时,在第三步才找到其对应的方法。如果在搜索到原型末端还未找到属性或方法时,则会报错,如上 dog.notFoundFruit() 所示。

当然,在 javascript 中,还存在“对象冒充”的方式的继承,在这里就不详细论述了。
  • 大小: 264.4 KB
   发表时间:2011-11-15  
楼主帖子中的图有个地方画错了,就是继承之后的那一部分,少了一个方法:isFruit();可能是你粗心画错了,nice
0 请登录后投票
   发表时间:2011-11-15  
dingchao.lonton 写道
楼主帖子中的图有个地方画错了,就是继承之后的那一部分,少了一个方法:isFruit();可能是你粗心画错了,nice


呵呵,我特意没有那么画。isFruit() 是父类的方法。在 NotFruit.prototype 原型对象中有一个 __proto__ 的指针是指向其父函数 Fruit() 的,指向过去之后,isFruit() 就在其父函数中找到了。
0 请登录后投票
   发表时间:2011-11-15  
前一篇看了,继续来这里看看。希望能多写点这样的帖子 ^ ^
0 请登录后投票
   发表时间:2011-11-17  
有点简单问题复杂化。
0 请登录后投票
   发表时间:2011-11-18  
这图是怎么画的啊,很漂亮
0 请登录后投票
   发表时间:2011-11-18  
yangguo 写道
有点简单问题复杂化。


有点想仔细说明其中道理,大侠有什么简单的方法,欢迎提出 :)谢谢。
0 请登录后投票
   发表时间:2011-11-18   最后修改:2011-11-18
dargoner 写道
这图是怎么画的啊,很漂亮


没用 Visio, 用的 fireworks, 操作起来简单 实用 呵呵。不过有点笨。。。。
0 请登录后投票
   发表时间:2011-11-18  
lz举得例子有点乱啊。
父类Fruit,子类NotFruit,对象居然是dog...
0 请登录后投票
   发表时间:2011-11-18  
dingchao.lonton 写道
楼主帖子中的图有个地方画错了,就是继承之后的那一部分,少了一个方法:isFruit();可能是你粗心画错了,nice

没画错,原型链方式,子类原型不应当有冗余的方法,否则继承就没有意义了
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics