记录--JS精粹,原型链继承和构造函数继承的 “毛病”
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
先从面向对象讲起,本瓜认为:面向对象编程,它的最大能力就是:复用!
咱常说,面向对象三大特点,封装、继承、多态。
这三个特点,以“继承”为核心。封装成类,是为了继承,继承之后再各自发展(重写),可理解为多态。所以,根本目的是为了继承,即“复用“!
如果你用 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 继承真的很奇怪。。。并不是面向对象语言,又要通过原型链去模拟面向对象,真的很多小坑的点需要去注意。(哈哈哈,想想还是函数式好,清晰)
本文转载于:
https://juejin.cn/post/7107779239281164301
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
记录--JS精粹,原型链继承和构造函数继承的 “毛病”的更多相关文章
- JS原型,原型链,类,继承,class,extends,由浅到深
一.构造函数和原型 1.构造函数.静态成员和实例成员 在ES6之前,通常用一种称为构造函数的特殊函数来定义对象及其特征,然后用构造函数来创建对象.像其他面向对象的语言一样,将抽象后的属性和方法封装到对 ...
- JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承
说好的讲解JavaScript继承,可是迟迟到现在讲解.废话不多说,直接进入正题. 既然你想了解继承,证明你对JavaScript面向对象已经有一定的了解,如还有什么不理解的可以参考<面向对象J ...
- 探索js原型链和vue构造函数中的奥妙
这篇文章首先会讲到原型链以及原型链的一些概念,然后会通过分析vue的源码,来看一下vue的构造函数是如何被创建的,now we go! 一.什么是原型链? 简单回顾下构造函数,原型和实例的关系: ...
- 怎么理解js的原型链继承?
前言 了解java等面向对象语言的童鞋应该知道.面向对象的三大特性就是:封装,继承,多态. 今天,我们就来聊一聊继承.但是,注意,我们现在说的是js的继承. 在js的es6语法出来之前,我们想实现js ...
- js原型链理解(4)-经典继承
经典继承就是组合继承,就是组合构造函数和原型链的优点混合继承. 1.避免引用类型的属性初始化 2.避免相同方法的多次初始化 function Super(name){ this.ages = [100 ...
- 前端基本知识(二):JS的原型链的理解
之前一直对于前端的基本知识不是了解很详细,基本功不扎实,但是前端开发中的基本知识才是以后职业发展的根基,虽然自己总是以一种实践是检验真理的唯一标准,写代码实践项目才是唯一,但是经常遇到知道怎么去解决这 ...
- JS中原型链的理解
new操作符具体干了什么呢?其实很简单,就干了三件事情. var obj = {}; obj.__proto__ = Base.prototype; Base.call(obj); 第一行,我们创建了 ...
- JS高级——原型链
构造函数 构造函数是特殊的函数,里面没有returen返回值 new的过程中,会自动将对象返回,不需要return new的过程中,会执行函数中的代码,会将创建的对象赋值给构造函数中的this 基本概 ...
- js javascript 原型链详解
看了许多大神的博文,才少许明白了js 中原型链的概念,下面给大家浅谈一下,顺便也是为了巩固自己 首先看原型链之前先来了解一下new关键字的作用,在许多高级语言中,new是必不可少的关键字,其作用是为了 ...
- js对象原型链
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象.这个对象的所有属性和方法,都会被构造函数的所拥有. 这也就意味着,我们可以把所有对象实例需要共享的属性和方 ...
随机推荐
- [Java][Spring]spring profile与maven profile多环境管理
spring profile 与 maven profile 多环境管理 spring profile Spring profile是Spring提供的多环境管理方案. 如下图: 每种环境都对应一个y ...
- CF1902
A 只要不是全 \(1\) 即可. B 二分完成天数. C \(x\) 取差的 \(gcd\),\(a_{n+1}\) 见缝插针. D 用一个 map 记录按原始操作序列,要走到 \((x,y)\) ...
- 浅谈 2-SAT
SAT 是适定性(Satisfiability)问题的简称.一般形式为 k - 适定性问题,简称 k-SAT.而当 \(k>2\) 时该问题为 NP 完全的.所以我们只研究 \(k=2\) 的情 ...
- NC15479 最短路
题目链接 题目 题目描述 企鹅国中有 \(N\) 座城市,编号从 \(1\) 到 \(N\) . 对于任意的两座城市 \(i\) 和 \(j\),企鹅们可以花费 \((i\,\,xor\,\, j)* ...
- 【Unity3D】UGUI之布局组件
1 概述 布局组件主要有:水平布局(HorizontalLayoutGroup).垂直布局(VerticalLayoutGroup).网格布局(GridLayoutGroup),用于约束子控件的布 ...
- Laravel入坑指南(8)——控制台程序
我们知道,php代码不仅可以用web的形式对外提供服务,同时也可以在命令行下执行. 对于原生的php来说,假设我们有一个php文件,名为Command.php,如果想要在控制台下执行这个文件,那么我们 ...
- Modbus协议入门
1.Modbus协议是不是开源的,免费的? 标准.开放,用户可以免费.放心地使用Modbus协议,不需要交纳许可证费,也不会侵犯知识产权. 2.怎么传输,有线还是无线? 既可以有线传输如双绞线.光纤, ...
- OpenCV开发笔记(五十八):红胖子8分钟带你深入了解图像的矩(图文并茂+浅显易懂+程序源码)
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- 正则表达式re模块---day18
1.匹配单个字符 import re lst = re.findall(正则表达式,要匹配的字符串) 返回的是列表,按照正则表达式匹配到的内容都扔到列表中 # ### 1.预定义字符集 # \d 匹配 ...
- Dockerfile和docker-compose详解
Dockerfile镜像制作 docker/podman中, 镜像是容器的基础,每次执行docker run的时候都会指定哪个基本镜像作为容器运行的基础.我们之前的docker的操作都是使用来自doc ...