JS中的继承

JS中的继承

ECMA-262把原型链定义为ECMAScript的主要继承方式。其基本思想就是通过原型继承多个引用类型的属性和方法,

每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型,如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另外一个原型,相应的另一个原型也有一个指针指向另一个构造函数,这样在实例和原型之间构造了一条原型链,这就是原型链的基本思想。

实现原型链的代码模式

function Person(){}

Person.prototype.name = '张三'
Person.prototype.age = 20
Person.prototype.getName = function(){
    return this.name
}

function Student(){}

//继承Person
Student.prototype = new Person()

Student.prototype.grade = '三年级'

let stu = new Student()

console.log(stu.getName())//张三

上面例子实现继承的关键是 Student没有使用默认的原型,而是将其替换成了一个新的对象,这个对象恰好是Person的实例,这样一来,Student的实例不仅能从Person的实例中继承属性和方法,而且与Student的原型挂钩。于是stu通过内部的[[prototype]]__proto__)指向Student.prototypeStudent.prototype又通过内部的[[prototype]]__proto__)指向Person.prototype

在读取实例属性时,首先会在实例上搜索这个属性,如果没找到,则会继续搜索实例原型。在通过原型链实现继承之后,搜索就可以继续向上搜索原型的原型。

默认原型

默认情况下,所有引用类型都继承自Object,这也是通过原型链实现的,任何函数的默认原型都是一个Object的实例,这意味着这个实例有一个内部指针指向Object.prototype。这也是为什么自定义类型能够使用包括toString()valueOf在内的所有默认方法

原型与实例关系

原型与实例的关系可以通过两种方式来确定
  • instanceof

    console.log(stu instanceof Person)// true;
    
    console.log(stu instanceof Student) // true;
    
    console.log(stu instanceof Object) // true;

    从技术上将,stu是 Student、Person、和Object的实例,因为stu的原型链中包含这些构造函数的原型,结果就是 instanceof 对所有这些构造函数都返回 true

  • isPrototypeOf

    console.log(Object.prototype.isPrototypeOf(stu))// true;
    
    console.log(Person.prototype.isPrototypeOf(stu)) // true;
    
    console.log(Student.prototype.isPrototypeOf(stu)) // true;

    只要原型链上包含这个原型、这个方法就会返回 true

关于方法

子类有时候需要覆盖父类的方法,或者增加父类中没有的方法。为此,这些方法必须在原型赋值之后再添加到原型上

function Person(){}

Person.prototype.name = '张三'
Person.prototype.age = 20
Person.prototype.getName = function(){
    return this.name
}

function Student(){}

//继承Person
Student.prototype = new Person()

Student.prototype.grade = '三年级'

//新方法
Student.prototype.getGrade = function(){
    return this.grade
}

//覆盖已有方法
Student.prototype.getName = function(){
    return `${this.name}--${this.grade}`
}

let stu = new Student()

console.log(stu.getName())

原型链失效

以对象字面量的方式创建原型方法会破坏之前的原型链,因为这相当于重写了原型

function Person(){}

Person.prototype.name = '张三'
Person.prototype.age = 20
Person.prototype.getName = function(){
    return this.name
}

function Student(){}

//继承Person
Student.prototype = new Person()

//以对象字面量的形式添加方法,会导致上一行代码无效
Student.prototype = {
    grade:'三年级',
    getGrade(){
        return this.grade
    }
}

let stu = new Student()

console.log(stu.getName())

页面报错,找不到getName方法

QQ20220302-212737![]()