2024年Web前端最新【面试题】JS 常见的 6 种继承方式(常见)_面试题 js继承(1),全网独家首发

ajax

1)ajax请求的原理/ 手写一个ajax请求?
2)readyState?
3)ajax异步与同步的区别?
4)ajax传递中文用什么方法?

ajax.PNG

前12.PNG

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

1、原生链继承

原型链继承是比较常见的继承方式之一,其中涉及的构造函数、原型和实例,三者之间存在着一定的关系,即每一个构造函数都有一个原型对象,原型对象又包含一个指向构造函数的指针,而实例则包含一个原型对象的指针。

  function Parent1() {

    this.name = 'parent1';

    this.play = [1, 2, 3]

  }

  function Child1() {

    this.type = 'child2';

  }

  Child1.prototype = new Parent1();

  console.log(new Child1());
复制代码

原型链继承是比较常见的继承方式之一,其中涉及的构造函数、原型和实例,三者之间存在着一定的关系,即每一个构造函数都有一个原型对象,原型对象又包含一个指向构造函数的指针,而实例则包含一个原型对象的指针。虽然父类的方法和属性都能够访问,但其实有一个潜在的问题,明明我只改变了 s1 的 play 属性,为什么 s2 也跟着变了呢?原因很简单,因为两个实例使用的是同一个原型对象。它们的内存空间是共享的,当一个发生变化的时候,另外一个也随之进行了变化,这就是使用原型链继承方式的一个缺点。

  var s1 = new Child1();

  var s2 = new Child2();

  s1.play.push(4);

  console.log(s1.play, s2.play);
复制代码

2、构造函数继承

  function Parent1(){

    this.name = 'parent1';

  }



  Parent1.prototype.getName = function () {

    return this.name;

  }



  function Child1(){

    Parent1.call(this);

    this.type = 'child1'

  }



  let child = new Child1();

  console.log(child);  // 没问题

  console.log(child.getName());  // 会报错
复制代码

可以看到最后打印的 child 在控制台显示,除了 Child1 的属性 type 之外,也继承了 Parent1 的属性 name。这样写的时候子类虽然能够拿到父类的属性值,解决了第一种继承方式的弊端。

问题:父类原型对象中一旦存在父类之前自己定义的方法,那么子类将无法继承这些方法。

3、组合继承

这种方式结合了前两种继承方式的优缺点,结合起来的继承。

  function Parent3 () {

    this.name = 'parent3';

    this.play = [1, 2, 3];

  }



  Parent3.prototype.getName = function () {

    return this.name;

  }

  function Child3() {

    // 第二次调用 Parent3()

    Parent3.call(this);

    this.type = 'child3';

  }



  // 第一次调用 Parent3()

  Child3.prototype = new Parent3();

  // 手动挂上构造器,指向自己的构造函数

  Child3.prototype.constructor = Child3;

  var s3 = new Child3();

  var s4 = new Child3();

  s3.play.push(4);

  console.log(s3.play, s4.play);  // 不互相影响

  console.log(s3.getName()); // 正常输出'parent3'

  console.log(s4.getName()); // 正常输出'parent3'
复制代码

问题:通过注释我们可以看到 Parent3 执行了两次,第一次是改变Child3 的 prototype 的时候,第二次是通过 call 方法调用 Parent3 的时候,那么 Parent3 多构造一次就多进行了一次性能开销,这是我们不愿看到的。

4、原型式继承

ES5 里面的 Object.create 方法,这个方法接收两个参数:一是用作新对象原型的对象、二是为新对象定义额外属性的对象(可选参数)。

  let parent4 = {

    name: "parent4",

    friends: ["p1", "p2", "p3"],

    getName: function() {

      return this.name;

    }

  };



  let person4 = Object.create(parent4);

  person4.name = "tom";

  person4.friends.push("jerry");



  let person5 = Object.create(parent4);

  person5.friends.push("lucy");



  console.log(person4.name);//tom

  console.log(person4.name === person4.getName());//true

  console.log(person5.name);//parent4

  console.log(person4.friends);//["p1","p2","p3","jerry","lucy"]

  console.log(person5.friends);//["p1","p2","p3","jerry","lucy"]
