如果没有继承

假如要写2个构造函数,他们有可能会有重复代码,如果没有继承,这些都要重新写一次
对象的继承-ES5 - 图1
对象的继承-ES5 - 图2

对象的继承方案

继承方案一:原型链继承

说明

  1. //父类:公共属性和方法
  2. function Person () {
  3. this.name = "why";
  4. this.age = 18;
  5. this.friends = []
  6. }
  7. Person.prototype.eating = function () {
  8. console.log(this.name + "eating~")
  9. }
  10. //子类:特有属性和方法
  11. function Student () {
  12. this.sno = 111;
  13. }
  14. Student.prototype = new Person()
  15. Student.prototype.studying = function () {
  16. console.log(this.name + "studying~")
  17. };
  18. let stu = new Student()
  19. console.log(stu.name) // why
  20. stu.eating() // whyeating~

直接修改子类构造函数的原型
对象的继承-ES5 - 图3

弊端

1、某些继承属性看不到

  1. // 打印son,某些属性看不到
  2. console.log(stu) // Student {sno: 111}

2、公共的属性,如果值是引用类型的(对象、数组)子类会相互影响

  1. let stu1 = new Student()
  2. let stu2 = new Student()
  3. // 直接修改实例对象上的属性,是给该实例对象添加了一个新属性
  4. stu1.name = 'kobe'
  5. console.log(stu2.name); // why
  6. // 获取引用,修改引用中的值,会相互影响
  7. stu1.friends.push('kobe')
  8. console.log(stu1.friends); // ['kobe']
  9. console.log(stu2.friends); // ['kobe']

因为都是同一个引用,stu1.friends.push() 一开始会查找stu1有没有friends这个属性,没有,然后查找原型上有没有,如果有,就往原型的数组上push,因此和自己是没有关系的。
3、不好传参数

  1. function Student (name, sno) {
  2. this.name = name
  3. this.sno = 111;
  4. }
  5. let stu3 = new Student('张三'112)
  6. // 这样传的话,需要修改Student这个函数,这样他的父类Person就没意义了

我们想做的应该是Person里面传参数,但是new Person( ) 的时候就会定下来(如name),这样所有后面所有的学生都有共同的名字,也不是我们想要的

原型式继承函数

这种模式要从道格拉斯·克罗克福德(Douglas Crockford,著名的前端大师,JSON的创立者)在2006年写的一篇文章说起: Prototypal Inheritance in JavaScript(在JS中使用原型式继承)
既创建一个函数,给对象设置原型:

  1. // 原型链式继承函数
  2. // 方式一
  3. function createObject1 (o) {
  4. const newObj = {};
  5. Object.setPrototypeOf(newObj, o)
  6. return newObj
  7. }
  8. // 方式二
  9. function createObject2 (o) {
  10. function Fn () { }
  11. Fn.prototype = o;
  12. const newObj = new Fn()
  13. return newObj
  14. }
  15. let obj = {
  16. name: '张三'
  17. }
  18. const info = createObject2(obj)
  19. console.log(info);
  20. /*
  21. Fn {}
  22. [[Prototype]]: Object
  23. name: "张三"
  24. */
  25. console.log(info.__proto__); // {name: '张三'}
  26. // 或者用方法:Object.create( ) ,和上面功能一样的
  27. const info = Object.create(obj)
  28. console.log(info);
  29. /*
  30. {}
  31. [[Prototype]]: Object
  32. name: "张三"
  33. */
  34. console.log(info.__proto__);

继承方案二:借用构造函数

说明

  1. //父类:公共属性和方法
  2. function Person (name, age, friends) {
  3. this.name = "why";
  4. this.age = 18;
  5. this.friends = []
  6. }
  7. Person.prototype.eating = function () {
  8. console.log(this.name + "eating~")
  9. }
  10. //子类:特有属性和方法
  11. function Student (name, age, friends, sno) {
  12. // call( )或apply( )
  13. Person.call(this.name, age, friends)
  14. this.sno = 111
  15. }
  16. Student.prototype = new Person()
  17. Student.prototype.studying = function () {
  18. console.log(this.name + "studying~")
  19. };

优势

一次解决方案一的三个弊端

  1. let stu1 = new Student('why', 18, ['lilei'], 111)
  2. let stu2 = new Student('kobe', 30, ['james'], 112)
  3. console.log(stu1); // Student {name: 'why', age: 18, friends: ['lilei'], sno: 111}

对象的继承-ES5 - 图4

弊端

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

对象的继承-ES5 - 图5

继承方案三:寄生式继承

寄生式(Parasitic)继承是与原型式继承紧密相关的一种思想,并且同样由道格拉斯·克罗克福德(DouglasCrockford)提出和推广的
寄生式继承的思路是结合原型类继承和工厂模式的一种方式
即创建一个封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再将这个对象返回

  1. const personObj = {
  2. running: function () {
  3. console.log("running")
  4. }
  5. }
  6. function createStudent (name) {
  7. let stu = Object.create(personObj)
  8. stu.name = name
  9. stu.studying = function () { console.log("studying~") }
  10. return stu
  11. }
  12. let stuObj = createStudent("why ")
  13. let stuObj1 = createStudent("kobe")
  14. let stuObj2 = createStudent("james")

最终继承方案四:寄生组合式继承

  1. // 父类
  2. function Person (name, age, friends) {
  3. this.name = name
  4. this.age = age
  5. this.friends = friends
  6. }
  7. Person.prototype.running = function () {
  8. console.log("running~")
  9. }
  10. Person.prototype.eating = function () {
  11. console.log("eating~")
  12. }
  13. // 子类
  14. function Student (name, age, friends, sno, score) {
  15. Person.call(this, name, age, friends)
  16. this.sno = sno
  17. this.score = score
  18. }
  19. // 1.修改原型
  20. Student.prototype = Object.create(Person.prototype)
  21. // 2.修改类型,否则打印时类型是Person,因为student没有自己的constructor
  22. object.defineProperty(Student.prototype, "constructor", {
  23. enumerable: false,
  24. configurable: true,
  25. writable: true,
  26. value: Student
  27. })
  28. // 3.再给子类定义自己的方法
  29. Student.prototype.studying = function () {
  30. console.log("studying~")
  31. }
  32. // 4.创建具体的子类对象
  33. var stu = new Student("why", 18, ["kobe"], 111, 100)
  34. // 把1、2重新封装一下,方便后面给其他不同对象继承
  35. //定义寄生式核心函数
  36. function inheritPrototype (subType, superType) {
  37. subType.prototype = object(superType.prototype)
  38. subType.prototype.constructor· = subType
  39. }
  40. // 调用
  41. inheritPrototype(Student, Person)