image.png

1.为什么需要继承

  1. // Student
  2. function Student(name,age,sno) {
  3. this.name = name;
  4. this.age = age;
  5. this.sno = sno;
  6. }
  7. Student.prototype.running = function () {
  8. console.log(this.name + "running");
  9. }
  10. Student.prototype.eating = function () {
  11. console.log(this.name + "eating~");
  12. }
  13. Student.prototype.studing = function () {
  14. console.log(this.name + "studing~");
  15. }
  16. // Teacher
  17. function Teacher(name,age,title) {
  18. this.name = name;
  19. this.age = age;
  20. this.title = title;
  21. }
  22. Teacher.prototype.running = function() {
  23. console.log(this.name + " running~")
  24. }
  25. Teacher.prototype.eating = function() {
  26. console.log(this.name + " eating~")
  27. }
  28. Teacher.prototype.teaching = function() {
  29. console.log(this.name + " teaching")
  30. }

在这里我们定义2个构造函数 来生产一些类,我们发现了大量的重复代码,因此我们可以封装一个功能从而节约代码的体积,所以我们引出了继承

2.原型链实现继承

  1. // 父类 公共属性和方法
  2. function Person () {
  3. this.name = "why";
  4. this.friends = [];
  5. }
  6. Person.prototype.eating = function () {
  7. console.log(this.name + "eating~");
  8. }
  9. // 子类: 特有属性和方法
  10. function Student() {
  11. this.son = 11;
  12. }
  13. let p = new Person()
  14. Student.prototype = p
  15. Student.prototype.studying = function() {
  16. console.log(this.name + " studying~")
  17. }
  18. // 原型链实现继承的弊端: 无法枚举的,因此无法查到
  19. // 1.第一个弊端: 打印stu对象, 继承的属性是看不到的
  20. // console.log(stu.name)
  21. // 2.第二个弊端: 创建出来两个stu的对象
  22. let stu1 = new Student()
  23. let stu2 = new Student()
  24. // 直接修改对象上的属性, 是给本对象添加了一个新属性
  25. stu1.name = "kobe"
  26. console.log(stu2.name)
  27. // 获取引用, 修改引用中的值, 会相互影响
  28. stu1.friends.push("kobe")
  29. console.log(stu1.friends) // kobe
  30. console.log(stu2.friends) // kobe
  31. // 3.第三个弊端: 在前面实现类的过程中都没有传递参数
  32. let stu3 = new Student("lilei", 112);

3.借用构造函数实现

 // 父类: 公共属性和方法
function Person(name, age, friends) {
  // this = stu
  this.name = name
  this.age = age
  this.friends = friends
}

Person.prototype.eating = function() {
  console.log(this.name + " eating~")
}

// 子类: 特有属性和方法
function Student(name, age, friends, sno) {
  Person.call(this, name, age, friends) // -这句代码借用构造函数实现继承
  // this.name = name
  // this.age = age
  // this.friends = friends
  this.sno = 111
}

var p = new Person()
Student.prototype = p

Student.prototype.studying = function() {
  console.log(this.name + " studying~")
}


// name/sno
var stu = new Student("why", 18, ["kobe"], 111)

// console.log(stu.name)
// stu.eating()

// stu.studying()


// 原型链实现继承已经解决的弊端
// 1.第一个弊端: 打印stu对象, 继承的属性是看不到的
console.log(stu)

// 2.第二个弊端: 创建出来两个stu的对象
var stu1 = new Student("why", 18, ["lilei"], 111)
var stu2 = new Student("kobe", 30, ["james"], 112)

// // 直接修改对象上的属性, 是给本对象添加了一个新属性
// stu1.name = "kobe"
// console.log(stu2.name)

// // 获取引用, 修改引用中的值, 会相互影响
stu1.friends.push("lucy")

console.log(stu1.friends)
console.log(stu2.friends)

// // 3.第三个弊端: 在前面实现类的过程中都没有传递参数
// var stu3 = new Student("lilei", 112)



// 强调: 借用构造函数也是有弊端:
// 1.第一个弊端: Person函数至少被调用了两次
// 2.第二个弊端: stu的原型对象上会多出一些属性, 但是这些属性是没有存在的必要

image.png

4.寄生组合式继承

function Person(name, age, friends) {
    this.name = name
    this.age = age
    this.friends = friends
}

Person.prototype.eating = function () {
    console.log("eating")
}
Person.prototype.running = function () {
    console.log("running");
}

function Student(name, age, friends, son, score) {
    Person.call(this, name, age, friends)
    this.son = son
    this.score = score
}

function inhertPrototype(son, father) {
    son.prototype = Object.create(father.prototype);
    Object.defineProperty(son.prototype, "constructor", {
        enumerable: false,
        configurable: true,
        writable: true,
        value: son
    })
}

inhertPrototype(Student, Person);
Student.prototype.studying = function () {
    console.log("studying");
}
let sty1 = new Student("why", 22, ["kobel"], 12, 98);
console.log(sty1);
sty1.studying();
sty1.eating();
sty1.running();

5.es6中的继承

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  running() {
    console.log(this.name + " running~")
  }

  eating() {
    console.log(this.name + " eating~")
  }

  personMethod() {
    console.log("处理逻辑1")
    console.log("处理逻辑2")
    console.log("处理逻辑3")
  }

  static staticMethod() {
    console.log("PersonStaticMethod")
  }
}

// Student称之为子类(派生类)
class Student extends Person {
  // JS引擎在解析子类的时候就有要求, 如果我们有实现继承
  // 那么子类的构造方法中, 在使用this之前
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }

  studying() {
    console.log(this.name + " studying~")
  }

  // 类对父类的方法的重写
  running() {
    console.log("student " + this.name + " running")
  }

  // 重写personMethod方法
  personMethod() {
    // 复用父类中的处理逻辑
    super.personMethod()

    console.log("处理逻辑4")
    console.log("处理逻辑5")
    console.log("处理逻辑6")
  }

  // 重写静态方法
  static staticMethod() {
    super.staticMethod()
    console.log("StudentStaticMethod")
  }
}

var stu = new Student("why", 18, 111)
console.log(stu)

// console.log(Object.getOwnPropertyDescriptors(stu.__proto__))
// console.log(Object.getOwnPropertyDescriptors(stu.__proto__.__proto__))

stu.eating()
stu.running()

stu.personMethod()

Student.staticMethod()

console.log(Object.getOwnPropertyDescriptors(Person))