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 ztree实现下拉树形框使用到了json数据
May 14 Javascript
JavaScript 作用域链解析
Nov 13 Javascript
js实现从数组里随机获取元素
Jan 12 Javascript
js+HTML5实现canvas多种颜色渐变效果的方法
Jun 05 Javascript
jQuery实现仿微软首页感应鼠标变化滑动窗口效果
Oct 08 Javascript
JavaScript希尔排序、快速排序、归并排序算法
May 08 Javascript
AngularJS基础 ng-srcset 指令简单示例
Aug 03 Javascript
jQuery中过滤器的基本用法示例
Oct 11 jQuery
vue2.0 路由不显示router-view的解决方法
Mar 06 Javascript
详解JS转换数值函数Number()、parseInt()、parseFloat()
Aug 24 Javascript
vue 实现模糊检索并根据其他字符的首字母顺序排列
Sep 19 Javascript
Vue+abp微信扫码登录的实现代码示例
Jan 06 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
php AJAX实例根据邮编自动完成地址信息
2008/11/23 PHP
php $_SERVER windows系统与linux系统下的区别说明
2014/02/14 PHP
PDO防注入原理分析以及注意事项
2015/02/25 PHP
php框架CodeIgniter使用redis的方法分析
2018/04/13 PHP
TP5框架实现签到功能的方法分析
2020/04/05 PHP
asp.net下利用js实现返回上一页的实现方法小集
2009/11/24 Javascript
javascript中的变量是传值还是传址的?
2010/04/19 Javascript
jQuery 源码分析笔记(5) jQuery.support
2011/06/19 Javascript
jQuery+css实现图片滚动效果(附源码)
2013/03/18 Javascript
JavaScript String(字符串)对象的简单实例(推荐)
2016/08/31 Javascript
JavaScript操作文件_动力节点Java学院整理
2017/06/30 Javascript
vue组件父子间通信之综合练习(聊天室)
2017/11/07 Javascript
vue渲染时闪烁{{}}的问题及解决方法
2018/03/28 Javascript
用Python解决计数原理问题的方法
2016/08/04 Python
再谈Python中的字符串与字符编码(推荐)
2016/12/14 Python
Python使用getpass库读取密码的示例
2017/10/10 Python
解决Django的request.POST获取不到内容的问题
2018/05/28 Python
如何使用pyinstaller打包32位的exe程序
2019/05/26 Python
python变量命名的7条建议
2019/07/04 Python
python pycharm最新版本激活码(永久有效)附python安装教程
2020/09/18 Python
python3 字符串知识点学习笔记
2020/02/08 Python
python连接mysql有哪些方法
2020/06/24 Python
美国美发品牌:Bumble and Bumble
2016/10/08 全球购物
澳大利亚波西米亚风情网上商店:Czarina
2019/03/18 全球购物
自然健康的概念:Natural Healthy Concepts
2020/01/26 全球购物
潘婷洗发水广告词
2014/03/14 职场文书
软件专业毕业生个人自我鉴定
2014/04/17 职场文书
注册资产评估专业求职信
2014/07/16 职场文书
保密工作整改情况汇报
2014/11/06 职场文书
2014年团队工作总结
2014/11/24 职场文书
教学反思怎么写
2016/02/24 职场文书
浅谈golang 中time.After释放的问题
2021/05/05 Golang
Python OpenCV 图像平移的实现示例
2021/06/04 Python
IDEA使用SpringAssistant插件创建SpringCloud项目
2021/06/23 Java/Android
微软Win11什么功能最惊艳? Windows11新功能特性汇总
2021/11/21 数码科技
淡雅古典唯美少女娇媚宁静迷人写真
2022/03/21 杂记