面向对象、原型及原型链

面向对象、原型及原型链

一、面向对象

1.1 对象是什么?

  • 对象是对于单个物体的简单抽象
  • 对象是一个容器,封装了属性和方法
    属性:对象的状态
    方法:对象的行为
      // 简单对象
    const school = {
        teacher: '张三',
        student: '李四',
        startCourse: function(name) {
            return `开始${name}课`;
        }
    }

    // 函数对象
    function school() {
        this.teacher = '张三';
        this.student = '李四';
        this.startCourse = function(name) {
            return `开始${name}课`;
        }
    }

1.2 为什么要面向对象

面向对象 —> 逻辑迁移更加灵活、代码复用性高、高度的模块化

二、构造函数 -> 生成对象

  • 需要一个模板(表征了一类物体的共同特征,从而生成对象)
  • 类即是对象模板
  • js本质并不是基于类,而是基于构造函数和原型链
  //construtor(构造函数) + prototype(原型链)
  
  function School(){
    //函数体内使用的this,指向所要生成的实例
    this.teacher = '张三';
    this.student = '李四';
    this.startClass = function(name){
      return `开始${name}课`;
    }
  }
  //生成对象用new来进行实例化,可以做初始化传参
  const school = new School;

根据上述代码可知,school本质就是构造函数

三.关于构造函数的追问

3.1 构造函数

3.1.1 如果构造函数不初始化,可以使用吗?
  • 答:不可以使用
3.1.2 如果项目中需要使用,通常(不被外界感知)如何解决?
  function Class(){
    const _isClass = this instanceof Class; //instance of:查看变量是不是对象的实例(所有对象都是Object的实例)
    if(!_isClass){
      return new Class();
    }
    this.teacher = '张三';
    this.student = '李四';
    this.startClass = function(name){
      return `开始${name}课`;
    }
  }
  const course  = new Class();
  • 启发:如果编写底层的api代码时,尽量做到不需要让外部感知内部类型

3.2 new是什么 / new的原理 /new时候做了什么

  function Class(){};
  const class = new Class();

1.创建了一个空对象
2.将生成的空对象的原型对象指向了构造函数的prototype属性
3.将当前的实例对象赋给了内部this
4.执行构造函数的初始化代码

3.2.1 追问 实例属性之间的影响
  • 答:实例属性之间是独立关系,原因如下:
  function Class(teacher, student){
    this.teacher = teacher;
    this.student = student;
  }
  const class1 = new Class('张三','李四');
  const class2 = new Class('张三','王五');
  
  class2.leader = '佩奇';//不影响class1实例

3.3 constructor是什么?

1.每个对象在创建时,会自动拥有一个构造函数属性constructor
2.constructor继承自原型对象,指向了构造函数的引用

  function Class(teacher, student){
    this.teacher = teacher;
    this.student = student;
  }
  const class = new Class('张三','李四');
3.3.1 使用构造函数没有问题吗?会有什么性能上的问题?
  • 构造函数中的方法,会存在于每一个生成的实例里,重复的挂载其实是会导致资源浪费
  function Class(name){
    this.teacher = '张三';
    this.student = '李四';
    this.startClass = function(){
      return `开始${name}课`}
  }
  const class1 = new Class('vue');
  const class2 = new Class('react');

四、原型对象

  function Class(){};
  const class1 = new Class();
  const class2 = new Class();

1.构造函数:用来初始化创建对象的函数 -> Class
自动给构造函数赋予一个属性prototype,该属性等于实例对象的原型对象
2.实例对象:class1、class2是实例对象,根据原型对象创建出来的实例
1)每个对象都有一个proto
2)每个实例对象都有一个constructor
3)constructor由继承而来,并指向当前的构造函数
3.原型对象:Class.prototype

   function Class() {};
    Class.prototype.teacher = '张三';
    const course1 = new Class();
    const course2 = new Class();

    // 对上篇原型对象做优化
    function Class() {
        this.teacher = '张三';
        this.student = '李四';
    }

    //方法挂载于prototype上
    Class.prototype.startClass = function(name){
      return `开始${name}课`;
    }
   const class1 = new Class('vue');
   const class2 = new Class('react');

五、继承

  • 在原型对象的所有属性和方法,都能被实例所共享
  • 本质:重写原型对象方式,将父对象的属性方法,作为子对象原型对象的属性和方法
  //Game类
  function Game(){
    this.name = 'lol';
  }
  Game.prototype.getName = function(){
    return this.name;
  }
  function LOL(){}
    LOL.prototype = new Game();
    LOL.prototype.constructor = LOL;
    const game = new LOL();

5.1 原型链继承有什么缺点

  function Game(){
    this.name = 'lol';
    this.skin = ['s'];
  }
  Game.prototype.getName = function(){
    return this.name;
  }
  function LOL(){}
  LOL().prototype = new Game();
  LOL().prototype.constructor = LOL;
  const game1 = new LOL();
  const game2 = new LOL();
  game1.skin.push('s');//加入皮肤 game1 和 game2会同时拥有
  • 1.父类属性一旦赋值给子类的原型属性,此时属性属于子类的共享属性了
  • 2.实例化子类时,无法向父类做传参

5.1.1 以上问题的解决方法

  //经典继承:在子类构造函数内部调用父类构造函数
  function Game(arg){
    this.name = 'lol';
    this.skin = ['s'];
  }
  Game.prototype.getName = function(){
    return this.name;
  }

  function LOL(arg){
    Game.call(this,arg);//父级的东西可通过call给子级
  }
  //解决了共享属性的问题和子向父传参的问题
  const game3 = new LOL('arg');

5.2 追问

  • 原型链上的共享方法无法被读取继承,如何解决?
  //解决方案:组合继承
   function Game(arg) {
        this.name = 'lol';
        this.skin = ['s'];
    }
    Game.prototype.getName = function() {
        return this.name;
    }

    // LOL类
    function LOL(arg) {
        Game.call(this, arg);
    }
    LOL.prototype = new Game();
    LOL.prototype.constructor = LOL;

    const game3 = new LOL();

5.3 再次追问

  • 组合继承没有缺点吗?
    答:有,问题在于:无论何种场景,都会调用两次父类的构造函数(1.初始化子类原型时;2.子类调用函数内部call父类时)
  //解决方案:寄生组合继承
  function Game(arg) {
        this.name = 'lol';
        this.skin = ['s'];
    }
    Game.prototype.getName = function() {
        return this.name;
    }

    // LOL类
  function LOL(arg) {
      Game.call(this, arg);
  }
  LOL.prototype = Object.create(Game.prototype);
  LOL.prototype.constructor = LOL;

5.4 终级追问

  • 看起来解决了继承,但是如何实现多重继承?
   function Game(arg) {
        this.name = 'lol';
        this.skin = ['s'];
    }
    Game.prototype.getName = function() {
        return this.name;
    }

    function Store() {
        this.shop = 'steam';
    }
    Store.prototype.getPlatform = function() {
        return this.shop;
    }

    function LOL(arg) {
        Game.call(this, arg);
        Store.call(this, arg);
    }
    LOL.prototype = Object.create(Game.prototype);
    Object.assign(LOL.prototype,Store.prototype);
    LOL.prototype.constructor = LOL;
    //LOL两类继承
    const game3 = new LOL();

六、原型链图解

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨龙王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值