Javascript原型、原型链、继承

对象属性 __proto__ prototype
普通对象 ×
函数对象
    function Person(name,age){
        this.name = name;
        this.age = age;
        Person.prototype.say = function(){
            console.log(`my name is ${this.name} and ${this.age} years`)
        }
    }

    let p1 = new Person('pfzz',25) 

p1是普通对象
Person则是函数对象

// prototype是函数对象的属性,其本质是 一个对象,包含了__proto__与constructor,
// 只看普通对象p1,p1只有__proto__,并且指向创建它的函数对象的prototype
    console.info(p1)
    console.info(p1.__proto__ === Person.prototype)
    console.info(p1.__proto__.constructor === Person)
    console.info(p1.__proto__.__proto__ === Object.prototype)
    console.info(p1.__proto__.__proto__.constructor === Object)
    console.info(p1.__proto__.__proto__.__proto__ === null)



    // 函数对象Person,有__proto__、prototype属性
    console.info(Person.__proto__ === Function.prototype)
    console.info(Person.__proto__.constructor === Function)
    console.info(Person.__proto__.__proto__ === Object.prototype)
    console.info(Person.__proto__.__proto__.constructor === Object)
    console.info(Person.__proto__.__proto__.__proto__ === null)

    console.info(Person.prototype.constructor === Person)
    console.info(Person.prototype.__proto__ === Object.prototype)
    console.info(Person.prototype.__proto__.constructor === Object)
    console.info(Person.prototype.__proto__.__proto__ === null)
    
    // 综上具有在指向的只有__proto__与constructor属性,
    // 而constructor属性总是指向prototype所在的函数对象,形成闭环。
    // __proto__最终总是指向null

由上可知javasript的继承就要靠__proto__来实现

// 1、类式继承
    function Child1(name,age,sex){
        this.sex = sex;
        const prototype = new Person(name,age);
        prototype.play = function(){
            console.log(`my name is ${this.name} and ${this.age} years and ${this.sex}`)
        };
        this.__proto__ = prototype
    }
    const kind1 = new Child1('pfzzz',25,'man')
    kind1.say()
    console.info(kind1)
    console.info(kind1.__proto__ !== Child1.prototype)
    缺点:继承属性被放在原型链上,导致kind1.__proto__ !== Child1.prototype
    // 2、构造函数式继承
    function Child2(name,age,sex){
        this.sex = sex;
        Person.call(this,name,age)
    }

    const kind2 = new Child2('pfzzz',25,'man')
    console.info(kind2)
    kind2.say() // 报错
    缺点:原型链上的方法没有被继承
 // 3、组合式继承
    function Child3(name,age,sex){
        this.sex = sex;
        Person.call(this,name,age)  // 第一次
    };
    Child3.prototype = new Person() // 第二次
     
    const kind3 = new Child3('pfzzz',25,'man')
    console.info(kind3)
    kind3.say()
    缺点:子类无法传递动态参数给父类,且父类构造函数被调用两次,问题不大。
// 4、野路子继承
    function inherit(child, parent) {
        // 将父类原型和子类原型合并,并赋值给子类的原型
        child.prototype = Object.assign(Object.create(parent.prototype), child.prototype)
        // 重写被污染的子类的constructor
        p.constructor = child
    }
//  缺点:Object.assign并不是最好的方法    
//5、利用es5的Reflect来优化
    function fancyShadowMerge(target, source) {
        for (const key of Reflect.ownKeys(source)) {
            Reflect.defineProperty(target, key, Reflect.getOwnPropertyDescriptor(source, key))
        }
        return target
    }

    // Core
    function inherit(child, parent) {
        const objectPrototype = Object.prototype
        // 继承父类的原型
        const parentPrototype = Object.create(parent.prototype)
        let childPrototype = child.prototype
        // 若子类没有继承任何类,直接合并子类原型和父类原型上的所有方法
        // 包含可枚举/不可枚举的方法
        if (Reflect.getPrototypeOf(childPrototype) === objectPrototype) {
            child.prototype = fancyShadowMerge(parentPrototype, childPrototype)
        } else {
            // 若子类已经继承子某个类
            // 父类的原型将在子类原型链的尽头补全
            while (Reflect.getPrototypeOf(childPrototype) !== objectPrototype) {
            childPrototype = Reflect.getPrototypeOf(childPrototype)
            }
        Reflect.setPrototypeOf(childPrototype, parent.prototype)
        }
        // 重写被污染的子类的constructor
        parentPrototype.constructor = child
    }

其他还有寄生式继承,寄生组合式继承,但都有一些副作用。
这些都是es5的继承,现在有了es6的 class,相关继承就不需要在过多考虑了,这些属于拓展知识,知道最好不知道也不影响代码书写。

你可能感兴趣的:(javascript,前端,es6)