1)对象冒充
2)call方式
3)apply方式
4)原型链
5)混合方式

1)对象冒充

Js代码
  1. function People(name, age) {
  2. this.name = name;
  3. this.age = age;
  4. this.speak = function() {};
  5. };
  6. function Chinese(name) {
  7. this.inherit = People;  //冒充,会继承所有方法
  8. this.inherit(name, age);    //继承,必须指定继承的属性
  9. delete this.inherit;    //删除继承
  10. //需要定义的新方法、属性或者“重载”的方法在删除inherit后
  11. this.speak = function() {};
  12. }
function People(name, age) {
this.name = name;
this.age = age;
this.speak = function() {};
};
function Chinese(name) {
this.inherit = People; //冒充,会继承所有方法
this.inherit(name, age);    //继承,必须指定继承的属性
delete this.inherit;    //删除继承
//需要定义的新方法、属性或者“重载”的方法在删除inherit后
this.speak = function() {};
}

2)call方式
封装了方式1的“经典三句”,一样的效果。

Js代码
  1. function Sub(param1, param2) {
  2. Super1.call(this, param1);
  3. Super2.call(this, param2);
  4. };
function Sub(param1, param2) {
Super1.call(this, param1);
Super2.call(this, param2);
};

3)apply方式
同call一样,只是使用方式不同:Super.apply(对象,参数数组);

Js代码
  1. function Sub(param1, param2, param3) {
  2. Super1.apply(this, new Array(param1, param2);
  3. Super2.apply(this, new Array(param3);
  4. };
function Sub(param1, param2, param3) {
Super1.apply(this, new Array(param1, param2);
Super2.apply(this, new Array(param3);
};

4)原型链方式
父类不用构造函数创建,而是原型方式
缺点:不能传递参数,不支持多重继承

Js代码
  1. function Super() {};
  2. Super.prototype.param1 = "sth.";
  3. Super.prototype.foo = function() {};
  4. function Sub() {};
  5. Sub.prototype = new Super();
  6. usage: var aSub = new Sub; Sub.foo();
function Super() {};
Super.prototype.param1 = "sth.";
Super.prototype.foo = function() {};
function Sub() {};
Sub.prototype = new Super();
usage: var aSub = new Sub; Sub.foo();

5)混合方式:
a)用构造函数定义新的属性,用call或apply继承属性。
b)用原型方式定义新的方法,用原型链继承方法。

Js代码
  1. function Super(param1) {
  2. this.param1 = param1;
  3. Super.prototype.foo = function() {};
  4. };
  5. function Sub(param1, param2) {
  6. Super.call(this, param1);
  7. this.param2 = param2;   //别用this.prototype.param2 = param2;
  8. Sub.prototype = new Super();
  9. Sub.prototype.bar = function() {};
  10. };

js继承探讨

每当我们说到js的继承时,在您的脑袋的第一反应就是prototype原型机制来实现。但是您是否使用过其他的方法来实现继承呢,或者您是否了解其他实现方式及各种不同的继承实现机制的优缺点呢?

好了,下面我们就来看看几种比较常见的继承实现吧。

1、 prototype方式

 1var BaseClass =function()
 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
17var Derived = function()
18
19
20
21    this.address = "ShenZhen";
22
23}
24
25Derived.prototype = new BaseClass();
26
27var instance = new Derived();
28
29instance.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;如何验证呢?看如下代码:

1       var foo = function(){};
2
3       var result = (foo.prototype==Object);

这段代码的result 所得值为 false;

以下几个需要注意:

 1    typeof(Object.prototype) == "object";
 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方式

 1var BaseClass =function()
 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
17var Derived = function()
18
19
20
21       BaseClass.apply(this,new Array());
22
23    this.address = "ShenZhen";
24
25}
26
27var instance = new Derived();
28
29instance.ToString();
30
31 
32

在这种方式下,我们最需要理解的就是apply函数的作用。

该方法普遍的解释为用A方法去替换B方法。第一个参数为B方法的对象本身,第二个参数为一个数组,该数组内的值集合为需要传递给A方法对应的参数列表,如果参数为空,即没有参数传递,则通过 new Array() 来传递,null无效。

一般的方式为:

但是在本例当中,apply方法执行了两步操作。

