JS的五种继承方式

作者: 大佬教我写程序 | 来源:发表于2021-07-12 20:14 被阅读0次

原型对象

  • 每个函数都有prototype属性,他就是原型对象


    image.png
  • 原型对象的作用:为每个实例对象存储共享的方法和属性,原型对象只有一份

JS五种继承方式

方式一:原型链继承
  • 核⼼:将⽗类实例作为⼦类原型
  • 优点:⽅法复⽤
    由于⽅法定义在⽗类的原型上,复⽤了⽗类构造函数的⽅法。⽐如say⽅法。
  • 缺点:
    创建⼦类实例的时候,不能传⽗类的参数(⽐如name)。
    ⼦类实例共享了⽗类构造函数的引⽤属性,⽐如arr属性。
    ⽆法实现多继承。
function Parent(name) {
  this.name = name || '⽗亲'; // 实例基本属性 (该属性,强调私有,不共享)
  this.arr = [1]; // (该属性,强调私有)
}
Parent.prototype.say = function() { // -- 将需要复⽤、共享的⽅法定义在⽗类原型上
  console.log('hello')
}

function Child(like) {
  this.like = like;
}
Child.prototype = new Parent() // 核⼼,但此时Child.prototype.constructor==Parent
Child.prototype.constructor = Child // 修正constructor指向
let boy1 = new Child()
let boy2 = new Child()
  // 优点:共享了⽗类构造函数的say⽅法
console.log(boy1.say(), boy2.say(), boy1.say === boy2.say); // hello , hello , true
// 缺点1:不能向⽗类构造函数传参
console.log(boy1.name, boy2.name, boy1.name === boy2.name); // ⽗亲,⽗亲,true
/ 缺点2: ⼦类实例共享了⽗类构造函数的引⽤属性,⽐如arr属性
boy1.arr.push(2);
// 修改了boy1的arr属性,boy2的arr属性,也会变化,因为两个实例的原型上(Child.prototype)有了⽗类构造函数的实例属性arr;
所以只要修改了boy1.arr, boy2.arr的属性也会变化。
console.log(boy2.arr); // [1,2]
方式二:借用构造函数
  • 核心:复制父类的实例给子类
  • 优点:实例之间独立
  • 缺点:
    ⽗类的⽅法不能复⽤
    由于⽅法在⽗构造函数中定义,导致⽅法不能复⽤(因为每次创建⼦类实例都要创建⼀遍⽅法)。
    ⽐如say⽅法。(⽅法应该要复⽤、共享)
    ⼦类实例,继承不了⽗类原型上的属性。(因为没有⽤到原型)
function Parent(name) {
  this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
  this.arr = [1]; // (该属性,强调私有)
  this.say = function() { // 实例引⽤属性 (该属性,强调复⽤,需要共享)
    console.log('hello')
  }
}

function Child(name, like) {
  Parent.call(this, name); // 核⼼ 拷⻉了⽗类的实例属性和⽅法
  this.like = like;
}
let boy1 = new Child('⼩红', 'apple');
let boy2 = new Child('⼩明', 'orange ');
// 优点1:可向⽗类构造函数传参
console.log(boy1.name, boy2.name); // ⼩红, ⼩明
// 优点2:不共享⽗类构造函数的引⽤属性
boy1.arr.push(2);
console.log(boy1.arr, boy2.arr); // [1,2] [1]
// 缺点1:⽅法不能复⽤
console.log(boy1.say === boy2.say) // false (说明,boy1和boy2的say⽅法是独⽴,不是共享的)
  // 缺点2:不能继承⽗类原型上的⽅法
