prototype、__proto__、constructor

隐式原型__proto__

除了元对象Object,每个js对象都有隐式原型,但也可以通过Object.create(null, {})强制创建一个没有__proto__的对象,__proto__指向该对象的构造函数的原型对象。原型链指的就是__proto__串起来的链。

原型prototype

函数对象拥有的属性,函数的原型对象包含所有实例共享的方法,包含__proto__和constructor两个属性,constructor指回函数本身,函数原型对象也有__proto__属性,指向构造函数的原型即Function.prototype。
实例的__proto__指向函数原型对象,即f1.__proto__ === Foo.prototype
Object.prototype.__proto__指向null。

总结

新建函数时自动创建prototype原型对象,原型对象包含constructor构造函数,构造函数指回函数本身,new出来的实例__proto__指向prototype,注意this永远指向当前对象。

类式继承

将父类的实例赋给子类的prototype,缺点是父类共有属性为引用对象时,所有子类实例会公用同一个对象;无法向父类传参;子类不是父类的实例,而子类的原型是父类的实例。

function Animal() {
    this.name = '动物'
    this.animal = '父类属性'
}
Animal.prototype.getname = function () {
    console.log(this.name)
}
Animal.prototype.getsupername = function () {
    console.log(this.animal)
}
function Dog() {
    this.name = '狗'
}
Dog.prototype = new Animal()
dog = new Dog()
dog.getname()
dog.getsupername()
console.log(dog)

构造函数继承

将子类的变量在父类中执行一遍,即在子类的构造函数中执行一次父类的构造函数,缺点只执行了父类的构造函数,没有继承父类原型。

function Animal(name) {
    this.name = name
    this.animal = '父类属性'
}
Animal.prototype.getname = function () {
    console.log(this.name)
}
Animal.prototype.getsupername = function () {
    console.log(this.animal)
}
function Dog(name) {
    Animal.call(this, name)
}
dog = new Dog('狗')
// dog.getname()
// dog.getsupername()
console.log(dog)

组合继承

在子类构造函数中执行父类构造函数,子类原型指向父类的实例,缺点是父类构造函数执行了两遍,子类不是父类的实例。

function Animal(name) {
    this.name = name
    this.animal = '父类属性'
    console.log('执行了父类构造函数')
}
Animal.prototype.getname = function () {
    console.log(this.name)
}
Animal.prototype.getsupername = function () {
    console.log(this.animal)
}
function Dog(name) {
    Animal.call(this, name)
}
Dog.prototype = new Animal()
dog = new Dog('狗')
dog.getname()
dog.getsupername()
console.log(dog)

原型式继承

类式继承的一个封装,创建一个新对象并使用父类对象做为新对象的原型对象,和类式继承存在一样的问题。和Object.create()方法作用一致。

function inherit(o) {
    function F() { }
    F.prototype = o
    return new F()
}
let Animal = {
    name: '动物'
}
animal = inherit(Animal)
console.log(animal)

寄生式继承

对原型继承的二次封装,对继承的对象进行了拓展。

function inherit(o) {
    function F() { }
    F.prototype = o
    return new F()
}
let Animal = {
    name: '动物'
}
function createDog(obj) {
    let o = new inherit(obj)
    o.getname = function () {
        console.log(this.name)
    }
    return o
}
dog = new createDog(Animal)
dog.getname()
console.log(dog)

寄生组合式继承

寄生继承和构造函数继承的组合,修复子类的原型,子类的原型指向父类原型的一份实例,construction指向子类本身。

function inherit(o) {
    function F() { }
    F.prototype = o
    return new F()
}
function inheritPtototype(subClass, superClass) {
    let p = inherit(superClass.prototype)
    p.construction = subClass
    subClass.prototype = p
}
function Animal(name) {
    this.name = name
    this.animal = '父类属性'
    console.log('执行了父类构造函数')
}
Animal.prototype.getname = function () {
    console.log(this.name)
}
Animal.prototype.getsupername = function () {
    console.log(this.animal)
}
function Dog(name) {
    Animal.call(this, name) //构造函数式继承
}
inheritPtototype(Dog, Animal) //寄生式继承
dog = new Dog('狗')
dog.getname()
dog.getsupername()
console.log(dog)

但是上面左还有一个小问题,就是遍历子类实例属性时,会把construction也遍历出来,可以通过Object.defineProperty方法禁止遍历。

for (const key in dog) {
    console.log(key);
}

所以再升级一下

function extend(subClass, superClass) {
    subClass.prototype = Object.create(superClass.prototype)
    Object.defineProperty(subClass.prototype, "constructor", {
        value: subClass,
        enumerable: false //禁止遍历
    });
}
function Animal(name) {
    this.name = name
    this.animal = '父类属性'
    console.log('执行了父类构造函数')
}
Animal.prototype.getname = function () {
    console.log(this.name)
}
Animal.prototype.getsupername = function () {
    console.log(this.animal)
}
function Dog(name) {
    Animal.call(this, name) //构造函数式继承
}
extend(Dog, Animal) //寄生式继承
dog = new Dog('狗')
dog.getname()
dog.getsupername()

mixin多继承

const Mammal = {
    skill() {
        console.log(this.name + '是哺乳动物')
    }
} // minin类
function Dog(name) {
    Animal.call(this, name) //构造函数式继承
}
extend(Dog, Animal) //寄生式继承
Object.assign(Dog.prototype, Mammal) //往子类原型拓展其他类方法
dog = new Dog('狗')
dog.getname()
dog.getsupername()
dog.skill()

参考

帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
JavaScript中的new操作符的原理解析
js中__proto__和prototype的区别和关系?
原型基础

最后修改:2021 年 04 月 15 日 03 : 29 PM