JS精髓原型链继承及构造函数继承问题纠正


Posted in Javascript onJune 16, 2022

前言

先从面向对象讲起,本瓜认为:面向对象编程,它的最大能力就是:复用!

咱常说,面向对象三大特点,封装、继承、多态。

这三个特点,以“继承”为核心。封装成类,是为了继承,继承之后再各自发展(重写),可理解为多态。所以,根本目的是为了继承,即“复用“!

如果你用 JavaScript 面向对象的能力来编程的话,能想到的,也只供使用的就是:基于原型

因为这门语言设计就是这样,我们之前也提过:JavaScript的语言设计主要受到了Self(一种基于原型的编程语言)和 Scheme(一门函数式编程语言)的影响;

它复用的能力就是来自原型!

好了,有这个认知基础,我们再看原型继承。

原型链继承

原型继承最直接的一种实现就是:原型链继承

ECMA-262 把原型链定义为 ECMAScript 的主要继承方式。其基本思想就是通过原型继承多个引用类型的属性和方法。

我们来看看原型链继承的代码实现:

function SuperType() {
	 this.property = true;
}
function SubType() {
	 this.subproperty = false;
}
SuperType.prototype.getSuperValue = function() {
 return this.property;
};
SubType.prototype.getSubValue = function () {
 return this.subproperty;
};
SubType.prototype = new SuperType(); // 对 SubType 得原型链重新指定,是原型链继承
let instance = new SubType();
console.log(instance.getSuperValue()); // true

还需要再额外说明查找关系吗??不懂得工友可见这篇 《歪理解?原型链中的函数和对象》

这里还是用代码展示下它们的指向关系吧:

上面例子中有 1 个对象 instance , 两个函数,SuperType 和 SubType 。函数是上帝,对象是基本物质。

继承来自两方面:

  • 1. 继承自祖先(遗产);
  • 2. 继承自上帝(天赋);
//  继承自祖先(遗产)
instance.__proto__ === SubType.prototype // true
SubType.prototype.__proto__ === SuperType.prototype // true
// 继承自上帝(天赋)
SuperType.__proto__  === Function.prototype // true
SubType.__proto__  === Function.prototype // true
SuperType.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // true

当然,我们并不是来讲原型链的。重点是:点出原型链继承的“问题”!!

它的主要问题出现在:原型中包含引用值的时候,原型中包含的引用值会在所有实例间共享。

function SuperType() {
 this.colors = ["red", "blue", "green"];
}
function SubType() {}
SubType.prototype = new SuperType() // 原型链继承
let s1 = new SubType()
let s2 = new SubType()
s1.colors.push("yellow")
console.log(s1.colors) // ['red', 'blue', 'green', 'yellow']
console.log(s2.colors) // ['red', 'blue', 'green', 'yellow']

colors 是个数组,引用值,当它共享给 SubType 的时候,用的是引用值,当我们实例化的时候,如果其中一个实力对它做出了修改,将会影响到其它实例的引用。

其实,我们也知道,很少在业务代码中这样去写继承:SubType.prototype = new SuperType() ,原型链继承会造成复用的混乱,所以它基本不会被单独使用。

构造函数继承

构造函数继承,也叫做:“盗用构造函数”,“对象伪装”或“经典继承”。

基本思路:在子类构造函数中用 apply()和 call()方法调用父类构造函数。

上一小节的例子改造为:

function SuperType() {
 this.colors = ["red", "blue", "green"];
}
function SubType() {
	SuperType.call(this) // 构造函数继承
}
let s1 = new SubType()
let s2 = new SubType()
s1.colors.push("yellow")
console.log(s1.colors) // ['red', 'blue', 'green', 'yellow']
console.log(s2.colors) // ['red', 'blue', 'green']

完美解决原型链继承的问题,但是它也有它的问题,也是使用构造函数模式自定义类型的问题,

即:必须在构造函数中定义方法(在原型上定义方法,子类是访问不到的),函数不能重用

function SuperType() {
}
function SubType() {
	SuperType.call(this) // 构造函数继承
}
SuperType.prototype.fn = ()=>{}
let s1 = new SubType()
console.log(s1.fn) // undefined
function SuperType() {
	 this.fn=()=>{}
}
function SubType() {
	SuperType.call(this) // 构造函数继承
}
let s1 = new SubType()
let s2 = new SubType()
console.log(s1.fn === s2.fn) // false

而这一点,在原型链继承中,又是可以的。。。

function SuperType() {}
function SubType() {}
SuperType.prototype.fn = ()=>{}
SubType.prototype = new SuperType() // 原型链继承
let s1 = new SubType()
console.log(s1.fn) // ()=>{}
function SuperType() {
	 this.fn=()=>{}
}
function SubType() {}
SubType.prototype = new SuperType() // 原型链继承
let s1 = new SubType()
let s2 = new SubType()
console.log(s1.fn === s2.fn) // true

所以,综上,原型链继承和构造函数继承的 “毛病” 分别是:

  • 原型链继承:所有继承的属性和方法都会在对象实例间共享,无法做到实例私有。
  • 构造函数继承:子类不能访问父类原型上的方法。

咱就是说,这东西怎么这么拧巴呢。。。

于是乎一个规避二者“毛病”的继承方式出现了:组合继承~~

组合继承

目前最流行的继承模式是组合继承!

思路是:使用原型链继承原型上的属性和方法,而通过构造函数继承实例属性。

