详解js继承

原型链继承构造函数继承组合继承(原型链+构造函数))原型式继承寄生式继承寄生组合继承
Child.prototype = new Parent()function Child() {ParentParent.call(this)}在这里插入图片描述在这里插入图片描述var anotherPerson = object(person)在这里插入图片描述在这里插入图片描述 在这里插入图片描述
子类所有实例都会共享父类属性方法都在构造函数中定义,函数无法复用调用两次父类函数必须要有一个函数以另一个函数为基础新对象有person的属性和方法。还有自己的方法解决组合模式调用两次父类函数的问题

一、原型链

在这里插入图片描述

1. 原型关系

函数通过—prototype–指向原型对象
实例对象/原型对象–proto—指向该对象的原型
原型对象—constructor–指向构造函数

2. 确定对象之间的关系

  1. instanceof
son.instanceof.father //true
  1. isPrototypeOf
father.prototype.isPrototypeOf(son) // true
  1. Object.getPrototypeOf这个方法返回[[Prototype]]的值,可以获取到一个对象的原型
Object.getPrototypeOf(son)  // father.prototype

二、继承

1. 原型链继承

让一个原型对象–>另一个类型的实例
方法:通过创建父类实例,并将其赋给子类的原型

function Parent() {  //创建父类构造函数
  this.property = true
}
Parent.prototype.getParentValue = function () {  //在父类构造函数上定义方法
  return this.property
}
function Child() {  //创建子类构造函数
  this.Childproperty = false
}
Child.prototype = new Parent()  //new Parent()创建父类的实例对象,重写了子类的原型对象
Child.prototype.getChildValue = function () { //创建子类的原型方法
  return this.Childproperty
}
var instance = new Child()  //创建子类的实例
console.log(instance.getParentValue()) // true 

存在的问题

function Parent() {
  this.colors = [‘red’, ‘blue’, ‘green’]
}
function Child() {
}
Child.prototype = new Parent()
var instance1 = new Child()
var instance2 = new Child()
instance1.colors.push(‘black’)
console.log(instance1.colors)  // [“red”, “blue”, “green”, “black”]
console.log(instance2.colors) // [“red”, “blue”, “green”, “black”]

从上面代码可以看出,

  1. 每个子类的实例对象都可以访问父类的原型方法;
  2. 若其中一个子类实例改变了父类的原型对象,所有的实例都会改变

综上:所有子类实例都会共享父类的属性

2. 构造函数继承

为了解决原型链继承中,子类实例共享父类属性的问题
**方法:**在子类的构造函数中,使用apply/call来调用父类构造函数,改变对象执行的上下文

function Parent() {
  this.colors = [‘red’, ‘blue’, ‘green’]
}
function Child() {
  // 继承Parent
  Parent.call(this) 
}
var instance1 = new Child() //创建child对象实例时,就会进行初始化
var instance2 = new Child()
instance1.colors.push(‘black’)
console.log(instance1.colors)  // [“red”, “blue”, “green”, “black”]
console.log(instance2.colors) // [“red”, “blue”, “green”]

可以看出每个对象实例都会有自己的属性副本


拓展:传递参数

function Parent(name) {
  this.name = name
}
function Child() {
  // 继承Parent
  Parent.call(this, ‘Jiang’)
  
  this.job = ‘student’
}
var instance = new Child()
console.log(instance.name)  // Jiang
console.log(instance.job)   // student


存在的问题
方法都在构造函数中定义,函数无法达到复用

3. 组合继承(原型链+构造函数)

方法

  1. 使用原型链实现对原型属性和方法的继承
  2. 借用构造函数实现对实例属性的继承
function Parent(name) {  //定义父类构造函数
  this.name = name
  this.colors = [‘red’, ‘blue’, ‘green’]
}
Parent.prototype.sayName = function () {  //父类原型方法
  console.log(this.name)
}
// *1
function Child(name, job) {  //继承父类
  // 继承属性
  Parent.call(this, name)
  
  this.job = job
}
// *2
// 继承方法
Child.prototype = new Parent()//将父类的实例赋给子类的原型对象
Child.prototype.constructor = Parent

