前面说到了原型和原型链,今天就来说说在面向对象中比較好的继承方式吧。先来看看两种基础的继承方式:

一、构造函数型

  1. function People(name)
  2. {
  3. this.name=name;
  4. }
  5. People.prototype.sayName=function()
  6. {
  7. console.log(this.name);
  8. }
  9. function Student(id,name)
  10. {
  11. People.call(this,name);
  12. this.id=id;
  13. }
  14. Student.prototype.sayId=function()
  15. {
  16. console.log(this.id);
  17. }

在Student类里用了Person的call方法,就会在对象上自己主动绑定了name属性。可是这种方法的缺点就是无法继承原型方法。也就是无法继承sayName这种方法。

二、原型覆盖型

  1. function People(name)
  2. {
  3. <span style="white-space:pre"> </span>this.name=name;
  4. <span style="white-space:pre"> </span>this.friends=[];
  5. }
  6. People.prototype.sayName=function()
  7. {
  8. <span style="white-space:pre"> </span>console.log(this.name);
  9. }
  10. function Student(id)
  11. {
  12. <span style="white-space:pre"> </span>this.id=id;
  13. }
  14. Student.prototype=new People();
  15. Student.prototype.sayId=function()
  16. {
  17. <span style="white-space:pre"> </span>console.log(this.id);
  18. }
  19. var stu1=new Student(1);
  20. stu1.name="stu1";
  21. console.log(stu1);

在这里,Student类的原型指向了People的一个实例,尽管这样既继承了属性。又继承了原型方法,可是缺点就是无法像构造函数那样传递name进去,仅仅能实例化以后再为name属性赋值。大家都知道,尽管在查找时遵循原型链的规则。可是在赋值时,这个规则却是不有用的,实例化过后的对象是无法改动原型对象里面的属性的。

比方上式里面,为stu1的name属性赋值了,可是结果例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDcxNjc1OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

正如大家所见,并没有改动其原型对象的name属性。而是在stu1上加了一条name属性

再来看看改动对象指针:

  1. function People(name)
  2. {
  3. this.name=name;
  4. this.friends=[];
  5. }
  6. People.prototype.sayName=function()
  7. {
  8. console.log(this.name);
  9. }
  10. function Student(id)
  11. {
  12. this.id=id;
  13. }
  14. Student.prototype=new People();
  15. Student.prototype.sayId=function()
  16. {
  17. console.log(this.id);
  18. }
  19. var stu1=new Student(1);
  20. stu1.friends=["ro"];
  21. console.log(stu1);

结果与一般类型的几乎相同,可是我们用以下这样的方式:

  1. function People(name)
  2. {
  3. this.name=name;
  4. this.friends=[];
  5. }
  6. People.prototype.sayName=function()
  7. {
  8. console.log(this.name);
  9. }
  10. function Student(id)
  11. {
  12. this.id=id;
  13. }
  14. Student.prototype=new People();
  15. Student.prototype.sayId=function()
  16. {
  17. console.log(this.id);
  18. }
  19. var stu1=new Student(1);
  20. stu1.friends.push("Bob");
  21. console.log(stu1);

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDcxNjc1OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

大家看到了,在原型对象里的friends数组加了一个Bob。那是由于friends是引用类型,找到指针后再为其push一个值,这样就改动了原型对象的属性,可是为其赋值的话就会在对象上添加一个,而不是改动原型对象,问题来了。这样的push的方式能改动原型对象的friends。可是每个Student对象都要拥有自己私有的friends属性怎么办呢,能够为其赋值,可是这跟我们要的效果不一样,要的就是friends以及name属性都能成为私有属性,可是方法是原型方法。那么就能借鉴以上两种方式。

  1. function People(name,arr)
  2. {
  3. <span style="white-space:pre"> </span>this.name=name;
  4. <span style="white-space:pre"> </span>this.friends=arr;
  5. }
  6. People.prototype.sayName=function()
  7. {
  8. <span style="white-space:pre"> </span>console.log(this.name);
  9. }
  10. function Student(id,name,arr)
  11. {
  12. <span style="white-space:pre"> </span>People.call(this,name,arr);
  13. <span style="white-space:pre"> </span>this.id=id;
  14. }
  15. Student.prototype=new People();
  16. Student.prototype.sayId=function()
  17. {
  18. <span style="white-space:pre"> </span>console.log(this.id);
  19. }
  20. var stu1=new Student(1,"I",["bob"]);
  21. console.log(stu1);



