6种JavaScript继承方式及优缺点(小结)


Posted in Javascript onFebruary 06, 2020

温馨提示:想要更好的理解JS继承方式,须了解构造函数、原型对象、实例化对象、原型链等概念

第一种:原型链继承

利用原型链的特点进行继承

function Parent(){
  this.name = 'web前端';
  this.type = ['JS','HTML','CSS'];
}
Parent.prototype.Say=function(){
  console.log(this.name);
}
function Son(){};
Son.prototype = new Parent();
son1 = new Son();
son1.Say();

以上例子解释:

①创建一个叫做Parent的构造函数,暂且称为父构造函数,里面有两个属性name、type
②通过Parent构造函数的属性(即原型对象)设置Say方法,此时,Parent有2个属性和1个方法
③创建一个叫做Son的构造函数,暂且称为子构造函数
④设置Son的属性(即原型对象)值为父构造函数Parent的实例对象,即子构造函数Son继承了父构造函数Parent,此时Son也有2个属性和1个方法
⑤对Son构造函数进行实例化,结果赋值给变量son1,即son1为实例化对象,同样拥有2个属性和1个方法
⑥输出son1的Say方法,结果为"web前端"

优点:可以实现继承

缺点:
①因为Son.prototype(即原型对象)继承了Parent实例化对象,这就导致了所有Son实例化对象都一样,都共享有原型对象的属性及方法。代码如下:

son1 = new Son();
son2 = new Son();
son1.type.push('VUE');
console.log(son1.type);//['JS','HTML','CSS','VUE']
console.log(son2.type);//['JS','HTML','CSS','VUE']

结果son1、son2都是['JS','HTML','CSS','VUE']
②Son构造函数实例化对象无法进行参数的传递

第二种:构造函数继承

通过构造函数call方法进行继承,直接来看代码:

function Parent(){
  this.name = 'web前端';
  this.type = ['JS','HTML','CSS'];
}
function Son(){
  Parent.call(this);
}
son1 = new Son();
son1.type.push('VUE');
console.log(son1.type);//['JS','HTML','CSS','VUE']
son2 = new Son();
console.log(son2.type);//['JS','HTML','CSS']

以上例子解释:
①创建父级构造函数Parent,有name、type两个属性
②创建子级构造函数Son,函数内部通过call方法调用父级构造函数Parent,实现继承
③分别创建构造函数Son的两个实例化对象son1、son2,对son1的type属性新增元素,son2没有新增,结果不一样,说明实现了独立

优点:
①实现实例化对象的独立性;
②还可以给实例化对象添加参数

function Parent(name){
  this.name = name;
}
function Son(name){
  Parent.call(this,name);
}
son1 = new Son('JS');
console.log(son1);//JS
son2 = new Son('HTML');
console.log(son2);//HTML

缺点:
①方法都在构造函数中定义,每次实例化对象都得创建一遍方法,基本无法实现函数复用
②call方法仅仅调用了父级构造函数的属性及方法,没有办法调用父级构造函数原型对象的方法

第三种:组合继承

利用原型链继承和构造函数继承的各自优势进行组合使用,还是看代码:

function Parent(name){
  this.name = name;
  this.type = ['JS','HTML','CSS'];
}
Parent.prototype.Say=function(){
  console.log(this.name);
}
function Son(name){
  Parent.call(this,name);
}
Son.prototype = new Parent();
son1 = new Son('张三');
son2 = new Son('李四');
son1.type.push('VUE');
son2.type.push('PHP');
console.log(son1.type);//['JS','HTML','CSS','VUE']
console.log(son2.type);//['JS','HTML','CSS','PHP']
son1.Say();//张三
son2.Say();//李四

以上例子解释:
①创建一个叫做Parent的构造函数,里面有两个属性name、type
②通过Parent构造函数的属性(即原型对象)设置Say方法,此时,Parent有2个属性和1个方法
③创建子级构造函数Son,函数内部通过call方法调用父级构造函数Parent,实现继承
④子构造函数Son继承了父构造函数Parent,此时Son也有2个属性和1个方法
⑤分别创建构造函数Son的两个实例化对象son1、son2,传不同参数、给type属性新增不同元素、调用原型对象Say方法