Child.prototype.sayJob = function() { //子类的原型方法
  console.log(this.job)
}
var instance1 = new Child(‘Jiang’, ‘student’)
instance1.colors.push(‘black’)
console.log(instance1.colors) //[“red”, “blue”, “green”, “black”]
instance1.sayName() // ‘Jiang’
instance1.sayJob()  // ‘student’

var instance2 = new Child(J, ‘doctor’)
console.log(instance2.colors) // //[“red”, “blue”, “green”]
instance2.sayName()  // ‘J’
instance2.sayJob()  // ‘doctor’

4. 原型式继承

方法:借助原型基于已有对象创建新对象

function object(o) {
  function F() {} //1. 创建新临时构造函数
  F.prototype = o  //2. 将传进来的对象作为这个构造函数的原型对象
  return new F() //3. 返回这个对象的实例对象
}

再次声明:该函数返回一个实例对象,而这个对象是以传进来的对象为原型对象的实例对象
从本质上来说:该函数对传进来的对象执行了一次浅复制

var person = {
  name: ‘Jiang’,
  friends: [‘Shelby’, ‘Court’]
}
var anotherPerson = object(person)//创建anotehPerson为一个实例对象且他的原型对象为person
console.log(anotherPerson.friends)  // [‘Shelby’, ‘Court’]

如下所示:可以看出在这个函数中临时创建了一个构造函数,但是最终返回的是这个函数的实例对象
在这里插入图片描述
这种方法必须要求有一个对象作为另一个对象的基础

  1. person作为原型对象
  2. anotherPerson作为实例对象

拓展
Object.create()方法,来自ES5,规范了原型式继承

  • 参数一:一个对象(作为新对象的原型对象)
  • 参数二:对象(定义额外属性)可选
var person = {
  name: ‘Jiang’,
  friends: [‘Shelby’, ‘Court’]
}
var anotherPerson = Object.create(person)
console.log(anotherPerson.friends)  // [‘Shelby’, ‘Court’]

5. 寄生式继承

寄生继承的思路和工厂模式很像(下边会介绍创建对象的模式)
即:创建一个用于封装继承过程的函数

function createAnother(o) {
  var clone = Object.create(o) // 创建一个新对象实例
  clone.sayHi = function() { // 给这个实例对象添加方法
    console.log(‘hi’)
  }
  return clone  // 返回这个对象
}
var person = {
  name: ‘Jiang’
}
var anotherPeson = createAnother(person)
anotherPeson.sayHi()

由上面代码可以看出,这个新对象不仅有person的属性和方法,还拥有自己的方法

6. 寄生组合继承

回顾一下组合模式(原型链+构造函数):通过借用构造函数来继承属性,通过原型链来继承方法

function Parent(name) { //父类构建
  this.name = name
  this.colors = [‘red’, ‘blue’, ‘green’]
}
function Child(name, job) {  
  // 继承属性
  Parent.call(this, name)  // 1. 调用父类构造函数
  
  this.job = job
}
// 继承方法(将子类的原型指向父类的实例)
Child.prototype = new Parent()  // 2.调用父类构造函数
var instance = new Child()
//当创建子类实例时,其实会产生两组name和color属性,一组在实例上,一组在child原型上,只不过实例屏蔽了原型上的

所以,寄生模式就是为了规避这种问题
方法:使用寄生模式来继承父类的原型,将结果指定给子类的原型
(指定子类原型时,不再去调用父类的构造函数;我们要的无非是父类原型的一个副本)

function inheritPrototype(Child, Parent) {
  var prototype = Object.create(Parent.prototype)//创建一个父类原型的副本
  prototype.constructor = Child //给子类添加constructor属性
  Child.prototype = prototype  //让子类的原型指向这个副本
}

这个函数的作用就是将子类的原型通过这个原型副本和父类原型链接起来了
在这里插入图片描述

