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

一、构造函数型

function People(name)
{this.name=name;
}
People.prototype.sayName=function()
{console.log(this.name);
}
function Student(id,name)
{People.call(this,name);this.id=id;
}
Student.prototype.sayId=function()
{console.log(this.id);
}

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

二、原型覆盖型

function People(name)
{
<span style="white-space:pre"> </span>this.name=name;
<span style="white-space:pre"> </span>this.friends=[];
}
People.prototype.sayName=function()
{
<span style="white-space:pre"> </span>console.log(this.name);
}
function Student(id)
{
<span style="white-space:pre"> </span>this.id=id;
}
Student.prototype=new People();
Student.prototype.sayId=function()
{
<span style="white-space:pre"> </span>console.log(this.id);
}
var stu1=new Student(1);
stu1.name="stu1";
console.log(stu1);

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

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

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

再来看看改动对象指针:

function People(name)
{this.name=name;this.friends=[];
}
People.prototype.sayName=function()
{console.log(this.name);
}
function Student(id)
{this.id=id;
}
Student.prototype=new People();
Student.prototype.sayId=function()
{console.log(this.id);
}
var stu1=new Student(1);
stu1.friends=["ro"];
console.log(stu1);

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

function People(name)
{this.name=name;this.friends=[];
}
People.prototype.sayName=function()
{console.log(this.name);
}
function Student(id)
{this.id=id;
}
Student.prototype=new People();
Student.prototype.sayId=function()
{console.log(this.id);
}
var stu1=new Student(1);
stu1.friends.push("Bob");
console.log(stu1);

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

function People(name,arr)
{
<span style="white-space:pre"> </span>this.name=name;
<span style="white-space:pre"> </span>this.friends=arr;
}
People.prototype.sayName=function()
{
<span style="white-space:pre"> </span>console.log(this.name);
}
function Student(id,name,arr)
{
<span style="white-space:pre"> </span>People.call(this,name,arr);
<span style="white-space:pre"> </span>this.id=id;
}
Student.prototype=new People();
Student.prototype.sayId=function()
{
<span style="white-space:pre"> </span>console.log(this.id);
}
var stu1=new Student(1,"I",["bob"]);
console.log(stu1);


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

function People(name,arr)
{this.name=name;this.friends=arr;
}
People.prototype.sayName=function()
{console.log(this.name);
}
function Student(id,name,arr)
{People.call(this,name,arr);this.id=id;
}
Student.prototype=People.prototype;
Student.prototype.sayId=function()
{console.log(this.id);
}
var stu1=new Student(1,"I",["bob"]);
console.log(stu1);

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

function inheritPrototype(subType, superType){ var prototype = superType.prototype;prototype.constructor = subType; subType.prototype = prototype;
}
function People(name,arr)
{this.name=name;this.friends=arr;
}
People.prototype.sayName=function()
{console.log(this.name);
}
function Student(id,name,arr)
{People.call(this,name,arr);this.id=id;
}
inheritPrototype(Student,People);
Student.prototype.sayId=function()
{console.log(this.id);
}
var stu1=new Student(1,"I",["bob"]);
console.log(stu1);

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

function inheritPrototype(subType, superType){ function Obj(){}Obj.prototype=superType.prototype;var newObject=new Obj();newObject.constructor = subType; subType.prototype = newObject;
}
function People(name,arr)
{this.name=name;this.friends=arr;
}
People.prototype.sayName=function()
{console.log(this.name);
}
function Student(id,name,arr)
{People.call(this,name,arr);this.id=id;
}
inheritPrototype(Student,People);
Student.prototype.sayId=function()
{console.log(this.id);
}
var stu1=new Student(1,"I",["bob"]);
console.log(stu1);

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

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