复制代码

通过 Object.create 这个方法可以实现普通对象的继承,不仅仅能继承属性,同样也可以继承 getName 的方法。

第一个结果“tom”,比较容易理解,person4 继承了 parent4 的 name 属性,但是在这个基础上又进行了自定义。

第二个是继承过来的 getName 方法检查自己的 name 是否和属性里面的值一样,答案是 true。

第三个结果“parent4”也比较容易理解,person5 继承了 parent4 的 name 属性,没有进行覆盖,因此输出父对象的属性。

最后两个输出结果是一样的,其实 Object.create 方法是可以为一些对象实现浅拷贝的。

问题:多个实例的引用类型属性指向相同的内存,存在篡改的可能。

5、寄生继承

使用原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力再进行增强,添加一些方法,这样的继承方式就叫作寄生式继承。

虽然其优缺点和原型式继承一样,但是对于普通对象的继承方式来说,寄生式继承相比于原型式继承,还是在父类基础上添加了更多的方法。

   let parent5 = {

    name: "parent5",

    friends: ["p1", "p2", "p3"],

    getName: function() {

      return this.name;

    }

  };



  function clone(original) {

    let clone = Object.create(original);

    clone.getFriends = function() {

      return this.friends;

    };

    return clone;

  }



  let person5 = clone(parent5);



  console.log(person5.getName());

  console.log(person5.getFriends());
复制代码

person5 是通过寄生式继承生成的实例,它不仅仅有 getName 的方法,而且可以看到它最后也拥有了 getFriends 的方法。person5 通过 clone 的方法,增加了 getFriends 的方法,从而使 person5 这个普通对象在继承过程中又增加了一个方法,这样的继承方式就是寄生式继承。

问题:优缺点和原型式继承一样,但是对于普通对象的继承方式来说,寄生式继承相比于原型式继承,还是在父类基础上添加了更多的方法。

6、寄生组合式继承

结合第四种中提及的继承方式,解决普通对象的继承问题的 Object.create 方法,我们在前面这几种继承方式的优缺点基础上进行改造,得出了寄生组合式的继承方式,这也是所有继承方式里面相对最优的继承方式

  function clone (parent, child) {

    // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程

    child.prototype = Object.create(parent.prototype);

    child.prototype.constructor = child;

  }



  function Parent6() {

    this.name = 'parent6';

    this.play = [1, 2, 3];

  }

   Parent6.prototype.getName = function () {

    return this.name;

  }

  function Child6() {

    Parent6.call(this);

    this.friends = 'child5';

  }



  clone(Parent6, Child6);



  Child6.prototype.getFriends = function () {

    return this.friends;

  }
### 最后

好了,这就是整理的前端从入门到放弃的学习笔记,还有很多没有整理到,我也算是边学边去整理,后续还会慢慢完善,这些相信够你学一阵子了。

做程序员,做前端工程师,真的是一个学习就会有回报的职业,不看出身高低,不看学历强弱,只要你的技术达到应有的水准,就能够得到对应的回报。

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**

学习从来没有一蹴而就,都是持之以恒的,正所谓活到老学到老,真正懂得学习的人,才不会被这个时代的洪流所淘汰。





nds = function () {

    return this.friends;

  }
### 最后

好了,这就是整理的前端从入门到放弃的学习笔记,还有很多没有整理到,我也算是边学边去整理,后续还会慢慢完善,这些相信够你学一阵子了。

做程序员,做前端工程师,真的是一个学习就会有回报的职业,不看出身高低,不看学历强弱,只要你的技术达到应有的水准,就能够得到对应的回报。

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**

学习从来没有一蹴而就,都是持之以恒的,正所谓活到老学到老,真正懂得学习的人,才不会被这个时代的洪流所淘汰。





  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值