JS的寄生组合式继承

什么叫继承

  1. 在很多语言中,都有继承的概念,继承他可以让我们的代码更加简化,没有那么亢余,在实际开发过程中,我们肯定会遇到多个对象拥有共同属性的情况,这个时候我们是每个对象都写一遍重复的代码呢,还是考虑将其抽离出去,给他们一个共同的父类呢?当我们是开发中习惯了这种思维,我相信整个代码的可阅读性和可拓展性都是不可比的。

如何在js中实现继承

首先给出个概念,ES6提出的class,他是一个全新的概念吗?并不是,他只是一种语法糖。一种基于寄生组合式继承的语法糖,下面就详细的看看这到底是一个什么东西?

  1. 首先我们先定义一个普通的构造函数

    1
    2
    3
    4
    5
    //这里有个细节,或者是是一种规范,通常定义的构造函数我们会以大写字母开头,以便和普通的函数加以区分,这时社区的一种不谋而合规范,即便不遵守从代码层面来说也没问题
    function People(name, age) {
    this.name = name;
    this.age = age;
    }
  2. 然后我们给这个构造函数的原型上定义一些方法

    1
    2
    3
    4
    5
    6
    7
    8
    People.prototype.eating = function () {
    console.log("吃就完事了");
    };

    People.prototype.sleeping = function () {
    console.log("睡就完事了");
    };

  3. 我们再次定义一个构造函数

    1
    2
    3
    4
    5
    6
    7
    function Student(name, age, number, height, sex) {
    People.call(this, name, age);
    this.number = number;
    this.height = height;
    this.sex = sex;
    }
    //这里我们用People.call,让Student构造函数能够执行Person的方法,因为name和age我们不想重新再次复写,
  4. 给Student的原型上定义一个自己的方法

    1
    2
    3
    Student.prototype.wan = function () {
    console.log("出去玩");
    };
  5. 然后我们实例化一个Student对象

    1
    2
    3
    let s1 = new Student("dw", 18, 100, 1.98, "男");

    //s1 ---->Student { name: 'dw', age: 18, number: 100, height: 1.98, sex: '男' }
  6. 好,准备工作我们就做完了,我们做了那么多铺垫,我们的目的是什么呢?我们需要通过s1访问到Person定义的两个方法,这样的话我们就将Student和Person联系在一起了,从某种意义上来说就是实现了继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     Student.prototype = Object.create(People.prototype);
    Object.defineProperty(Student.prototype, "constructor", {
    enumerable: false,
    configurable: true,
    writable: true,
    value: Student,
    });
    //Object.create方法会返回一个对象,并且将传递进去的对象的原型对象作为返回对象的隐式原型 举个例子
    //example
    let test = {name:'codeywei',age:18}
    const obj = Object.create(test)
    console.log(obj.__proto__) //{ name: 'coderwei', age: 18 }
    console.log(obj.prototype === test.prototype); //true
  7. 现在我们就成功的将原本毫无关系的Person、Student联系到一起,并且实例化的Student可以访问到Person上的方法

  8. 粗略的画了一副s1、Person、Student原型图

    原型图

提示

  1. 当然这种方法是有弊端的,比如说代码顺序,如果在Object.create之前创建实例对象s1或者是给Student的原型上定义方法,在后续我们是访问不到的
  2. 为什么呢?其实思考一下也能发现,js是单线程语言的解释性语言,解释一行执行一行,当创建实例s1的时候还没有将他们联系到一起,那么这个对象已经创建出来了,后续在通过create函数将Person和Student联系到一起的时候,s1对象的隐式原型依然不是Person.prototype的
  3. 至于为什么访问不到方法,先在Student.prototype添加一个方法,后续创建一个新的对象将其覆盖,之前的方法自然也不会存在。

写在最后

我有个习惯,写完代码会对代码进行审视,最起码站在我先在的知识面,对代码进行审视,何处优化?怎么优化?这就是一个进步的过程,没有绝对完美的代码,只有不断趋近于完美的代码,所以最后,这个代码的Object.create方法我觉得可以进行封装,不可能每次对两个构造函数进行继承处理的时候都复写一遍这样的代码,逻辑还是一模一样的,所以最后的代码我就直接贴在下面了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 对寄生组合继式承封装
function myextend(newObj, oldObj) {
newObj.prototype = Object.create(oldObj);
Object.defineProperty(newObj.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: newObj,
});
}
function People(name, age) {
this.name = name;
this.age = age;
}

People.prototype.eating = function () {
console.log("吃就完事了");
};

People.prototype.sleeping = function () {
console.log("谁就完事了");
};

function Student(name, age, number, height, sex) {
People.call(this, name, age);
this.number = number;
this.height = height;
this.sex = sex;
}

myextend(Student, People.prototype);
Student.prototype.wan = function () {
console.log("出去玩");
};
let s1 = new Student("dw", 18, 100, 1.98, "男");
console.log(s1);
s1.wan(); //出去玩
s1.eating(); //吃就完事了

最后的最后,恭喜EDG,成功拿下GEN,我们六号决赛见。