js中比較好的继承方式相关推荐

  1. JS中对象的四种继承方式:class继承、原型链继承、构造函数继承、组合继承(构造函数和原型链继承的结合)

    前言 才发现之前没有对JavaScript中的继承做过总结,不过看得到是不少,接下来就对这几种继承方式做一下总结. class继承 class继承是ES6引入的标准的继承方式. ES6引入了class ...

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

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

  3. js常见的的6种继承方式

    继承是面向对象的,继承可以帮助我们更好的复用以前的代码,缩短开发周期,提高开发效率:继承也常用在前端工程技术库的底层搭建上,在整个js的学习中尤为重要 常见的继承方式有以下的六种 一.原型链继承 原型 ...

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

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

  5. 前端百题斩【006】——js中三类字符串转数字的方式

    写该系列文章的初衷是"让每位前端工程师掌握高频知识点,为工作助力".这是前端百题斩的第6斩,希望朋友们关注公众号"执鸢者",用知识武装自己的头脑. js中字符串 ...

  6. js中数组的几种循环方式

    js中数组的几种循环方式 for循环最基本的循环方式,不多说.这种最基本的循环才是速度最快的,效率最高的. for(var i = 0;i<5;i++){console.log(i) } for ...

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

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

  8. js中关于类及类继承

    在面向对象的程序设计语言中,类和类之间有显式的继承关系,一个类可以显式的指定继承了哪个类,子类将具有父类的所有属性和方法.js虽然也支持类.对象的概念,但没有继承的概念,只能通过特殊的手段来扩展原有的 ...

  9. JS中遍历数组的两种方式

    方式一 for循环 //遍历arr,获取arr中Person对象for(var i=0 ; i<arr.length ; i++){var p = arr[i];//判断Person对象的age ...

  10. js中函数的三种定义方式、函数声明、函数同名重复、函数删除、

    全栈工程师开发手册 (作者:栾鹏) js系列教程4-函数.函数参数 在js中,函数本身属于对象的一种,因此可以定义.赋值,作为对象的属性或者成为其他函数的参数.函数名只是函数这个对象类的引用. 一.函 ...

最新文章

  1. I/O多路复用,从来没遇到过这么明白的文章
  2. 记一次OOM问题排查过程
  3. Java并发编程实战系列15之原子遍历与非阻塞同步机制(Atomic Variables and Non-blocking Synchronization)...
  4. 8 一点就消失_微信富二代男友转账20w后,却在见面前一秒消失??...
  5. 2019-03-18-算法-进化(字符串中的第一个唯一字符)
  6. [JSOI2008]最小生成树计数
  7. VC6.0常见英文错误对照表
  8. 文字溢出时,实现在末尾显示三个点省略效果
  9. 木兰编程语言重现——支持列表操作,演示编辑器高亮
  10. ​OCO订单(委托)
  11. Python 数据结构与算法 —— Prim 算法与小顶堆
  12. linux(5)--补充(管道| / 重定向 / xargs)/find 与xargs结合使用/vi,grep,sed,awk(支持正则表达式的工具程序)...
  13. 2020-08-21 第一次面试小结
  14. linux 小度 驱动_小度Wifi,360随身Wifi2,小米Wifi树莓派驱动下载
  15. 嵌入式技术栈之I2S
  16. illegal multibyte sequence 解决方法
  17. 啥是甘特图?用思维导图制作甘特图的方法
  18. Excel 两列合并为一列中间加空格
  19. 语音计算机音乐学猫叫,语音控制开启家庭背景音乐系统新篇章
  20. 三角形的几何公式大全_椰岛数学:超全高中数学公式记忆表(文末分享PDF)

热门文章

  1. 【Matlab学习笔记】【细胞或颗粒检测分割】资源汇总
  2. 【VS2010学习笔记】【函数学习】二(SetTimer()函数)
  3. leetcode刷题日记-转换成小写字母
  4. 利用BayesianOptimization库对模型进行贝叶斯调参(XGBOOST)
  5. 微波遥感SNAP(二)——基于Sentinel-1雷达数据反演矿区地表形变
  6. ENVI 不规则多边形shp裁剪后Memory灰色显示问题解决
  7. 动态规划题目-------蓝桥杯真题-------蓝桥杯备战
  8. C/C++二维数组的传参方法总结
  9. 递归 解决汉诺塔问题(栈应用)
  10. Volley源码解析(二)