Parent.prototype.walk = function() { // 在⽗类的原型对象上定义⼀个walk⽅法。
  console.log('我会⾛路')
}
boy1.walk; // undefined (说明实例,不能获得⽗类原型上的⽅法)
方式三:组合继承
  • 核⼼:通过调⽤⽗类构造函数,继承⽗类的属性并保留传参的优点;然后通过将⽗类实例作为
    ⼦类原型,实现函数复⽤。
  • 优点:
    保留构造函数的优点:创建⼦类实例,可以向⽗类构造函数传参数。
    保留原型链的优点:⽗类的⽅法定义在⽗类的原型对象上,可以实现⽅法复⽤。
    不共享⽗类的引⽤属性。⽐如arr属性
  • 缺点:
    由于调⽤了2次⽗类的构造⽅法,会存在⼀份多余的⽗类实例属性,具体原因⻅⽂末。
    注意:'组合继承'这种⽅式,要记得修复Child.prototype.constructor指向
    第⼀次Parent.call(this);从⽗类拷⻉⼀份⽗类实例属性,作为⼦类的实例属性,第⼆次
    Child.prototype = new Parent();创建⽗类实例作为⼦类原型,Child.protype中的⽗类属性和⽅法
    会被第⼀次拷⻉来的实例属性屏蔽掉,所以多余。
function Parent(name) {
  this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
  this.arr = [1]; // (该属性,强调私有)
}
Parent.prototype.say = function() { // --- 将需要复⽤、共享的⽅法定义在⽗类原型上
  console.log('hello')
}

function Child(name, like) {
  Parent.call(this, name, like) // 核⼼ 第⼆次
  this.like = like;
}
Child.prototype = new Parent() // 核⼼ 第⼀次
Child.prototype.constructor = Child // 修正constructor指向
let boy1 = new Child('⼩红', 'apple')
let boy2 = new Child('⼩明', 'orange')
  // 优点1:可以向⽗类构造函数传参数
console.log(boy1.name, boy1.like); // ⼩红,apple
// 优点2:可复⽤⽗类原型上的⽅法
console.log(boy1.say === boy2.say) // true
  // 优点3:不共享⽗类的引⽤属性,如arr属性
boy1.arr.push(2)
console.log(boy1.arr, boy2.arr); // [1,2] [1] 可以看出没有共享arr属性。
// 缺点1:由于调⽤了2次⽗类的构造⽅法,会存在⼀份多余的⽗类实例属性
方式四:组合继承优化
  • 核⼼:
    通过这种⽅式,砍掉⽗类的实例属性,这样在调⽤⽗类的构造函数的时候,就不会初始化两次实
    例,避免组合继承的缺点。
  • 优点:
    只调⽤⼀次⽗类构造函数。
    保留构造函数的优点:创建⼦类实例,可以向⽗类构造函数传参数。
    保留原型链的优点:⽗类的实例⽅法定义在⽗类的原型对象上,可以实现⽅法复⽤。
  • 缺点:
    修正构造函数的指向之后,⽗类实例的构造函数指向,同时也发⽣变化(这是我们不希望的)
    注意:'组合继承优化1'这种⽅式,要记得修复Child.prototype.constructor指向
    原因是:不能判断⼦类实例的直接构造函数,到底是⼦类构造函数还是⽗类构造函数。
function Parent(name) {
  this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
  this.arr = [1]; // (该属性,强调私有)
}
Parent.prototype.say = function() { // --- 将需要复⽤、共享的⽅法定义在⽗类原型上
  console.log('hello')
}

function Child(name, like) {
  Parent.call(this, name, like) // 核⼼
  this.like = like;
}
Child.prototype = Parent.prototype // 核⼼ ⼦类原型和⽗类原型,实质上是同⼀个
  <!--这⾥是修复构造函数指向的代码-->
Child.prototype.constructor = Child
let boy1 = new Child('⼩红', 'apple')
let boy2 = new Child('⼩明', 'orange')
let p1 = new Parent('⼩爸爸')
  // 优点1:可以向⽗类构造函数传参数
console.log(boy1.name, boy1.like); // ⼩红,apple
// 优点2:可复⽤⽗类原型上的⽅法
console.log(boy1.say === boy2.say) // true
  // 缺点1:当修复⼦类构造函数的指向后,⽗类实例的构造函数指向也会跟着变了。