大家看到了,name和friends属性确实成为了私有属性,可是原型对象里面也有这两个属性,这样也不好。占用了空间,那应该怎么办呢?怎么样才干去掉这个原型对象里面的属性,事实上非常easy,我们想要的仅仅是People原型对象里面的方法,那么我们仅仅须要继承原型对象即可了呀

  1. function People(name,arr)
  2. {
  3. this.name=name;
  4. this.friends=arr;
  5. }
  6. People.prototype.sayName=function()
  7. {
  8. console.log(this.name);
  9. }
  10. function Student(id,name,arr)
  11. {
  12. People.call(this,name,arr);
  13. this.id=id;
  14. }
  15. Student.prototype=People.prototype;
  16. Student.prototype.sayId=function()
  17. {
  18. console.log(this.id);
  19. }
  20. var stu1=new Student(1,"I",["bob"]);
  21. console.log(stu1);

这里大概就变成我们想要的样子了。可是另一点问题。constructor指向问题,本来这个constructor应该指向Student,改动一下就可以:

  1. function inheritPrototype(subType, superType){
  2. var prototype = superType.prototype;
  3. prototype.constructor = subType;
  4. subType.prototype = prototype;
  5. }
  6. function People(name,arr)
  7. {
  8. this.name=name;
  9. this.friends=arr;
  10. }
  11. People.prototype.sayName=function()
  12. {
  13. console.log(this.name);
  14. }
  15. function Student(id,name,arr)
  16. {
  17. People.call(this,name,arr);
  18. this.id=id;
  19. }
  20. inheritPrototype(Student,People);
  21. Student.prototype.sayId=function()
  22. {
  23. console.log(this.id);
  24. }
  25. var stu1=new Student(1,"I",["bob"]);
  26. console.log(stu1);

搞定了,这里我在前面写了一个继承原型的函数,首先将子类的原型指向父类的原型,然后改动其constructor指向,貌似这样就已经非常完美了,可是在不知道不觉中我又发现了一个问题,我也是此时此刻发现的,所以写博客还是有非常大的帮助的。能够帮自己理清思路,大家看到了我改动了constructor指向,可是我同一时候改动了父类的constructor指向,由于两者的原型对象是一个对象。所以这是有问题的。那么如何才干修正这个问题呢?事实上动动脑筋就知道,我们须要复制一个People的原型的副本,然后将Student的原型指向这个副本。那么问题来了,究竟是深拷贝还是浅拷贝呢,至于深拷贝和浅拷贝的概念不知道的能够上网查一查,深拷贝是全然能够满足这个要求的,那假设是浅拷贝呢,并且这个浅拷贝还不是普通的浅拷贝。要用到上面我们分析的知识。刚刚我们是为其赋值,赋值的话我们是不能改动一个对象的原型对象的。那么问题就迎刃而解了。我们能够这样:

  1. function inheritPrototype(subType, superType){
  2. function Obj()
  3. {
  4.  
  5. }
  6. Obj.prototype=superType.prototype;
  7. var newObject=new Obj();
  8. newObject.constructor = subType;
  9. subType.prototype = newObject;
  10. }
  11. function People(name,arr)
  12. {
  13. this.name=name;
  14. this.friends=arr;
  15. }
  16. People.prototype.sayName=function()
  17. {
  18. console.log(this.name);
  19. }
  20. function Student(id,name,arr)
  21. {
  22. People.call(this,name,arr);
  23. this.id=id;
  24. }
  25. inheritPrototype(Student,People);
  26. Student.prototype.sayId=function()
  27. {
  28. console.log(this.id);
  29. }
  30. var stu1=new Student(1,"I",["bob"]);
  31. console.log(stu1);

在inheritPrototype函数里,我先新建了一个类,然后将这个类的原型指向了父类的原型,然后再实例化一个对象。这时我为这个对象的constructor赋值,那么这个constructor属性就会存在于这个对象上,而不会去改动原型对象的constructor。唯一的缺点就是多了一重继承。可是这是最好的继承方式,我们来看看结果:

看上去就完美了。这就是比較好的继承方式。写到末尾,我想说写博客确实不错。不仅帮助了大家。也帮助了自己,大家一起成长,一起进步!

