【12-30】手写继承

function Person(name) {
    this.name = name
  }
  Person.prototype.say = function() {
    console.log(`你好,我是 ${this.name}`)
  }

// 尝试定义一个子类,来继承 Person
  function Student(name, id){
    Person.call(this, name) // 调用父类构造函数
    this.id = id
  }
Student.prototype.fun = function(){
    console.log(`你好,我是 ${this.name},我的学号是${this.id}`);
}
  let s = new Student('玛丽','1534543')
 s.fun()//你好,我是 玛丽,我的学号是1534543
 s.toString()
//   s.say() s的proto中找,proto指向器构造函数的prototype,原型是一个对象也有proto,proto又可以指向构造函数的prototype
console.log(Student.prototype.__proto__===Object.prototype);//true

s.fun()//你好,我是 玛丽,我的学号是1534543
说明Person的属性成功被继承了
但是这个时候的say()方法未定义,s.say() s的proto中找,proto指向器构造函数的prototype,这
原型是一个对象也有proto,proto又可以指向构造函数的prototype,这个时候应该是指向Object,身上并没say方法
加上这句话

Student.prototype=Person.prototype;

找不到say时,就 proto->Student.prototype->Object.prototype现在把指向改成了
但是!如果这个时候 Student 想要给自己的 prototype 加一个新方法,怎么办?我们知道因为只是复制了地址,如果修改了 Student.prototype,Person.prototype 也将被修改,这显然是我们不愿意看到的。
Person.prototype,找到了了Person上的say方法,但是Student.prototype.fun上的fun找不到了
再进行更改:

Student.prototype.__proto__ = Person.prototype

改成这样,就会先去prototype寻找,没找到的话再顺着proto往上找到Person

是不是看起来有点眼熟?
Array.prototype.__proto__ === Object.prototype // true
这就是原型链!这就是我们所说的继承!是不是有点感觉了?
当然,刚才也说了,我们最好用下面这种写法:
Student.prototype = Object.create(Person.prototype)
复制代码回顾一下到现在我们做了什么?

首先我们通过 call 父级构造函数,来实现属性的继承,有了 姓名
然后我们通过建立原型链,来实现方法的继承,我们的 小明 可以 自我介绍 了

看似已经结束,但是实际上还有一个隐藏的 Bug,我们接下来来解决这个 Bug。

解决 constructor 的问题

细心的你会发现(我们在最开始也说过了),我们的对象实例和构造函数中是有一个 constructor 属性的,比如:

const arr = [1, 2]
arr.__proto__.constructor === Array // true
Array.prototype.constructor === Array // true

但是,Student.prototype 中的 constructor 被刚才的那一番操作给搞没了,我们需要把它弄回来:
Student.prototype.constructor = Student
这样就完成了一波类的继承。

总结

  function Person(name) {
    this.name = name
  }
  Person.prototype.say = function() {
    console.log(`你好,我是 ${this.name}`)
  }

// 尝试定义一个子类,来继承 Person
  function Student(name, id){
    Person.call(this, name) // 调用父类构造函数
    this.id = id
  }
Student.prototype.fun = function(){
    console.log(`你好,我是 ${this.name},我的学号是${this.id}`);
}
// Student.prototype=Person.prototype;
Student.prototype.__proto__ = Person.prototype;
Student.prototype.constructor = Student
  let s = new Student('玛丽','1534543')
  let p = new Person('张三')
  s.say()
  s.fun()

用 class 继承

既然他本身就是语法糖,我个人认为没必要搞那么细,其实本质跟上面的使用原型链的继承是一样的,搞清楚是怎么写的就好啦:

// 定义父类
class Person {
  constructor(name) { // 定义属性
    this.name = name
  }
  say() { // 定义方法
    console.log(`你好,我是 ${this.name}`)
  }
}

// 定义子类
class Student extends Person {
  constructor(name, id) {
    super(name) // 这里的 姓名 两个字要与父类中的一样,继承属性和方法
    this.id = id // 定义新属性
  }
  fun() { // 定义新方法
    console.log(`我的学号是 ${this.id}`)
  }
}



let s = new Student('小红', 345678)
s.say() // 你好,我是 小红
s.fun() // 我的学号是 345678

不同点可以看这篇文章

  1. class类内部定义的所有方法都是不可枚举的。这点和ES5行为不一致。

  2. 类和模块的内部默认使用严格模式,所以不需要使用use strict指定运行模式。

  3. 类必须使用 new 来调用,否则会报错。这是他跟普通构造函数的一个主要区别,后者不用 new 也可以执行。

  4. 类内部不存在变量提升,这一点与ES5完全不同。

  5. class继承可以实现原生构造函数的继承,而ES5不可以。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 概述 本篇主要讲述构造函数和继承 构造函数 编程 编程主要分为函数式编程和面向对象编程 函数式编程 ==> 推崇函...
    bowen_wu阅读 3,110评论 0 0
  • Javascript中的‘类’ Javascript中并没有严格意义上的类。ES5中通过使用首字母大写的方法来模拟...
    softbone阅读 2,844评论 0 0
  • 原型 简单创建一个构造函数与实例: Person 构造函数person 是 Person 的一个实例对象 inst...
    月光在心中阅读 3,833评论 0 5
  • 一,JavaScript的书写格式 1.行内脚本:写在HTML标签内部,通过一个属性节点来添加,不推荐使用,例如“...
    刘远舟阅读 3,462评论 0 0
  • this部分链接 原型链相关问题 问题7:有如下代码,解释Person、 prototype、proto、p、co...
    鸿鹄飞天阅读 2,806评论 0 0

友情链接更多精彩内容