第一:将BaseClass以apply传递的Array数组作为初始化参数进行实例化。

第二:将新生成的实例对象的所有属性(name,age,ToString方法)复制到 instance实例对象。这样就实现了继承。

 1var foo = function()
 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
21new foo().fooA();
22

3、call+prototype 方式

 1var BaseClass =function(name,age)
 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
17var Derived = function()
18
19
20
21       BaseClass.call(this,"3zfp",100);
22
23    this.address = "ShenZhen";
24
25}
26
27Derived.prototype = new BaseClass();
28
29var instance = new Derived();
30
31instance.ToString();
32
33

其实,call函数和apply方式有很类似的作用,都是用A方法去替换B方法,但是参数传递不一样,call方法的第一个参数为B方法的对象本身,和面的参数列不用Array对象包装,直接依次传递就可以。

为什么作用类似,call方式的实现机制却要多一条 Derived.prototype = new BaseClass(); 语句呢?那是因为call方法只实现了方法的替换而没有作对象属性的复制操作。

call方法实际上是做了如下几个操作:

例:

 1var foo = function()
 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
21new foo().fooA();
22
23

则 this.fooB.call(this,"sorry")执行了如下几个操作:

1this.temp = this.fooB;
2
3this.temp("sorry");
4
5delete (this.temp);
6

其实,google Map API 的继承就是使用这种方式。大家可以下载的参考参考(maps.google.com)。

4、prototype.js中的实现方式

 1Object.extend = function(destination, source) {
 2
 3 for (property in source) {
 4
 5    destination[property] = source[property];
 6
 7 }
 8
 9       return destination;
10
11}
12
13var 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
27var 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
45Object.extend(Derived.prototype,new BaseClass());
46
47var instance = new Derived();
48
49document.write(instance.ToString());

该方式,实际上是显式的利用了apply的原理来实现继承。先 var temp = new BaseClass(),再将 temp 的属性遍历复制到Derived.prototype中。for (property in source) 表示遍历某个对象的所有属性。但是私有属性无法遍历。例:

 1var foo = function()
 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
21var f =new foo();
22
23var eles = "";
24
25for (property in f)
26
27{
28
29       eles+=" "+property;
30
31}
32
33document.write(eles); 
34
35

输出为 "name age"而没有"innerString" 和 "innerToString()";具体原理,以后有机会可以解释(包括私有变量,私有函数,私有函数的变量可访问性等等)。上面总结了种种继承方式的实现。但是每种方法都有其优缺点。

第一种方式,如何实现如下需求,需要显示 "3zfp__100";

 1var BaseClass =function(name,age)
 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
17var Derived = function(name,age)
18
19
20
21    this.address = "ShenZhen";
22
23}
24
25Derived.prototype = new BaseClass();
26
27var instance = new Derived("3zfp",100);
28
29document.write(instance.ToString());
30
31

我们通过运行可以发现,实际上输出的是 "undefined__undefined"。也就是说name和age没有被赋值。

oh,my god!天无绝人之路。第二和第三种方法可以实现,具体如下:

 1var BaseClass =function(name,age)
 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
17var Derived = function(name,age)
18
19
20
21       BaseClass.apply(this,new Array(name,age));
22
23    this.address = "ShenZhen";
24
25}
26
27var instance = new Derived("3zfp",100);
28
29document.write(instance.ToString());
30
31______________________________________________
32
33---------------------------------------------------------------------
34
35var 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
51var Derived = function(name,age)
52
53
54
55       BaseClass.call(this,name,age);
56
57    this.address = "ShenZhen";
58
59}
60
61Derived.prototype = new BaseClass();
62
63var instance = new Derived("3zfp",100);
64
65 
66
67document.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,感兴趣的可以搜索下载。

转载于:https://www.cnblogs.com/sharewind/archive/2008/08/25/1275617.html

