javascript面向对象编程学习(四) —— 原型式、寄生式和寄生组合式继承

182 阅读2分钟

导读

javascript面向对象编程学习(一)
javascript面向对象编程学习(二) —— 原型链继承
javascript面向对象编程学习(三) —— 构造函数继承和组合继承

一、原型式继承

1. 如何实现原型式继承?

把某一个对象直接赋值给构造函数的原型,或者使用Object.create()方法。

2.代码实现

2.1 使用构造函数原型实现

function CreateObject(obj){
     function O() {};
     O.prototype = obj;
     return new O();
}
   
const person = {
   name: '大魔王',
   names: ['小魔王', '魔鬼']
}
   
const child = CreateObject(person);
console.log(child.name);   // 大魔王
child.name = '小魔王' 
child.names.push('魔王')
console.log(child.name);   // 小魔王
console.log(child.names);  // ["小魔王", "魔鬼", "魔王"]
   
const child2 = CreateObject(person);
child2.names.push('人类');
console.log(person.names);  // ["小魔王", "魔鬼", "魔王", "人类"]

2.2 使用Object.create()实现。Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

const person = {
     name: '大魔王',
     names: ['小魔王', '魔鬼']
}
   
const child = Object.create(person);
console.log(child.name);   // 大魔王
child.name = '小魔王' ;
child.names.push('魔王');
console.log(child.name);   // 小魔王
console.log(child.names);  // ["小魔王", "魔鬼", "魔王"]
   
const child2 = Object.create(person);
child2.names.push('人类');
console.log(person.names);  // ["小魔王", "魔鬼", "魔王", "人类"]

3. 缺点

  1. 多个实例的引用类型指向相同,操作其中一个实例的某个引用类型,所有实例中此引用类型都将被修改。
  2. 无法传递参数,无法实现复用。(实例额外属性都是后面添加的)

二、寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象

function createObj (o) {
  const obj = Object.create(o);
  obj.getName = function () {
    console.log(obj.name);
  }
  return obj;
}
const person = {
  name: '小魔王'
}
const child = createObj(person);
child.getName(); // 小魔王

三、寄生组合式继承

1.如何实现?

利用构造函数和寄生方式继承属性,利用原型链继承方法。

2.代码实现

/**
 *继承函数
 *
 * @param {*} Child 子构造函数
 * @param {*} Parent 父构造函数
 */
function selfExtends(Child, Parent) {
    const prototype = Object.create(Parent.prototype);  // 复制父构造函数的prototype
    prototype.constructor = Child;  // 把constructor指向Child
    Child.prototype = prototype;    // 修改Child的prototype
}

function Person(name) {
    this.name = name;
    this.friends = ['大魔王', '人类', '魔鬼'];
}

Person.prototype.getName = function () {
    console.log(this.name);
}

function Child(name, age) {
    Person.call(this, name);  // 使用构造函数方式 继承父属性 支持传参
    this.age = age;
}

// 继承原型上的属性和方法,实际就是修改原型链
selfExtends(Child, Person);

// 继承后,才能在原型链上写属性和方法,不然会被替换掉
Child.prototype.getAge = function () {
    console.log(this.age);
}

const child = new Child('小魔王', 22);
const child2 = new Child('人类', 23);
child.friends.push('魔帝');
child2.friends.push('小魔王');

console.log(child.friends);  // ["大魔王", "人类", "魔鬼", "魔帝"]
console.log(child2.friends); // ["大魔王", "人类", "魔鬼", "小魔王"]

child.getName();             // 小魔王
child2.getName();            // 人类

child.getAge();              // 22
child2.getAge();             // 23

总结

有人说,现在直接用es6的class就可以实现继承了,为什么还要去学习es5的继承方式?
如果有这种想法就错了,如果你想成为一个厉害的前端,你必须务实基础。
打好基础,能够帮助自己以后阅读框架源码或者自己写框架走一点小捷径。

参考文献

object.create —— mdn