优点:
①利用原型链继承,实现原型对象方法的继承
②利用构造函数继承,实现属性的继承,而且可以参数
组合函数基本满足了JS的继承,比较常用

缺点:
无论什么情况下,都会调用两次父级构造函数:一次是在创建子级原型的时候,另一次是在子级构造函数内部

第四种:原型式继承

创建一个函数,将参数作为一个对象的原型对象

function fun(obj) {
  function Son(){};
  Son.prototype = obj;
  return new Son();
}    
var parent = {
  name:'张三'
}
var son1 = fun(parent);
var son2 = fun(parent);
console.log(son1.name);//张三
console.log(son2.name);//张三

以上例子解释:
①创建一个函数fun,内部定义一个构造函数Son
②将Son的原型对象设置为参数,参数是一个对象,完成继承
③将Son实例化后返回,即返回的是一个实例化对象
优缺点:跟原型链类似

第五种:寄生继承

在原型式继承的基础上,在函数内部丰富对象

function fun(obj) {
  function Son() { };
  Son.prototype = obj;
  return new Son();
}
function JiSheng(obj) {
  var clone = fun(obj);
  clone.Say = function () {
    console.log('我是新增的方法');
  }
  return clone;
}
var parent = {
  name: '张三'
}
var parent1 = JiSheng(parent);
var parent2 = JiSheng(parent);
console.log(parent2.Say==parent1.Say);// false

以上例子解释:
①再原型式继承的基础上,封装一个JiSheng函数
②将fun函数返回的对象进行增强,新增Say方法,最后返回
③调用JiSheng函数两次,分别赋值给变量parent1、parent2
④对比parent1、parent2,结果为false,实现独立
优缺点:跟构造函数继承类似,调用一次函数就得创建一遍方法,无法实现函数复用,效率较低

这里补充一个知识点,ES5有一个新的方法Object.create(),这个方法相当于封装了原型式继承。这个方法可以接收两个参数:第一个是新对象的原型对象(可选的),第二个是新对象新增属性,所以上面代码还可以这样:

function JiSheng(obj) {
  var clone = Object.create(obj);
  clone.Say = function () {
    console.log('我是新增的方法');
  }
  return clone;
}
var parent = {
  name: '张三'
}
var parent1 = JiSheng(parent);
var parent2 = JiSheng(parent);
console.log(parent2.Say==parent1.Say);// false

第六种:寄生组合继承

利用组合继承和寄生继承各自优势
组合继承方法我们已经说了,它的缺点是两次调用父级构造函数,一次是在创建子级原型的时候,另一次是在子级构造函数内部,那么我们只需要优化这个问题就行了,即减少一次调用父级构造函数,正好利用寄生继承的特性,继承父级构造函数的原型来创建子级原型。

function JiSheng(son,parent) {
  var clone = Object.create(parent.prototype);//创建对象
  son.prototype = clone;   //指定对象
  clone.constructor = son;   //增强对象
}
function Parent(name){
  this.name = name;
  this.type = ['JS','HTML','CSS'];
}
Parent.prototype.Say=function(){
  console.log(this.name);
}
function Son(name){
  Parent.call(this,name);
}
JiSheng(Son,Parent);
son1 = new Son('张三');
son2 = new Son('李四');
son1.type.push('VUE');
son2.type.push('PHP');
console.log(son1.type);//['JS','HTML','CSS','VUE']
console.log(son2.type);//['JS','HTML','CSS','PHP']
son1.Say();//张三
son2.Say();//李四

以上例子解释:
①封装一个函数JiSheng,两个参数,参数1为子级构造函数,参数2为父级构造函数
②利用Object.create(),将父级构造函数原型克隆为副本clone
③将该副本作为子级构造函数的原型
④给该副本添加constructor属性,因为③中修改原型导致副本失去默认的属性