没修复之前: console.log(boy1.constructor); // Parent
修复代码: Child.prototype.constructor = Child
修复之后: console.log(boy1.constructor); // Child
console.log(p1.constructor); // Child 这⾥就是存在的问题(我们希望是Parent)
具体原因: 因为是通过原型来实现继承的, Child.prototype的上⾯ 是没有constructor属性的,
就会往上找, 这样就找到了Parent.prototype上⾯ 的constructor属性; 当你修改了⼦ 类实例的
construtor属性, 所有的constructor的指向都会发⽣ 变化。
方式五:寄生组合继承-----完美继承
function Parent(name) {
  this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
  this.arr = [1]; // (该属性,强调私有)
}
Parent.prototype.say = function() { // --- 将需要复⽤、共享的⽅法定义在⽗类原型上
  console.log('hello')
}

function Child(name, like) {
  Parent.call(this, name, like) // 核⼼
  this.like = like;
}
// 核⼼ 通过创建中间对象,⼦类原型和⽗类原型,就会隔离开。不是同⼀个啦,有效避免了⽅式4的缺点。
Child.prototype = Object.create(Parent.prototype)
  // 这⾥是修复构造函数指向的代码
Child.prototype.constructor = Child
let boy1 = new Child('⼩红', 'apple')
let boy2 = new Child('⼩明', 'orange')
let p1 = new Parent('⼩爸爸')

相关知识点

  • new 的过程
    创建新对象(如obj)。
    将新对象的proto指向构造函数的prototype对象。
    执⾏构造函数,为这个新对象添加属性,并将this指向创建的新对象obj。
    当构造函数本⾝返回值为对象时,返回该对象,否则返回新对象。
  • Object.create()⽅法创建⼀个新对象,使⽤第⼀个参数来提供新创建对象的proto(以第⼀个参
    数作为新对象的构造函数的原型对象);
    ⽅法还有第⼆个可选参数,是添加到新创建对象的属性,写法如下。
const a = Object.create(Person.prototype, {
  age: {
    value: 12,
    writable: true,
    configurable: true,
  }
})
  • new 与 Object.create() 的区别?
    new 产⽣的实例,优先获取构造函数上的属性;构造函数上没有对应的属性,才会去原型上查找;
    如果构造函数中以及原型中都没有对应的属性,就会报错。Object.create() 产⽣的对象,只会在原型上进⾏查找属性,原型上没有对应的属性,就会报错。

相关文章

  • js 的继承的几种方式

    js 继承有6种方式的代码。 js继承的6种方式[https://www.cnblogs.com/Grace-zy...

  • 继承

    研究学习了js内部的继承方式,以及多种方式的优点和缺点 目前项目中的 以前项目中的 js中继承有多种方式 原型继承...

  • JS继承

    JS中的继承 许多OO语言都支持两种继承方式:接口继承和实现继承; 因为JS中没有类和接口的概念 , 所以JS不支...

  • #js继承

    js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承)类式继承(构造函数间的继承) 类式继承是...

  • JavaScript 继承

    继承是JS中非常内容,原因就是JS没有地道的继承方式,我们只能通过各种方式来模拟面向对象中的继承。下面介绍几种常见...

  • js继承方式

    类式继承 构造函数继承 组合继承 类式继承 + 构造函数继承 原型式继承 寄生式继承 寄生组合式继承 寄生式继承 ...

  • JS继承方式

    js继承有多种继承方式,但最常用的是组合模式,代码例子如下 在这种方式下,把实例函数都保存在原型对象上,这样可...

  • js继承方式

    实现继承有两种常见方式: 混合式继承 最简单的继承就是将别的对象的属性强加到我身上,那么我就有这个成员了。 混合式...

  • js继承方式

    //Object.create(proObj)创建一个新的对象,但是还要把proObj作为这个对象的原型 //...

  • js继承方式

    1、原型链继承 缺点:复合类型数据改变会影响所有子函数2、构造函数继承 缺点:如果属性有公用函数,重复多了会浪费内...

网友评论

    本文标题:JS的五种继承方式

    本文链接:https://www.haomeiwen.com/subject/mkddpltx.html