js中比較好的继承方式的更多相关文章

  1. JS中通过call方法实现继承

    原文:JS中通过call方法实现继承 讲解都写在注释里面了,有不对的地方请拍砖,谢谢! <html xmlns="http://www.w3.org/1999/xhtml"& ...

  2. JavaScript学习12 JS中定义对象的几种方式【转】

    avaScript学习12 JS中定义对象的几种方式 转自:  http://www.cnblogs.com/mengdd/p/3697255.html JavaScript中没有类的概念,只有对象. ...

  3. JavaScript学习12 JS中定义对象的几种方式

    JavaScript学习12 JS中定义对象的几种方式 JavaScript中没有类的概念,只有对象. 在JavaScript中定义对象可以采用以下几种方式: 1.基于已有对象扩充其属性和方法 2.工 ...

  4. JS中事件绑定的三种方式

    以下是搜集的在JS中事件绑定的三种方式.   1. HTML onclick attribute     <button type="button" id="upl ...

  5. js中声明Number的五种方式

    转载自:http://www.jb51.net/article/34191.htm <!DOCTYPE html> <html> <head> <meta c ...

  6. js中定义变量的三种方式const,val,let 的区别

    js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始化. 1 const b = 2;//正确 2 // const b;//错误,必须初始 ...

  7. 【温故知新】——原生js中常用的四种循环方式

    一.引言 本文主要是利用一个例子,讲一下原生js中常用的四种循环方式的使用与区别: 实现效果: 在网页中弹出框输入0   网页输出“欢迎下次光临” 在网页中弹出框输入1   网页输出“查询中……” 在 ...

  8. js两种定义函数、继承方式及区别

    一:js两种定义函数的方式及区别 1:函数声明: function sayA() { alert("i am A"); } 2:函数表达式: var sayB = function ...

  9. js中自己实现bind函数的方式

    最近由于工作比较忙,好久都没时间静下心来研究一些东西了.今天在研究 call 和 apply 的区别的时候,看到 github 上面的一篇文章,看完以后,感觉启发很大. 文章链接为 https://g ...

随机推荐

  1. HDU 3853 LOOPS 概率DP入门

    LOOPS Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 125536/65536 K (Java/Others)Total Sub ...

  2. 控制台或Winform程序中如何编码或解码Server.URLEncode

    原文发布时间为:2010-07-10 -- 来源于本人的百度文章 [由搬家工具导入] 在Asp.net中可以使用Server.HTMLEncode和Server.URLEncode 将文本或URL的特 ...

  3. AtCoder Regular Contest 090 F - Number of Digits

    题目链接 Description For a positive integer \(n\), let us define \(f(n)\) as the number of digits in bas ...

  4. 关于platform_device和platform_driver的匹配【转】

    转自:http://blog.csdn.net/dfysy/article/details/5959451 版权声明:本文为博主原创文章,未经博主允许不得转载. 说句老实话,我不太喜欢现在Linux ...

  5. 《手把手教你学C语言》学习笔记(10)--- 程序的循环控制

    C语言程序设计中,有些代码需要重复执行很多次,循环主要有三类: 一.for循环 1.基本格式为:for(表达式1:表达式2:表达式3){ //表达式1:循环变量赋初值 //表达式2:循环变量满足的条件 ...

  6. 用Gen4消除电容触摸屏设计屏障【转】

    转自:http://www.cntronics.com/sensor-art/80015498?page=2 中心议题: 电容式触摸屏设计到产品的各种挑战 解决方案: 用Gen4消除电容触摸屏设计屏障 ...

  7. linux内核之情景分析mmap操作

    进程可以通过mmap把一个已打开文件映射到用户空间. mmap(void*start,size_t length,int prot,int flags,int fd,off_t offset) sta ...

  8. msm8917 GPIO Voh(min)

    有些 pin 可以當成多種 function, 此例以 GPIO function P3 voltage 為例 Voh(min) = 1.67 - 0.45 = 1.22 V

  9. Day 22 Object_oriented_programming 3

    isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象,如果是返回True 1 class F ...

  10. 树的直径新求法、codeforces 690C3 Brain Network (hard)

    树的直径新求法 讲解题目 今天考了一道题目,下面的思路二是我在考场上原创,好像没人想到这种做法,最原始的题目,考场上的题目是这样的: 你现在有1 个节点,他的标号为1,每次加入一个节点,第i 次加入的 ...