Skip to content

原型和继承

原型

js
function People(name) {
    this.name = name;
}
People.prototype.say = function(){ console.log(this.name) }
const xiaoming = new People('xiaoming');

以上代码完成了这些事

  1. 创建一个构造函数(People),并同时创建一个原型对象(People.prototype
    • 构造函数的 _proto_ 指向原型对象,原型对象的 constructor 指向构造函数
  2. 创建一个实例(xiaoming),实例的 _proto_ 会指向原型对象

原型链继承

  • 通过原型继承多个引用类型的属性和方法,构建原型链,搜索属性就可以通过原型链一直向上搜索

  • 所有引用类型的继承都是 Object

js
function Super() {}
Super.prototype.getName = function() {
    return 'Super'
}

function Sub() {}
Sub.prototype = new Super()
Sub.prototype.constructor = Sub
Sub.prototype.getName = function() {
    return 'Sub'
}

const sub = new Sub()
sub.getName() // 'Sub'

以上代码构成下列原型链

默认原型链继承有两个问题

  1. 原型中包含的引用值会在所有实例上共享

    js
    function Super() {
        this.colors = ['red', 'blue']
    }
    
    function Sub(){}
    Sub.prototype = new Super()
    
    const sub1 = new Sub()
    sub1.colors.push('yellow')
    console.log(sub1.colors) // ['red', 'blue', 'yellow']
    
    const sub2 = new Sub()
    console.log(sub2.colors) // ['red', 'blue', 'yellow']
  2. 子类型在实例化时不能给父类型的构造函数传参

组合继承

  • 解决原型链继承的两个问题
js
function Super(name) {
    this.name = name
    this.colors = ['red', 'blue']
}
Super.prototype.sayName = function() {
    console.log(this.name)
}

function Sub(name, age){
    Super.call(this, name) // 重点代码 !!!!!
    this.age = age
}
Sub.prototype = new Super()
Sub.prototype.constructor = Sub

const sub = new Sub('kingmusi', 22)
sub.sayName() // 'kingmusi'

// 也解决了共享引用值的问题
sub.push('yellow')
console.log(sub.colors) // ['red', 'blue', 'yellow']

const otherSub = new Sub('kingmusi', 22)
console.log(otherSub.colors) // ['red', 'blue']

从以下两行代码可以看到,Super() 执行了两次,有以下问题

  1. 产生了性能上的开销
  2. 在 Sub 上和 Sub.prototype 上产生了同样的变量,在实例上删除某个变量时,只会删除 Sub 层次上的,Sub.prototype 上的仍不能被删除
js
Super.call(this, name)
Sub.prototype = new Super()

寄生式组合继承(最佳模式)

  • 基本模式
js
function inheritPrototype(sub, super) {
    sub.prototype = Object.create(super.prototype)
    sub.prototype.constructor = sub
}
  • 使用
js
function Super(name) {
    this.name = name
    this.colors = ['red', 'blue']
}
Super.prototype.sayName = function() {
    console.log(this.name)
}

function Sub(name, age){
    Super.call(this, name) // 重点代码 !!!!!
    this.age = age
}

inheritPrototype(Sub, Super)

判断原型和继承的关系

  1. instanceof:只要原型链中包含此原型,则返回 true
  2. isPrototypeOf():只要原型链中包含此原型,则返回 true
js
function Super() {}

function Sub() {}
Sub.prototype = new Super()
Sub.prototype.constructor = Sub

const sub = new Sub()

console.log(sub instanceof Sub) // true
console.log(sub instanceof Super) // true
console.log(sub instanceof Object) // true

console.log(Sub.prototype.isPrototypeOf(sub)) // true
console.log(Super.prototype.isPrototypeOf(sub)) // true
console.log(Object.prototype.isPrototypeOf(sub)) // true

constructor 是不可靠的

js
function Super() {}

function Sub() {}
Sub.prototype = new Super()

const sub = new Sub()

console.log(sub.constructor === Sub) // false
console.log(sub.constructor === Super) // true

console.log(sub instanceof Sub) // true