阅读 54

JS面向对象、原型和原型链常见问题

知识要点

  • 了解JS的面向对象
  • 了解JS中的原型及原型链
  • 一些常见的问题

补充知识点

对象的理解

  • 对象是对单个物体的简单抽象,它是一个容器,封装了一些属性和方法
    // 简单对象
   const obj = {
       name: '11',
       age: 20
   }
   // 函数对象
   function Obj() {
       this.name = '11';
       this.age = 20;
   }
复制代码

构造函数 - 生成对象

  • JS 本质不是基于类,而是基于构造函数 + 原型链
    function Person() {
        this.name = '11';
        this.age = 20;
    }
    const person = new Person()
复制代码

Person本质就是构造函数

  1. 函数体内的 this, 代表生成的实例对象
  2. 通过 new 实例化对象
  3. 构造函数必须实例化
  • 如果构造函数不初始化使用,如何进行兼容
     function Person() {
        const isClass = this instanceof Person;
        if(!isClass) {
            return new Person();
        }
        this.name = '11';
        this.age = 20;
    }
    const person = Person()
复制代码

什么是 new,new做了哪些

  • 创建一个空对象,作为返回的对象实例
  • 将生成的空对象的原型对象(__proto__)指向构造函数的 prototype 属性
  • 将当前实例对象赋给内部 this
  • 执行构造函数内部初始化代码

什么是 constructor

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

原型对象

  • 构造函数用来初始化创建对象的函数,自动给构造函数赋予一个 prototype 属性,该属性实际等于实例对象的原型对象(__proto__)

实例对象

  • 每个对象中都有一个 __proto__
  • 每个实例对象都有个 constructor 属性
  • constructor 由继承而来,并指向当前构造函数

继承

在原型对象上的所有属性和方法,都能被实例所共享

通过原型链继承

    // 动物类
    function Animal() {
        this.name = 'dog'
    }
    Animal.prototype.getName = function() {
        return this.name
    }
    // 狗类
    function Dog() {}
    // 继承Animal类
    Dog.prototype = new Animal()
    Dog.prototype.constructor = Dog
    const animal = new Dog() 
复制代码

上述继承的实现方式,本质是重写原型对象,将父对象的属性和方法作为子对象原型对象上的属性和方法

通过原型链继承有什么缺点

  • 父类属性一旦赋值给子类的原型属性,该属性属于子类的共享属性
  • 实例化子类时不能向父类传参

通过构造函数继承

  • 在子类构造函数内部调用父类构造函数
    function Animal(arg) {
        this.name = 'dog'
    }
    Animal.prototype.getName = function() {
        return this.name
    }
    // 子类
    function Dog(arg) {
        Animal.call(this, arg)
    }
    const animal = new Dog()
复制代码

解决了共享属性和传参问题,但是原型链上的共享方法无法被继承

组合继承

通过组合继承可以解决上述问题

    function Animal(arg) {
        this.name = 'dog'
    }
    Animal.prototype.getName = function() {
        return this.name
    }
    // 子类
    function Dog(arg) {
        Animal.call(this, arg)
    }
    Dog.prototype = new Animal()
    Dog.prototype.constructor = Dog
    // Dog继承Animal
    const dog = new Dog()
复制代码

缺点:组合继承无论在何种场景,都会调用两次父类构造函数,一次在初始化子类原型时,一次在子类构造函数内部call父类的时候

寄生组合继承

寄生组合继承可以解决上述问题

    // 子类
    function Dog(arg) {
        Animal.call(this, arg)
    }
    Dog.prototype = Object.create(Animal.prototype)
    Dog.prototype.constructor = Dog
复制代码

多重继承

    // 动物类
    function Animal(arg) {
        this.name = 'bird'
    }
    Animal.prototype.getName = function() {
        return this.name
    }
    // 技能类
    function Skill(arg) {
        this.name = 'fly'
    }
    Skill.prototype.getSkill = function() {
        return this.name
    }
    // 鸟类
    function Bird(arg) {
        Animal.call(this, arg)
        Skill.call(this, arg)
    }
    Bird.prototype = Object.create(Animal.prototype)
    Object.assign(Bird.prototype, Skill.prototype)
    Bird.prototype.constructor = Bird
    
    const bird = new Bird()
复制代码

流程图

image.png

文章分类
前端
文章标签