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 继承真的很奇怪。。。并不是面向对象语言,又要通过原型链去模拟面向对象,真的很多小坑的点需要去注意。(哈哈哈,想想还是函数式好,清晰)

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

(0)

相关推荐

  • javascript原型链图解的总结和实践

    目录 原型链 我们可以基于原型链来实现一个简易的JQuery库 总结 原型链 在ES6中引入了class关键字,但是JS依然是基于原型的,class实际上是语法糖. 举个例子,有一个people class: class People { constructor(props) { this.name = props.name; } run() { console.log('run') } } 通过new people 这个class 产生了许多的人,张三,李四: let lisi = new P

  • JavaScript 原型与原型链详情

    目录 1.prototype(显式原型) 2.__proto__(隐式原型) 3.constructor(构造函数) 4.new的原理 5.原型链 5.1 原型链的作用 5.2 构造函数的__proto__是什么呢? 6.总结 前言: JavaScript常被描述为一种「基于原型的语言」--每个对象都拥有一个「原型对象」,对象以其原型为模板.从原型继承属性和放法.原型对象也可能拥有原型,并从中继承属性和方法,一层一层以此类推.这种关系常被称为「原型链」,它解释了为何一个对象会拥有定义在其他对象中

  • JavaScript原型链中函数和对象的理解

    目录 __ proto__ prototype.__ proto__ 理解 __ proto__ 最近在看高程4,原型链肯定是绕不过的,本瓜之前一直认为,只要记住这句话就可以了: 一个对象的隐式原型(__proto__)等于构造这个对象的构造函数的显式原型(prototype) 确实,所有对象都符合这句真理,在控制台打印一试便知: const str = new String("123") str.__proto__ === String.prototype // true const

  • JavaScript两张图搞懂原型链

    目录 1.原型的关系 2.原型链 3.结语 前言: 我们上一篇文章介绍了JavaScript原型,为什么不将原型链一起介绍了呢?因为JavaScript中的原型链是一个难点,也是一个面试必问的考点,现在就来学习一下. 1.原型的关系 在JavaScript中的每个函数都会有一个prototype属性,这个属性又会返回一个原型,原型又有一个constructor属性,这个属性指向与之关联的构造函数.通过构造函数实例化的对象会有一个__proto__属性,这个__proto__属性指向与构造函数的p

  • JavaScript原型链详解

    目录 1.构造函数和实例 2.属性Prototype 3.属性__proto__ 4.访问原型上的方法 5.构造函数也有__proto__ 6.构造函数的原型也有__proto__ 7.Object.prototype这个原型对象很特殊 8.总结 1.构造函数和实例 假设你声明一个方法叫做Foo() ,那么我们可以通过new Foo()来声明实例. function Foo() { console.log("我是一个构造方法"); } const f1 = new Foo(); 现在你

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

    目录 前言 原型链继承 构造函数继承 组合继承 前言 先从面向对象讲起,本瓜认为:面向对象编程,它的最大能力就是:复用! 咱常说,面向对象三大特点,封装.继承.多态. 这三个特点,以“继承”为核心.封装成类,是为了继承,继承之后再各自发展(重写),可理解为多态.所以,根本目的是为了继承,即“复用“! 如果你用 JavaScript 面向对象的能力来编程的话,能想到的,也只供使用的就是:基于原型. 因为这门语言设计就是这样,我们之前也提过:JavaScript的语言设计主要受到了Self(一种基于

  • JavaScript继承基础讲解(原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承)

    说好的讲解JavaScript继承,可是迟迟到现在讲解.废话不多说,直接进入正题. 既然你想了解继承,证明你对JavaScript面向对象已经有一定的了解,如还有什么不理解的可以参考<面向对象JS基础讲解,工厂模式.构造函数模式.原型模式.混合模式.动态原型模式>,接下来讲一般通过那些方法完成JavaScript的继承. 原型链 JavaScript中实现继承最简单的方式就是使用原型链,将子类型的原型指向父类型的实例即可,即"子类型.prototype = new 父类型();&qu

  • 深入浅析js原型链和vue构造函数

    一.什么是原型链? 简单回顾下构造函数,原型和实例的关系: 每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针. 然鹅,在js对象里有这么一个规则: 如果试图引用对象(实例instance)的某个属性,会首先在对象内部寻找该属性,直至找不到,然后才在该对象的原型(instance.prototype)里去找这个属性. 少废话,先来看个例子: function Fun1 ()

  • JavaScript原型链及常见的继承方法

    目录 原型链 原型链的概念 原型链的问题 几种常见的继承方法 盗用构造函数 组合继承 原型式继承 寄生式继承 寄生组合式继承 原型链 原型链的概念 在JavaScript中,每一个构造函数都有一个原型,这个原型中有一个属性constructor会再次指回这个构造函数,这个构造函数所创造的实例对象,会有一个指针(也就是我们说的隐式原型__proto__或者是浏览器中显示的[[Prototype]])指向这个构造函数的原型对象.如果说该构造函数的原型对象也是由另外一个构造函数所创造的实例,那么该构造

  • javascript原型链学习记录之继承实现方式分析

    本文实例讲述了javascript原型链学习记录之继承实现方式.分享给大家供大家参考,具体如下: 在慕课网学习继承的笔记: 继承的几种方式: ① 使用构造函数实现继承 function Parent(){ this.name = 'parent'; } function Child(){ Parent.call(this); //在子类函数体里面执行父类的构造函数 this.type = 'child';//子类自己的属性 } Parent.call(this),this即实例,使用this执行

  • JavaScript 原型继承之构造函数继承

    上回说到<JavaScript 原型继承之基础机制>,这一篇将具体说说构造函数的继承. 从一个简单的示例开始,创建描述人类的 People 构造函数: 复制代码 代码如下: function People(){ this.race = '愚蠢的人类'; } 然后,创建描述黄种人的 Yellow 构造函数: 复制代码 代码如下: function Yellow(name, skin){ this.name = name; this.skin = skin; } 要使得黄种人 Yellow 能继承

  • 一篇文章让你看懂Js继承与原型链

    目录 继承与原型链 继承属性 继承方法 在 JavaScript 中使用原型 性能 附:原型链是实现继承的主要方法 总结 继承与原型链 当谈到继承时,JavaScript 只有一种结构:对象.每个实例对象(object)都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype).该原型对象也有一个自己的原型对象(proto),层层向上直到一个对象的原型对象为 null.根据定义,null 没有原型,并作为这个原型链中的最后一个环节. 几乎所有 JavaScript

  • 一文详解如何用原型链的方式实现JS继承

    目录 原型链是什么 通过构造函数创建实例对象 用原型链的方式实现继承 方法1:Object.create 方法2:直接修改 [[prototype]] 方法3:使用父类的实例 总结 今天讲一道经典的原型链面试题. 原型链是什么 JavaScript 中,每当创建一个对象,都会给这个对象提供一个内置对象 [[Prototype]] .这个对象就是原型对象,[[Prototype]] 的层层嵌套就形成了原型链. 当我们访问一个对象的属性时,如果自身没有,就会通过原型链向上追溯,找到第一个存在该属性原

  • 基于JavaScript实现继承机制之构造函数+原型链混合方式的使用详解

    构造函数.原型实现继承的缺陷 首先来分析构造函数和原型链两种实现继承方式的缺陷: 构造函数(对象冒充)的主要问题是必须使用构造函数方式,且无法继承通过原型定义的方法,这不是最好的选择.不过如果使用原型链,就无法使用带参数的构造函数了.开发者如何选择呢?答案很简单,两者都用. 构造函数+原型混合方式 这种继承方式使用构造函数定义类,并非使用任何原型.创建类的最好方式是用构造函数定义属性,用原型定义方法.这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承 prototype 对象

  • javascript基于原型链的继承及call和apply函数用法分析

    本文实例讲述了javascript基于原型链的继承及call和apply函数用法.分享给大家供大家参考,具体如下: 1. 继承是面向对象编程语言的一个重要特性,比如Java中,通过extend可以实现多继承,但是JavaScript中的继承方式跟JAVA中有很大的区别,JS中通过原型链的方式实现继承. (1)对象的原型:因为JS中,函数也是对象,因此我们先从对象出发.什么是对象的原型,原型的定义为: 所有通过对象直接量创建的对象都具有同一个函数原型,并且可以通过Object.prototype获

随机推荐