JavaScript常见的六种继承方式

方式一、原型链继承

子类型的原型为父类型的一个实例对象

//父类型
function Person(name,age){
    this.name = name,
    this.age = age,
    this.play = ['学习','追剧','做饭'],
    this.setName = function(){

    }
}
Person.prototype.setAge = function(){

}
// 子类型
function Student(price){
    this.price = price,
    this.setScore = function(){

    }
}
// Student.prototype.sayHello = function () { }//在这里写子类的原型方法和属性是无效的,
//因为会改变原型的指向,所以应该放到重新指定之后
Student.prototype = new Person();//子类型的原型为夫类型的一个实例对象
// Student.prototype.sayHello = function () { }
var s1 = new Student(15000)
var s2 = new Student(14000)
s1.play.push('电风扇')
s1.name = 'wj'

// s1中play属性发生变化,与此同时,s2中play属性也会跟着变化
console.log(s1,s2,s1.play,s2.play,s1.name)

qSDOjx.png

方式二: 借用构造函数继承

在子类型构造函数中通用call()调用父类型构造函数


//父类型
function Person(name,age){
    this.name = name,
    this.age = age,
    this.play = ['学习','追剧','做饭'],
    this.setName = function(){}
}

// 子类型
Person.prototype.setAge = function(){}
function Student(name,age,price){
    Person.call(this,name,age);//相当于 this.Person(name,age)
//  this.name = name
//  this.age = age
this.price = price
}
 var s1  = new Student('Tom',20,15000)
 console.log('s1',s1)
 console.log(s1.setAge())

 //Uncaught TypeError: s1.setAge is not a function

注:只能继承父类的属性和方法,不能继承父类原型的熟悉和方法

qS40u4.png

方式三: 原型链+借用构造函数的组合继承

通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。


function Person(name, age) {
    this.name = name,
    this.age = age,
    this.setAge = function () {console.log('1111') }
}
Person.prototype.setBB = function () {
    console.log("111")
}
function Student(name, age, price) {
    Person.call(this,name,age)
    this.price = price
    this.setScore = function () { }
}
Student.prototype = new Person()
Student.prototype.constructor = Student//组合继承也是需要修复构造函数指向的
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
var s2 = new Student('Jack', 22, 14000)
var p1 = new Person('wj',11)

console.log(s1,p1)
console.log(s1.constructor) //Student
console.log(p1.constructor) //Person

qS44DH.png

方式四: 组合继承优化1

通过父类原型和子类原型指向同一对象,子类可以继承到父类的公有方法当做自己的公有方法,而且不会初始化两次实例方法/属性,避免的组合继承的缺点。


function Person(name, age) {
    this.name = name,
    this.age = age,
    this.setAge = function () { }
}
Person.prototype.setBB = function () {
    console.log("111")
}
function Student(name, age, price) {
    Person.call(this, name, age)
    this.price = price
    this.setScore = function () { }
}
Student.prototype = Person.prototype
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
console.log(s1)

// 没办法辨别是对象是子类还是父类实例化

qS5EMF.png

方式五: 组合继承优化2

借助原型可以基于已有的对象来创建对象,var B = Object.create(A)以A对象为原型,生成了B对象。B继承了A的所有属性和方法。

function Person(name, age) {
    this.name = name,
    this.age = age
    this.setPaly = function(){}
}
Person.prototype.setAge = function () {
    console.log("111")
}
function Student(name, age, price) {
    Person.call(this, name, age)
    this.price = price
    this.setScore = function () {}
}
Student.prototype = Object.create(Person.prototype)//核心代码
Student.prototype.constructor = Student//核心代码
var s1 = new Student('Tom', 20, 15000)
console.log(s1 instanceof Student, s1 instanceof Person) // true true
console.log(s1.constructor) //Student
console.log(s1)

// 目前来说,最完美的继承方法!
注:Student继承了所有的Person原型对象的属性和方法。目前来说,最完美的继承方法!

qS5tZd.png

方式六:ES6中class 的继承

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

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

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


// S6中class 的继承

class Person {
    //调用类的构造方法
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    //定义一般的方法
    showName1() {
        console.log("调用父类的方法")
        console.log(this.name, this.age);
    }
}
let p1 = new Person('kobe', 39)
console.log(p1)
//定义一个子类
class Student extends Person {
    constructor(name, age, salary) {
        super(name, age)//通过super调用父类的构造方法
        this.salary = salary
    }
    showName2() {//在子类自身定义方法
        console.log("调用子类的方法")
        console.log(this.name, this.age, this.salary);
    }
}
let s1 = new Student('wade', 38, 1000000000)
console.log(s1)
s1.showName2()

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

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

qS5dit.png

posted @ 2022-03-16 17:20  会吃鱼的猫123  阅读(207)  评论(0编辑  收藏  举报