JavaScript笔记 - 对象继承的几种方式相关推荐

  1. javascript 面向对象(实现继承的几种方式)

    欢迎大家关注我的公众号[老周聊架构],Java后端主流技术栈的原理.源码分析.架构以及各种互联网高并发.高性能.高可用的解决方案. 1.原型链继承 核心: 将父类的实例作为子类的原型 缺点: 父类新增 ...

  2. JavaScript(js)实现继承的几种方式

    1.原型链继承 核心:将父类的实例做为子类的原型对象 //动物类function Animal(name,sex) {this.name = name || 'Animal';this.sex = s ...

  3. JS 总结之原型继承的几种方式

    在之前的总结中,我们详细分析了原型<JS 总结之原型>,原型很大作用用于模拟继承,这一次,我们来聊原型继承的几种方式. function Person (age) {this.age = ...

  4. 继承有几种方式,分别是什么,想要实现继承可以使用哪些方法

    这里是修真院前端小课堂,每篇分享文从 [背景介绍][知识剖析][常见问题][解决方案][编码实战][扩展思考][更多讨论][参考文献] 八个方面深度解析前端知识/技能,本篇分享的是: [继承有几种方式 ...

  5. JavaScript 生成唯一ID的几种方式

    这篇文章主要介绍了JavaScript 生成唯一ID的几种方式,帮助大家更好的理解和使用JavaScript,感兴趣的朋友可以了解下. 编程的世界里,在很多的时候,我们都需要一个唯一的ID来代表一些数 ...

  6. 可以实现继承的几种方式

    继承的几种方式 说起继承,又是一个老生常谈的问题了.今天来讲讲继承的几种方法以及他们的优缺点吧. 源码地址:点击这里 一.原型链继承 原型链继承:通过原型将一个引用类型继承另一个引用类型的属性和方法. ...

  7. Django中Model继承的三种方式

    Django中Model继承的三种方式 Django中Model的继承有三种: 1.抽象继承 2.多表继承 3.proxy model(代理model) 1.抽象继承 第一种抽象继承,创建一个通用父类 ...

  8. jquery、javascript实现(get、post两种方式)跨域解决方法

     jquery.javascript实现(get.post两种方式)跨域解决方法 一.实现get方式跨域请求数据 浏览器端 <script> $(document).ready(fun ...

  9. python用于导入模块或模块中的对象_在 Python 中导入模块中的对象有哪几种方式? (5.0分)_学小易找答案...

    [判断题]尽管可以使用 import 语句一次导入任意多个标准库或扩展库,但是仍建议每次只导入一个标准库或扩展库. (2.0分) [填空题]表达式 int(str(34)) == 34 的值为 ___ ...

最新文章

  1. UVA 701 The Archeologists' Dilemma
  2. win10设置默认打开方式
  3. StackGAN mini review
  4. ES6系列之Set Map
  5. 谈谈我的移动端rem适配方案
  6. php常见漏洞修复,phpstudy漏洞修复方法
  7. mac 如何配置mysql_MAC下安装与配置MySQL
  8. “我来管管看”系列:采购误差缘何而来?
  9. .pem和.pk8是什么文件?(转载)
  10. 译文伪原创的全文翻译软件
  11. Excel基础知识(1):公式中相对引用、绝对引用的区别
  12. linux mint18 win8主题,Ubuntu/Linux Mint用上仿Win7/Win8主题
  13. docx文档文字怎么加边框_word给正文加边框 word怎样给一段文字加上边框
  14. OpenCv之黑白上色小技巧,奥黛丽赫本神颜,超惊艳滴~
  15. 得力计算机怎么用二进制,如何将得力计算器 DL-1529 调到保留两位小数点?
  16. python单向认证_使用Python进行单向方差分析的四种方法
  17. 校园二手交易平台-程序流程图
  18. 支付宝扫一扫连接WIFI
  19. Android scrollTo() scrollBy() Scroller讲解及应用
  20. 【matlab图像处理】理想低通滤波器

热门文章

  1. li鼠标悬浮手势样式
  2. sqlserver 字符串转换成日期、int与varchar类型转换及保留两位小数
  3. 总结之前有道笔记的内容--对于Touch 的不同讲解
  4. android 声卡音频策略小记
  5. 用treeview遍历文件夹(vb)
  6. SpringBoot实战(十一):MultipartException: Could not parse multipart servlet request
  7. 利用Nginx对不同的域名进行解析
  8. 英语总结系列(十五):别样的三月
  9. 2018中国年度AI评选揭晓!10大领航企业,50家明星公司,10佳投资机构
  10. CNN手把手维修攻略:你的网络不好好训练,需要全面体检