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 相关文章推荐
jQuery 学习第五课 Ajax 使用说明
May 17 Javascript
利用JQuery和JS实现奇偶行背景颜色自定义效果
Nov 19 Javascript
用JavaScript实现动画效果的方法
Jul 20 Javascript
JavaScript数组去重的两种方法推荐
Apr 05 Javascript
浅谈javascript中new操作符的原理
Jun 07 Javascript
Vue.Js中的$watch()方法总结
Mar 23 Javascript
angular使用bootstrap方法手动启动的实例代码
Jul 18 Javascript
关于JavaScript中的this指向问题总结篇
Jul 23 Javascript
JavaScript 数组的进化与性能分析
Sep 18 Javascript
Vue中的transition封装组件的实现方法
Aug 13 Javascript
node.js开发辅助工具nodemon安装与配置详解
Feb 06 Javascript
如何使用RoughViz可视化Vue.js中的草绘图表
Jan 30 Vue.js
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
解析php防止form重复提交的方法
2013/07/01 PHP
PHP处理postfix邮件内容的方法
2015/06/16 PHP
用PHP代码在网页上生成图片
2015/07/01 PHP
利用PHP生成CSV文件简单示例
2016/12/21 PHP
php获取微信共享收货地址的方法
2017/12/21 PHP
php生成二维码不保存服务器还有下载功能的实现代码
2018/08/09 PHP
兼容ie和firefox js关闭代码
2008/12/11 Javascript
JavaScript 继承详解(二)
2009/07/13 Javascript
js switch case default 的用法示例介绍
2013/10/23 Javascript
checkbox选中与未选中判断示例
2014/08/04 Javascript
用jquery模仿的a的title属性的例子
2014/10/22 Javascript
浅析JavaScript 箭头函数 generator Date JSON
2016/05/23 Javascript
angular ngClick阻止冒泡使用默认行为的方法
2016/11/03 Javascript
vue单个组件实现无限层级多选菜单功能
2018/04/10 Javascript
使用JavaScript通过前端发送电子邮件
2020/05/22 Javascript
js利用iframe实现选项卡效果
2020/08/09 Javascript
antd日期选择器禁止选择当天之前的时间操作
2020/10/29 Javascript
uniapp微信小程序:key失效的解决方法
2021/01/20 Javascript
[04:44]显微镜下的DOTA2第二期——你所没有注意到的细节
2014/06/20 DOTA
[01:07:20]DOTA2-DPC中国联赛 正赛 Dynasty vs XG BO3 第二场 2月2日
2021/03/11 DOTA
在Django框架中编写Contact表单的教程
2015/07/17 Python
Python3 无重复字符的最长子串的实现
2019/10/08 Python
浅谈django 模型类使用save()方法的好处与注意事项
2020/03/28 Python
基于python实现对文件进行切分行
2020/04/26 Python
Python 如何创建一个线程池
2020/07/28 Python
菲律宾最大的网上花店和礼品店:PhilFlower.com
2018/02/09 全球购物
Amara德国:家居饰品、设计师品牌和豪华礼品
2019/05/20 全球购物
工商管理毕业生推荐信
2013/12/24 职场文书
行政文秘岗位职责范本
2014/02/10 职场文书
测量工程专业求职信
2014/02/24 职场文书
创先争优承诺书范文
2014/03/31 职场文书
真诚的求职信
2014/07/04 职场文书
公司辞职信模板
2015/05/13 职场文书
歌咏比赛主持词
2015/06/29 职场文书
自考生自我评价
2019/06/21 职场文书
MySQL普通表如何转换成分区表
2022/05/30 MySQL