function Parent(name) {
  this.name = name
  this.colors = [‘red’, ‘blue’, ‘green’]
}
Parent.prototype.sayName = function () {
  console.log(this.name)
}
function Child(name, job) {
  // 继承属性
  Parent.call(this, name)
  
  this.job = job
}
// 继承
inheritPrototype(Child, Parent)//将子类的原型和父类的原型链接起来
var instance = new Child(‘Jiang’, ‘student’)
instance.sayName()


补充:
可以直接使用Object.create来实现,其实就是将上面封装的函数拆开,这样演示可以更容易理解

function Parent(name) {
  this.name = name
  this.colors = [‘red’, ‘blue’, ‘green’]
}
Parent.prototype.sayName = function () {
  console.log(this.name)
}
function Child(name, job) {
  // 继承属性
  Parent.call(this, name)
  
  this.job = job
}
// 继承
Child.prototype = Object.create(Parent.prototype)  //将子类原型和父类原型链接起来
// 修复constructor
Child.prototype.constructor = Child//可以理解为这一步为child原型和child构造函数创建关联

var instance = new Child(‘Jiang’, ‘student’)
instance.sayName()

ES6新增方法,可以直接创建关联,而不用手动添加constructor属性

// 继承
Object.setPrototypeOf(Child.prototype, Parent.prototype)
console.log(Child.prototype.constructor === Child) // true

参考文献:https://xxxgitone.github.io/2017/06/12/JavaScript六种继承方式/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 JavaScript 中,继承是一个非常常见的概念。ES6 引入了 Class 语法糖,让继承更加易于理解和实现。在 Class 中,我们可以使用 extends 关键字来创建一个子类,使其继承父类的属性和方法。 下面我们来详细了解一下如何在 JavaScript 中使用 extends 实现继承。 ### 基础语法 首先,我们需要定义一个父类。在 ES6 中,我们可以使用 Class 来定义一个类。例如: ```javascript class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } ``` 这个 Animal 类有一个构造函数和一个 speak 方法。构造函数会在创建实例时被调用,而 speak 方法则可以让动物发出一些声音。 接下来,我们来创建一个子类。使用 extends 关键字来创建子类,并使用 super() 方法调用父类的构造函数。例如: ```javascript class Dog extends Animal { constructor(name) { super(name); } speak() { console.log(this.name + ' barks.'); } } ``` 这个 Dog 类继承了 Animal 类,并覆盖了其 speak 方法。在构造函数中,我们通过 super() 方法来调用父类的构造函数,并将传递的参数传递给它。 现在,我们可以创建一个 Dog 的实例,并调用其 speak 方法: ```javascript let d = new Dog('Mitzie'); d.speak(); // Mitzie barks. ``` ### 继承父类的方法 当一个子类继承了一个父类时,它会继承父类的属性和方法。例如,在上面的例子中,Dog 类继承了 Animal 类,因此它继承了 Animal 类的 speak 方法。 当我们调用子类的方法时,如果子类没有实现该方法,它会自动调用父类的方法。例如,在上面的例子中,如果我们不覆盖 Dog 类的 speak 方法,它将调用 Animal 类的 speak 方法。 ### 覆盖父类的方法 如果一个子类需要覆盖父类的方法,我们可以在子类中重新定义该方法。例如,在 Dog 类中,我们覆盖了 Animal 类的 speak 方法,使其输出“barks”而不是“makes a noise”。 ### 调用父类的方法 有时候,我们需要在子类中调用父类的方法。我们可以使用 super 关键字来调用父类的方法。例如,在 Dog 类中,我们可以通过调用 super.speak() 来调用 Animal 类的 speak 方法。 ### 总结 继承是一个非常常见的概念,也是面向对象编程中的重要概念之一。在 JavaScript 中,我们可以使用 extends 关键字来实现继承。通过继承,子类可以继承父类的属性和方法,也可以覆盖父类的方法,并且可以调用父类的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值