function SuperType(name){
	 this.name = name;
	 this.colors = ["red", "blue", "green"];
}
function SubType(name, age){
	 SuperType.call(this, name) // 构造函数继承
	 this.age = age;
}
SuperType.prototype.sayName = function() {
	 console.log(this.name);
}
SubType.prototype = new SuperType() // 原型链继承
SubType.prototype.sayAge = function() {
	 console.log(this.age);
}
let s1 = new SubType("Nicholas", 29)
let s2= new SubType("Greg", 27)
s1.colors.push("yellow")
console.log(s1.colors) // ['red', 'blue', 'green', 'yellow']
console.log(s2.colors) // ['red', 'blue', 'green']
s1.sayName() // Nicholas
s2.sayName() // Greg
s1.sayAge() // 29
s2.sayAge() // 27

组合继承,总结起来就是,属性(特别是引用值)通过构造函数去继承,而公用的、需要复用的方法用原型链去继承!!

说实话,JS 继承真的很奇怪。。。并不是面向对象语言,又要通过原型链去模拟面向对象,真的很多小坑的点需要去注意。(哈哈哈,想想还是函数式好,清晰)

以上就是JS精髓原型链继承及构造函数继承问题纠正的详细内容,更多关于JS原型链继承构造函数继承的资料请关注三水点靠木其它相关文章!


Tags in this post...

Javascript 相关文章推荐
javascript 写类方式之六
Jul 05 Javascript
jquery的Theme和Theme Switcher使用小结
Sep 08 Javascript
调用HttpHanlder的几种返回方式小结
Dec 20 Javascript
javascript匿名函数实例分析
Nov 18 Javascript
javascript实现输出指定行数正方形图案的方法
Aug 03 Javascript
JavaScript深度复制(deep clone)的实现方法
Feb 19 Javascript
React组件中的this的具体使用
Feb 28 Javascript
vue中关闭eslint的方法分析
Aug 04 Javascript
vue实现将一个数组内的相同数据进行合并
Nov 07 Javascript
JavaScript实现联动菜单特效
Jan 07 Javascript
微信小程序换肤功能实现代码(思路详解)
Aug 25 Javascript
JavaScript arguments.callee作用及替换方案详解
Sep 02 Javascript
5个实用的JavaScript新特性
Jun 16 #Javascript
字节飞书面试promise.all实现示例
Jun 16 #Javascript
JS轻量级函数式编程实现XDM三
Jun 16 #Javascript
JS轻量级函数式编程实现XDM二
Jun 16 #Javascript
JS函数式编程实现XDM一
Jun 16 #Javascript
正则表达式基础与常用验证表达式
Jun 16 #Javascript
使用compose函数优化代码提高可读性及扩展性
You might like
Zerg剧情介绍
2020/03/14 星际争霸
如何解决PHP使用mysql_query查询超大结果集超内存问题
2016/03/14 PHP
PHP排序算法之快速排序(Quick Sort)及其优化算法详解
2018/04/21 PHP
JQuery 选项卡效果(JS与HTML的分离)
2010/04/01 Javascript
几个比较实用的JavaScript 测试及效验工具
2010/04/18 Javascript
ExtJs事件机制基本代码模型和流程解析
2010/10/24 Javascript
JavaScript高级程序设计 阅读笔记(十八) js跨平台的事件
2012/08/14 Javascript
JavaScript设计模式之代理模式介绍
2014/12/28 Javascript
javascript生成随机数方法汇总
2015/11/12 Javascript
微信小程序(六):列表上拉加载下拉刷新示例
2017/01/13 Javascript
简单实现js悬浮导航效果
2017/02/05 Javascript
jQuery实现的表格前端排序功能示例
2017/09/18 jQuery
详解如何实现Element树形控件Tree在懒加载模式下的动态更新
2019/04/25 Javascript
[23:21]Ti4 冒泡赛第二轮DK vs C9 2
2014/07/14 DOTA
[03:45]Newbee战队出征西雅图 决战2016国际邀请赛
2016/08/02 DOTA
使用python实现省市三级菜单效果
2016/01/20 Python
python3使用smtplib实现发送邮件功能
2018/05/22 Python
pycharm 解除默认unittest模式的方法
2018/11/30 Python
python实现AES加密与解密
2019/03/28 Python
Python3.5内置模块之shelve模块、xml模块、configparser模块、hashlib、hmac模块用法分析
2019/04/27 Python
Python3.5集合及其常见运算实例详解
2019/05/01 Python
pyqt5数据库使用详细教程(打包解决方案)
2020/03/25 Python
Pytest框架之fixture的详细使用教程
2020/04/07 Python
简述 Python 的类和对象
2020/08/21 Python
html5 Canvas画图教程(1)—画图的基本常识
2013/01/09 HTML / CSS
世界最大域名注册商:GoDaddy
2016/07/24 全球购物
苹果香港官方商城:Apple香港
2016/09/14 全球购物
教师实习期自我鉴定
2013/10/06 职场文书
预备党员党校学习自我评价分享
2013/11/12 职场文书
主持人演讲稿范文
2013/12/28 职场文书
如何打造一封优秀的留学推荐信
2014/01/25 职场文书
护士自我评价
2014/02/01 职场文书
挂职自我鉴定
2014/02/26 职场文书
思想品德评语大全
2014/12/31 职场文书
世界气象日活动总结
2015/02/27 职场文书
雾霾停课通知
2015/04/24 职场文书