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 相关文章推荐
求得div 下 img的src地址的js代码
Feb 28 Javascript
关于B/S判断浏览器断开的问题讨论
Oct 29 Javascript
javascript与CSS复习(二)
Jun 29 Javascript
深入理解JS中的Function.prototype.bind()方法
Oct 11 Javascript
Bootstrap 3.x打印预览背景色与文字显示异常的解决
Nov 06 Javascript
Bootstrap基本组件学习笔记之分页(12)
Dec 08 Javascript
JS实现动态生成html table表格的方法分析
Jul 11 Javascript
vue观察模式浅析
Sep 25 Javascript
vue-cli中vue本地实现跨域调试接口
Jan 16 Javascript
解决layer图标icon不加载的问题
Sep 04 Javascript
javascript跳转与返回和刷新页面的实例代码
Nov 20 Javascript
vue 动态设置img的src地址无效,npm run build 后找不到文件的解决
Jul 26 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
英雄试炼之肉山谷—引领RPG新潮流
2020/04/20 DOTA
PHP中__get()和__set()的用法实例详解
2013/06/04 PHP
memcache一致性hash的php实现方法
2015/03/05 PHP
laravel实现一个上传图片的接口,并建立软链接,访问图片的方法
2019/10/12 PHP
推荐40款强大的 jQuery 导航插件和教程(上篇)
2012/09/14 Javascript
js里怎么取select标签里的值并修改
2012/12/10 Javascript
jquery利用ajax调用后台方法实例
2013/08/23 Javascript
jquery validation验证身份证号,护照,电话号码,email(实例代码)
2013/11/06 Javascript
在ASP.NET中使用JavaScript脚本的方法
2013/11/12 Javascript
js分页工具实例
2015/01/28 Javascript
基于javascript实现窗口抖动效果
2016/01/03 Javascript
AngularJS基础 ng-include 指令简单示例
2016/08/01 Javascript
AngularJS 模块化详解及实例代码
2016/09/14 Javascript
利用jquery实现验证输入的是否是数字、小数,包含保留几位小数
2016/12/07 Javascript
jQuery插件FusionCharts实现的MSBar2D图效果示例【附demo源码】
2017/03/24 jQuery
VUE+node(express)实现前后端分离
2019/10/13 Javascript
微信小程序仿抖音短视频切换效果的实例代码
2020/06/24 Javascript
Python学习笔记之os模块使用总结
2014/11/03 Python
Python cookbook(数据结构与算法)找到最大或最小的N个元素实现方法示例
2018/02/13 Python
对pytorch网络层结构的数组化详解
2018/12/08 Python
django与小程序实现登录验证功能的示例代码
2019/02/19 Python
python中时间、日期、时间戳的转换的实现方法
2019/07/06 Python
Python Collatz序列实现过程解析
2019/10/12 Python
优衣库美国官网:UNIQLO美国
2018/04/14 全球购物
美国最大的户外装备和服装购物网站:Backcountry
2019/10/15 全球购物
体育教师自荐信范文
2013/12/16 职场文书
2013年军训通讯稿
2014/02/05 职场文书
中文专业自荐书
2014/06/29 职场文书
党的群众路线教育实践活动批评与自我批评发言稿
2014/10/16 职场文书
出生公证书
2015/01/23 职场文书
校运会加油稿大全
2015/07/22 职场文书
运动会运动员赞词
2015/07/22 职场文书
2019年大学生职业生涯规划书最新范文
2019/03/25 职场文书
导游词之南京夫子庙
2019/12/09 职场文书
【海涛教你打DOTA】虚空假面第一视角骨弓3房29杀
2022/04/01 DOTA
微软团队与 NASA 科学家和惠普企业(HPE)的工程师合作
2022/04/21 数码科技