优缺点:
组合继承优点、寄生继承的有点,目前JS继承中使用的都是这个继承方法

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Extjs ajax同步请求时post方式参数发送方式
Aug 05 Javascript
通过javascript设置css属性的代码
Dec 28 Javascript
jquery中map函数与each函数的区别实例介绍
Jun 23 Javascript
JS更改select内option属性的方法
Oct 14 Javascript
JavaScipt选取文档元素的方法(推荐)
Aug 05 Javascript
微信小程序 POST请求(网络请求)详解及实例代码
Nov 16 Javascript
JS正则匹配URL网址的方法(可匹配www,http开头的一切网址)
Jan 06 Javascript
微信小程序 动态的设置图片的高度和宽度详解及实例代码
Feb 24 Javascript
vue 页面加载进度条组件实例
Feb 05 Javascript
ES6的异步终极解决方案分享
Jul 11 Javascript
javascript设计模式 ? 抽象工厂模式原理与应用实例分析
Apr 09 Javascript
javascript实现移动端轮播图
Dec 09 Javascript
使用node.JS中的url模块解析URL信息
Feb 06 #Javascript
Node.JS获取GET,POST数据之queryString模块使用方法详解
Feb 06 #Javascript
node.JS事件机制与events事件模块的使用方法详解
Feb 06 #Javascript
如何通过javaScript去除字符串两端的空白字符
Feb 06 #Javascript
ckeditor一键排版功能实现方法分析
Feb 06 #Javascript
JavaScript如何判断input数据类型
Feb 06 #Javascript
如何使用Jquery动态生成二级选项列表
Feb 06 #jQuery
You might like
PHP的历史和优缺点
2006/10/09 PHP
php REMOTE_ADDR之获取访客IP的代码
2008/04/22 PHP
PHP使用文件锁解决高并发问题示例
2018/03/29 PHP
PHP高并发和大流量解决方案整理
2019/12/24 PHP
php设计模式之抽象工厂模式分析【星际争霸游戏案例】
2020/01/23 PHP
function, new function, new Function之间的区别
2007/03/08 Javascript
ExtJs GridPanel简单的增删改实现代码
2010/08/26 Javascript
js编码、解码函数介绍及其使用示例
2013/09/05 Javascript
jquery判断RadioButtonList和RadioButton中是否有选中项示例
2013/09/29 Javascript
JavaScript获取table中某一列的值的方法
2014/05/06 Javascript
JQuery控制div外点击隐藏而div内点击不会隐藏的方法
2015/01/13 Javascript
JavaScript中的值是按值传递还是按引用传递问题探讨
2015/01/30 Javascript
jquery小火箭返回顶部代码分享
2015/08/19 Javascript
JavaScript实现点击按钮复制指定区域文本(推荐)
2016/11/25 Javascript
ionic2屏幕适配实现适配手机、平板等设备的示例代码
2017/08/11 Javascript
JavaScript设计模式之责任链模式实例分析
2019/01/16 Javascript
JavaScript闭包与作用域链实例分析
2019/01/21 Javascript
[01:04:01]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS DT第一场
2014/05/24 DOTA
[00:34]TI7不朽珍藏III——纯金地穴编织者饰品展示
2017/07/15 DOTA
基于python 二维数组及画图的实例详解
2018/04/03 Python
python实现对任意大小图片均匀切割的示例
2018/12/05 Python
PyQt5的安装配置过程,将ui文件转为py文件后显示窗口的实例
2019/06/19 Python
Python3 执行Linux Bash命令的方法
2019/07/12 Python
python元组和字典的内建函数实例详解
2019/10/22 Python
在Python3.74+PyCharm2020.1 x64中安装使用Kivy的详细教程
2020/08/07 Python
初中女生自我鉴定
2013/12/19 职场文书
父亲的菜园教学反思
2014/02/13 职场文书
教师先进工作者事迹材料
2014/05/01 职场文书
2015年五四青年节活动总结
2015/02/10 职场文书
销售辞职信范文
2015/03/02 职场文书
2015年员工工作总结范文
2015/04/08 职场文书
敬老院义诊活动总结
2015/05/07 职场文书
解约证明模板
2015/06/19 职场文书
听证会主持词
2015/07/03 职场文书
go语言求任意类型切片的长度操作
2021/04/26 Golang
SpringBoot读取Resource下文件的4种方法
2021/07/02 Java/Android