前端小白入门 js 常见继承 |掘金·日新计划

69 阅读8分钟

一、预备知识

1.构造函数的属性

function A(name){
	this.name = name;	//实例基本属性(该属性强调私有 不共享)
	this.arr = [1];	//实例引用属性(该属性强调私有 不共享)
	this.say = function() {	//实例引用属性 (该属性强调复用  需要共享)
		console.log('helloworld');
	}
}

.什么是原型对象

简单来说,每个函数都有prototype属性,它就是原型对象,通过函数实例化出来的对象有个__proto__属性,指向原型对象。

let a = new A()
a.__proto__ == A.prototype

//prototype的结构如下
A.prototype = {
	constructor: A,
	...其他的原型属性和方法
}

继承是什么?

原型链

面向对象的三大特征:封装、继承、多态。作用就是判断代码是否是面向对象的思维方式。面向对象的思维方式:注重结果。

继承就是指一个子类继承父类的属性和方法,使得子类对象(实例化对象)具有父类的方法和属性。

这里要明白JavaScript是通过原型链实现继承的。

继承常见的六种方式:原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承。

4DF6D0F1DD88A00C5E9E8F6F69D33E8D.jpg

JavaScript的继承是一种实现对象之间共享属性和方法的方式,它使用原型链来实现。

在JavaScript中,每个对象都有一个原型(prototype),原型是一个对象,它包含了要继承的属性和方法。当我们访问对象的属性或方法时,如果对象本身没有,则会去它的原型上查找,如果原型上还没有,则会继续向上层的原型链查找,直到找到或者到达原型链的顶层。

通过原型链,子对象可以继承父对象的属性和方法,这样可以实现代码的复用和共享。

需要注意的是,在实现继承时,可以使用构造函数、原型链组合使用的方式,或者使用ES6中的类和继承关键字等方式。这些方式都可以用来创建具有继承关系的对象。

原型链继承

这种方式关键在于:子类型的原型为父类型的一个实例对象。

外注意一点的是,我们需要在子类中添加新的方法或者是重写父类的方法时候,切记一定要放到替换原型的语句之后

1.png

特点

  • 父类新增原型方法/原型属性,子类都能访问到
  • 简单,易于实现

缺点

  • 无法实现多继承
  • 来自原型对象的所有属性被所有实例共享
  • 创建子类实例时,无法向父类构造函数传参
  • 要想为子类新增属性和方法,必须要在Student.prototype = new Person() 之后执行,不能放到构造器中

借用构造函数继承

借用构造函数继承(Constructor Borrowing Inheritance)是一种实现对象之间继承关系的方法。在这种方式中,子对象通过调用父对象的构造函数,并使用 call 或 apply 方法将父对象的属性和方法应用到子对象上。

通过借用构造函数继承,子对象可以继承父对象的属性,并且每个子对象都会拥有父对象属性的独立副本。这种继承方式可以避免子对象之间共享属性时可能出现的问题。

// 借用构造函数 继承  属性的继承

function Persion(name, age) {

  this.type = '搬砖'

  this.name = '昌平小白龙'

  this.age = 18

}

Persion.prototype.sayName = function () {

  console.log(123)

}

// bind apply call

function Student(name, age) {

  Persion.call(this, name, age)

  this.job = '赚钱'

}

  


let s1 = new Student('大聪明', 20)

  
  


console.log(s1.name, s1.age, s1.type, s1.job)

  


// console.log(s1.sayName())

优点:

  • 解决了子类构造函数向父类构造函数中传递参数
  • 可以实现多继承(call或者apply多个父类)

缺点:

  • 方法都在构造函数中定义,无法复用
  • 不能继承原型属性/方法,只能继承父类的实例属性和方法

2.png

实例继承(原型式继承)

实例继承,又被称为原型式继承(Prototype-based inheritance),是一种通过直接使用已有对象作为新对象的原型来实现继承的方法。

在 JavaScript 中,每个对象都有一个特殊的属性称为原型(prototype),它指向另一个对象。在创建一个新对象时,可以通过指定原型来实现继承。新对象将从原型对象继承属性和方法。

3.png

优点:

  • 解决了子类构造函数向父类构造函数中传递参数
  • 可以实现多继承(call或者apply多个父类)

缺点:

  • 方法都在构造函数中定义,无法复用
  • 不能继承原型属性/方法,只能继承父类的实例属性和方法

组合式继承 (组合原型链继承和借用构造函数继承)【常用】 组合继承(combination inheritence)

有时候也叫做伪经典继承
指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式
思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性

原文链接:blog.csdn.net/qq_42496307…

4.png

优点:

  • 函数可以复用
  • 每个新实例引入的构造函数属性是私有的。
  • 可以继承父类原型上的属性,可以传参,可复用。

缺点:

  • 调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。

寄生组合继承【常用】

  • 实质:寄生在原型式继承上的继承

  • 实现:在原型式继承的基础上创建了一个原实例对象之后,为新创建的原型式实例对象加入一个属性和方法,然后再返回这个实例对象,这个就是寄生在原型式继承上的寄生式继承 5.png 寄生:

  • 在函数内返回对象然后调用
    组合:

  • 函数的原型等于另一个实例。

  • 在函数中用apply或者call引入另一个构造函数,可传参

**ES6中class 的继承**
//class中定义方法时,前后不能加function,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式
//es5中constructor为隐式属性
class People{
  constructor(name='wang',age='27'){
    this.name = name;
    this.age = age;
  }
  eat(){
    console.log(`${this.name} ${this.age} eat food`)
  }
}
//继承父类
class Woman extends People{ 
   constructor(name = 'ren',age = '27'){ 
     //继承父类属性
     super(name, age); 
   } 
    eat(){ 
     //继承父类方法
      super.eat() 
    } 
} 
let wonmanObj=new Woman('xiaoxiami'); 
wonmanObj.eat();



ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。

优点:语法简单易懂,操作更方便

缺点:并不是所有的浏览器都支持class关键字

继承图解

b4e49c681fcb6e1586e28a326084041.png

** ES5继承和ES6继承的区别:

es5继承首先是在子类中创建自己的this指向,最后将方法添加到this中 Child.prototype=new Parent() || Parent.apply(this) || Parent.call(this) es6继承是使用关键字先创建父类的实例对象this,最后在子类class中修改this 最后总结:

继承这些知识点与其说是对象的继承,更像是函数的功能用法,如何用函数做到复用,组合,这些和使用继承的思考是一样的。上述几个继承的方法都可以手动修复他们的缺点,但就是多了这个手动修复就变成了另一种继承模式。       这些继承模式的学习重点是学它们的思想,不然你会在coding书本上的例子的时候,会觉得明明可以直接继承为什么还要搞这么麻烦。就像原型式继承它用函数复制了内部对象的一个副本,这样不仅可以继承内部对象的属性,还能把函数(对象,来源内部对象的返回)随意调用,给它们添加属性,改个参数就可以改变原型对象,而这些新增的属性也不会相互影响。 **

好了今天的知识就分享到这里了 望大家多多支持点点赞 小编不易

AD1989B23755CC12C485F1C0418A9364.jpg

参考文章

  • JavaScript常见的继承方式

  • 阮一峰ES6入门之class的继承

二维码推广.jpg

求关注 交流分享