全文转载自:http://www.blogjava.net/baoyaer/articles/105481.html
每当我们说到 js 的继承时,在您的脑袋的第一反应就是 prototype 原型机制来实现。但是您是否使用过其他的方法来实现继承呢,或者您是否了解其他 实现方式及各种不同的继承实现机制的优缺点呢?
好了,下面我们就来看看几种比较常见的继承实现吧。
1、 prototype方式
2
3 {
4
5 this .name = " 3zfp " ;
6
7 this .age = 100 ;
8
9 this .ToString = function () {
10
11 return this .name + " " + this .age;
12
13 }
14
15 }
16
17 var Derived = function ()
18
19 {
20
21 this .address = " ShenZhen " ;
22
23 }
24
25 Derived.prototype = new BaseClass();
26
27 var instance = new Derived();
28
29 instance.ToString();
30
这种方式最为简单,只需要让一个类的prototype 为被继承的一个实例就 ok ,然后直接使用 BaseClass 的方法。
prototype 属性是啥意思呢? prototype 即为原型,每一个对象 ( 由 function 定义出来 ) 都有一个默认的原型属性,该属性是个对象类型。 并且该默认属性用来实现链的向上攀查。意思就是说,如果某个对象的属性不存在,那个将通过 prototype 属性对应的对象的来查找该对象的属性。 如果 prototype 查找不到呢? js 会自动地找 prototype 的 prototype 属性对应的对象来查找,这样就通过 prototype 一直往上索引攀查,直到查找到了 该属性或者 prototype 最后为空 ("undefined");
例如:上例中的 instance.ToString() 方法。 js 会先在 instance 实例中查找是否有 ToString() 方法,因为没有,所以查找 Derived.prototype 属性, 而 prototype 为 NewClass 的一个实例,该实例有 ToString() 方法,于是调用成功;同样给 instance 的 name 属性赋值时也是查找 prototype 来实现的。
注意,每一个对象得 prototype 都默认对应一个 object 对象,但是该对象不等于 Object ;如何验证呢?看如下代码:
2
3 var result = (foo.prototype == Object);
这段代码的result 所得值为 false;
以下几个需要注意:
2
3
4
5 typeof (Object.prototype.prototype) == " undefined " ;
6
7
8
9 var obj = new Object();
10
11 typeof (obj.prototype) == " undefined " ;
12
13
14
15 var obj = {} ;
16
17 typeof (obj.prototype) == " undefined " ;
18
19
20
21
2 、 apply 方式
2
3 {
4
5 this .name = " 3zfp " ;
6
7 this .age = 100 ;
8
9 this .ToString = function () {
10
11 return this .name + " " + this .age;
12
13 }
14
15 }
16
17 var Derived = function ()
18
19 {
20
21 BaseClass.apply( this , new Array());
22
23 this .address = " ShenZhen " ;
24
25 }
26
27 var instance = new Derived();
28
29 instance.ToString();
30
31
32
在这种方式下,我们最需要理解的就是 apply 函数的作用。
该方法普遍的解释为用 A 方法去替换 B 方法。第一个参数为 B 方法的对象本身,第二个参数为一个数组,该数组内的值集合为需要传递给 A 方法对应的 参数列表,如果参数为空,即没有参数传递,则通过 new Array() 来传递, null 无效。
一般的方式为:
但是在本例当中, apply 方法执行了两步操作。
第一 :将 BaseClass 以 apply 传递的 Array 数组作为初始化参数进行实例化。
第二 :将新生成的实例对象的所有属性( name , age , ToString 方法)复制到 instance 实例对象。 这样就实现了继承。
2
3 {
4
5 this .fooA = function () {
6
7 this .fooB.apply( this , new Array( " sorry " ));
8
9 }
10
11 this .fooB = function (str)
12
13 {
14
15 alert(str);
16
17 }
18
19 }
20
21 new foo().fooA();
22
3 、 call+prototype 方式
2
3 {
4
5 this .name = name;
6
7 this .age = age;
8
9 this .ToString = function () {
10
11 return this .name + " " + this .age;
12
13 }
14
15 }
16
17 var Derived = function ()
18
19 {
20
21 BaseClass.call( this , " 3zfp " , 100 );
22
23 this .address = " ShenZhen " ;
24
25 }
26
27 Derived.prototype = new BaseClass();
28
29 var instance = new Derived();
30
31 instance.ToString();
32
33
其实, call 函数和 apply 方式有很类似的作用,都是用 A 方法去替换 B 方法,但是参数传递不一样, call 方法的第一个参数为 B 方法的对象本身,和面的参数列不用 Array 对象包装,直接依次传递就可以。
为什么作用类似, call 方式的实现机制却要多一条 Derived.prototype = new BaseClass(); 语句呢?那是因为 call 方法只实现了方法的替换而没有作对象属性的复制操作。
例:
2
3 {
4
5 this .fooA = function () {
6
7 this .fooB.call( this , " sorry " );
8
9 }
10
11 this .fooB = function (str)
12
13 {
14
15 alert(str);
16
17 }
18
19 }
20
21 new foo().fooA();
22
23
则 this.fooB.call(this,"sorry") 执行了如下几个操作:
2
3 this .temp( " sorry " );
4
5 delete ( this .temp);
6
其实,google Map API 的继承就是使用这种方式。大家可以下载的参考参考 (maps.google.com) 。
4 、 prototype.js 中的实现方式
2
3 for (property in source) {
4
5 destination[property] = source[property];
6
7 }
8
9 return destination;
10
11 }
12
13 var BaseClass = function (name,age) {
14
15 this .name = name;
16
17 this .age = age;
18
19 this .ToString = function () {
20
21 return this .name + " " + this .age;
22
23 }
24
25 }
26
27 var Derived = function ()
28
29 {
30
31 BaseClass.call( this , " foo " , 100 );
32
33 this .address = " singapore " ;
34
35 this .ToString = function () {
36
37 var string = Derived.prototype.ToString.call( this );
38
39 return string + " " + this .address;
40
41 }
42
43 }
44
45 Object.extend(Derived.prototype, new BaseClass());
46
47 var instance = new Derived();
48
49 document.write(instance.ToString());
该方式,实际上是显式的利用了 apply 的原理来实现继承。先 var temp = new BaseClass() ,再将 temp 的属性遍历复制到 Derived.prototype 中。 for (property in source) 表示遍历某个对象的所有属性。但是私有属性无法遍历。 例 :
2
3 {
4
5 var innerString = "" ;
6
7 this .name = " 3zfp " ;
8
9 this .age = 100 ;
10
11 function innerToString()
12
13 {
14
15 return innerString;
16
17 }
18
19 }
20
21 var f = new foo();
22
23 var eles = "" ;
24
25 for (property in f)
26
27 {
28
29 eles += " " + property;
30
31 }
32
33 document.write(eles);
34
35
输出为 "name age" 而没有 "innerString" 和 "innerToString()"; 具体原理,以后有机会可以解释(包括私有变量,私有函数,私有函数的变量可 访问性等等)。 上面总结了种种继承方式的实现。但是每种方法都有其优缺点。
第一种方式,如何实现如下需求,需要显示 "3zfp__100";
2
3 {
4
5 this .name = name;
6
7 this .age = age;
8
9 this .ToString = function () {
10
11 return this .name + " " + this .age;
12
13 }
14
15 }
16
17 var Derived = function (name,age)
18
19 {
20
21 this .address = " ShenZhen " ;
22
23 }
24
25 Derived.prototype = new BaseClass();
26
27 var instance = new Derived( " 3zfp " , 100 );
28
29 document.write(instance.ToString());
30
31
我们通过运行可以发现,实际上输出的是 "undefined__undefined" 。也就是说 name 和 age 没有被赋值。
oh,my god! 天无绝人之路。第二和第三种方法可以实现,具体如下:
2
3 {
4
5 this .name = name;
6
7 this .age = age;
8
9 this .ToString = function () {
10
11 return this .name + " " + this .age;
12
13 }
14
15 }
16
17 var Derived = function (name,age)
18
19 {
20
21 BaseClass.apply( this , new Array(name,age));
22
23 this .address = " ShenZhen " ;
24
25 }
26
27 var instance = new Derived( " 3zfp " , 100 );
28
29 document.write(instance.ToString());
30
31 ______________________________________________
32
33 ---------------------------------------------------------------------
34
35 var BaseClass = function (name,age)
36
37 {
38
39 this .name = name;
40
41 this .age = age;
42
43 this .ToString = function () {
44
45 return this .name + " " + this .age;
46
47 }
48
49 }
50
51 var Derived = function (name,age)
52
53 {
54
55 BaseClass.call( this ,name,age);
56
57 this .address = " ShenZhen " ;
58
59 }
60
61 Derived.prototype = new BaseClass();
62
63 var instance = new Derived( " 3zfp " , 100 );
64
65
66
67 document.write(instance.ToString());
68
69
70
但是用 apply 方法也还是有缺点的,为什么?在 js 中,我们有个非常重要的运算符就是 "instanceof" ,该运算符用来比较某个对向是否为某种类型。 对于继承,我们除了是属于 Derived 类型,也应该是 BaseClass 类型,但是。 apply 方式返回值为 false( (instance instanceof BaseClass) == false). 由于 prototype.js 使用了类似 apply 的方式,所以也会出现这个问题。
啊,终极方法就是 call+prototype 方式了,还是 google 牛 X 。您可以试一下是否正确 ((instance instanceof BaseClass) == true) 。
最后,就是多重继承了,由于 js 中 prototype 只能对应一个对象,因此无法实现真正意义上的多重继承。有一个 js 库模拟了多重继承, 但是该库也额外重写了 instanceOf 方法,用 _instanceOf 和 _subclassOf 函数来模拟判断。该库的名字叫 modello.js ,感兴趣的可以搜索下载。
相关推荐
JavaScript继承机制探讨及其应用.pdf
在之前的两篇博客中,我们详细探讨了JavaScript OOP中的各种知识点(JS OOP基础与JS 中This指向详解 、 成员属性、静态属性、原型属性与JS原型链)。今天我们来继续探讨剩余的内容吧。 我们都知道,面向对象的三大...
在真正的Web站点和应用程序中,几乎不可能创建名为ClassA和ClassB的类,更可能的是创建表示特定事物(如形状)的类。考虑本章开头所述的形状的例子,Polygon、Triangle和Rectangle类就构成了一组很好的探讨数据。
而在继承的时候有两种常用方式,今天我们就来稍作探讨 代码如下://父类 function Person(name){ this.name = name;}; // 子类 function Student(sex){ Person.apply(this,arguments); //继承父类的构造函数 ...
1 它拥有对象,可以包含数据和处理数据的方法。对象可以包含其它对象。他没有类(在javascript2.0真正实现之前),但它却有构造器可以做类...另外还有批评说javascript不能提供继承,但随后有人证明了javascript不仅能
在之前javascript面向对象系列的文章里面,我们已经探讨了组合继承和寄生继承,回顾下组合继承: function Person( uName ){ this.skills = [ 'php', 'javascript' ]; this.userName = uName; } Person....
2006年,本书第1版问世,立刻脱颖而出,成为广大Web程序员心目中的经典,是提升JavaScript编程技能的必读书籍。...在讲解技术之余,第2版还探讨了JavaScript的发展历程,带领读者展望了这门技术的未来趋势。
依次介绍了JavaScript的发展历史、基础性话题(变量、数据类型、数组、循环以及条件表达式)、函数、对象、原型、继承的实现、BOM和DOM等。附录部分包括了学习JavaScript编程常用的参考资源。尤其值得一提的是,本书...
本文实例讲述了AngularJS的scope,继承结构,事件系统和生命周期。分享给大家供大家参考,具体如下: 深入探讨 Scope 作用域 每一个 $scope 都是类 Scope 的一个实例。类 Scope 拥有可以控制 scope 生命周期的方法,...
接下来,我将深入探讨原型继承,这是 JavaScript 的一个关键特性,它允许对象从其他对象继承属性和方法。此外,我还将解释异步编程,这对于构建可扩展且高效的 Web 应用程序至关重要。 之后,我将介绍提升、...
在 javascript 中有很多方式来创建对象,所以创建对象的方式使用起来非常灵活。那么,到底哪一种方式是最恰当的对象创建方式呢?...它没有类继承机制,但是可以通过原型(prototype)实现继承。 现在看起来,
JavaScript作为一个基于对象(没有类的概念)的语言,从入门到精通到放弃一直会被对象这个问题围绕。 平时发的文章基本都是开发中遇到的问题和对最佳解决方案的探讨,终于忍不住要写一篇基础概念类的文章了。 本文探讨...
JS中原型的概念不想多说,这里只是探讨一下修改父类原型属性与覆盖父类原型中属性的区别,防止以后出问题
本章将探讨一些令JavaScript如此富有表现力的特性。从中你可以体会到,这种语言允许你用各种方式完成同样的任务,还允许你在面向对象编程的过程中借用函数式编程中的概念来丰富其实现方式。本章解释了究竟为什么...
循环是所有编程语言中最为重要的机制之一,几乎任何拥有实际意义的计算机程序(排序、查询等)都里不开循环。 而循环也正是程序优化中非常让人... javascript 的for语法继承自c语言,for循环的基本语法有两种使用方法。
而本文的作者认为JavaScript设计之匆忙,初衷仅仅实现简单的网页互动,JavaScript继承完全缺乏设计指导等,所以他认为JavaScript仍然较差。 近,我和许多程序员一样,对JavaScript进行重新探讨。事实上,...
为了说明 JavaScript 是一门彻底的面向对象的语言,首先有必要从面向对象的概念着手 , 探讨一下面向对象中的几个概念: 一切事物皆对象 对象具有封装和继承特性 对象与对象之间使用消息通信,各自存在信息隐藏 以...
目前组件方案仍然处于探索阶段,欢迎共同探讨 主要分为三个文件 base.js、page.js、component.js base.js 页面类 和 组件类 都继承于该公共类,用于挂载公共方法 page.js 页面基础类,替代原来 Page() 方法 const ...
为了说明 JavaScript 是一门彻底的面向对象的语言,首先有必要从面向对象的概念着手 , 探讨一下面向对象中的几个概念:1.一切事物皆对象,2.对象具有封装和继承特性,3.对象与对象之间使用消息通信,各自存在信息...
毕业设计 个人博客系统源码 ...最后探讨了各种模式的技术,如简单工厂模式,包括工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式,以及外观模式,包括适配器模式。本书还讲解了几种适配器、代