跟着coderwhy学习JavaScript高级(十一)

288 阅读3分钟

继承

首先要知道在js中是不支持继承的,但是我们可以借助其他的方式来模拟这种过程。

在实现继承的时候,我们要遵循一个原则,属性写在构造函数内,方法写在原型上,为什么要这么做呢?

因为属性如果写在原型链上,每个new出来的对象都会使用这一个属性,每个属性都是一样的,如果不是刻意而为之,那么最好是写在构造函数内,这样每次new出来对象,每个对象的属性都相互不影响,就像vue中data为什么是一个函数一样。因为他需要返回一个新的对象。差不多可以这么理解。

那么方法为什么要写在原型上呢,方法干的事情,都是一样的,所以为了复用,我才考虑写在原型上,因为如果写在构造函数内,每次new都会生成一个新的方法,这样会造成内存的浪费,所以我们基本上约定俗成。

原型链继承

// 父类: 定义共用的属性和方法
function Person() {
    this.name = "malong";
}

Person.prototype.sayName = function(){
   console.log(this.name)
}

// 子类
function Student(sId) {
    this.sId = sId;
}

// 原型链继承
Student.prototype = new Person();

// 我们无法初始化继承过来的name属性
var stu1 = new Student(1);
var stu2 = new Student(2);


// 原型链继承弊端1:
// 打印stu1,stu2对象,继承来的属性是看不到的,因为在prototype里面
console.log(stu1,stu2)
 
// 原型链继承弊端2:
// 直接修改对象上继承的属性, 是给本对象添加了一个新属性
stu1.name = "malong1"
stu2.name = "malong2" 

// 修改继承过来的(prototype)属性, 对象之间会相互受到影响
stu1.prototype.name = '修改了'
console.log(stu2.prototype.name)

// 总结:我们发现使用原型链继承,无法继承属性,属性还是在原型链上,不能让自己的属性进行初始化
// 不能继承构造函数上的属性和方法,只能继承原型上的属性和方法

对象冒充继承

// 父类: 定义共用的属性和方法
function Person(name,age) {
    this.name = name;
    this.age = age;
}
// 这里没有被继承
Person.prototype.test = "test";
Person.prototype.sayName = function() {
    console.log(this.name);
}

// 子类
function Student(name,age,sId) {
    Person.apply(this,[name,age]);
    this.sId = sId;
}

var stu = new Student("malong",18,001);
console.log(stu);

// 总结: 无法继承原型(prototype)上的属性和方法,只能继承构造函数内部的属性和方法

组合继承(对象冒充+原型链)

function Person(name,age) {
    this.name = name;
    this.age = age;
}

Person.prototype.test = "test";
Person.prototype.sayName = function() {
    console.log(this.name)
}

function Student(name,age,sId) {
    // 对象冒充
    Person.apply(this,[name,age]); //第一次调用
    this.sId = sId;
}

// 原型链
Student.prototype = new Person();  //第二次调用

var stu = new Student("malong",18,001)

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

image.png

寄生式继承-对象

    // 寄生式继承:一定要是一个对象,不能是函数
    var base = {
      name: "base",
      say: function () {
        console.log(this.name)
      }
    }

    
    function inherit(base, name, age) {
      var newObj = {};
      Object.setPrototypeOf(newObj, base)
      newObj.name = name;
      newObj.age = age;
      newObj.test = function () {
        console.log(this.name)
      }

      return newObj;
    }
    
    // 这种写法和上面的写法一样
    function inherit1(base, name, age) {
      var newObj = Object.create(base)
      newObj.name = name;
      newObj.age = age;
      newObj.test = function () {
        console.log(this.name)
      }
      return newObj;
    }


    var obj1 = inherit(base,"malong",18)
    //总结: 这种方式的prototype 是Object 和工厂函数类似 我们在打印对象时,对象的类型都是Object类型
    console.log(obj1)

寄生式组合继承

  // 父类
    function Person(name, age) {
      this.name = name;
      this.age = age;
    }

    Person.prototype.getName = function () {
      console.log(this.name)
    }

    // 子类
    function Student(sId, name, age) {
      Person.apply(this, [name, age]);
      this.sId = sId;
    }


    function inheritPrototype(base, child) {
      child.prototype = Object.create(base.prototype);
      Object.defineProperty(child, "constructor", {
        enumerable: false, 
        configurable: true,
        writable: true,
        value: base
      });
    }

    inheritPrototype(Person, Student);
    let stu = new Student(1, "malong", 18);
    console.log(stu)
    
    //总结:没有缺点

ES6的class继承

    class Person {
      constructor(name, age) {
        this.name = name;
        this.age = age;
      }
      run() {
        console.log("person run run run")
      }
    }

    class Student extends Person {
      constructor(name, age, sId) {
        super(name, age);
        this.sId = sId;
      }
      eat() {
        console.log("student go go")
      }
    }

    var stu = new Student("zhangsan", 18, 001)
    console.log(stu)