继承

89 阅读4分钟

原型链继承

// 利用改变 原型链 的方式来达到继承效果
function Person (age) {
  this.age = age || 18
}
Person.prototype.sleep = function () {
  console.log('sleeping')
}
​
function Programmer() {}
​
Programmer.prototype = new Person ()
Programmer.prototype.code = function () {
  console.log('coding')
}
​
let jon = new Programmer()
jon.code() // coding
jon.sleep() // sleeping
​
jon instanceof Person // true
jon instanceof Programmer // trueObject.getPrototypeOf(jon) // Person {age: 18, code: ƒ}
jon.__proto__ // Person {age: 18, code: ƒ}//缺点:
// 无法向父类构造函数传参
// 父类的所有属性被共享,只要一个实例修改了属性,其他所有的子类实例都会被影响

借用构造函数继承

// 实现原理:在子类的构造函数内部,借用call() 或者 apply() 方法,调用超类型的构造函数
// 优点:相比于原型链继承而言,借用构造函数有一个很大的优势,就是子类型函数构造函数可以向超类型构造函数传递参数
// 缺点:方法都在构造函数内部定义,因此函数的复用性就无从谈起了function Animal(speices){
    this.speices = speices;
    //方法都在构造函数中定义,因此函数复用就无从谈起了
    this.write = function(){
        console.log(speices + "呜哈哈");
    }
}
​
//在超类型的原型中定义的方法,对子类型而言也是不可见的
Animal.prototype.say = function(){
    console.log("我好饿!");
}
​
function Dog(speices){
    Animal.call(this,speices);
}
​
var dog1 = new Dog("中华田园犬");
var dog2 = new Dog("德国牧羊犬");
dog1.write();//中华田园犬呜哈哈
dog2.write();//德国牧羊犬呜哈哈
​
dog1.say();//借用构造函数.html:65 Uncaught TypeError: dog1.say is not a function
dog2.say();

组合继承

// 把 原型继承 和 借用构造函数 合并在一起使用
// 优点:父类构造函数体内和原型上的内容都能继承;继承下来的属性放在自己身上;在一个位置传递所有参数
// 缺点:当给子类添加方法的时候,实际上是添加在了父类的实例身上// 父类
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayHi = function() {
    console.log('hello world');
}
​
function Student(gender, name, age) {
    this.gender = gender;
    
    //借用继承,目的:把属性继承在自己身上
    Person.call(this, name, age);
}
​
// 原型继承,目的:继承父类原型上的方法
Student.prototype = new Person();
​
// 书写 Student 自己的方法
Student.prototype.study = function() {
    console.log('study');
}
​
const s = new Student('男', 'Jack', 18);
console.log(s);

利用拷贝继承

// 利用 for in 循环的特点,来继承所有的内容
// 先实例化一个父类的实例
// 使用for in 循环遍历这个实例对象
// 直接把父类实例身上的所有内容直接复制到子类的 prototype
// 优点:父类构造函数体内的和原型上的都可以继承;constructor能正常配套;添加自己的方法的时候,能添加到自己原型身上
// 缺点:for in 循环需要一直遍历到 Object.prototype;不能继承 不可枚举 的属性;继承来的属性不在自己身上,在原型上// 父类
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayHi = function() {
    console.log('hello world');
}
​
function Student(gender, name, age) {
    this.gender = gender;
    
    // for in 继承
    const p = new Person(name, age);
    for( let key in p) {
        Student.prototype[key] = p[key];
    }
}
Student.prototype.study = function() {
    console.log('study');
}
​
const s = new Student('男', 'Jack', 18);
console.log(s);

寄生继承

// 实际上是一种伪继承
// 构造函数不要写return;return一个基本数据类型,写了白写;return一个复杂数据类型,构造函数没有意义
// 优点:原型和构造函数体内的都能继承下来;寄生原型,自己的属性和方法依旧可以添加和使用
// 寄生实例的时候,没有自己的任何内容;寄生原型的时候,一旦修改原型,父类的实例也会有这些方法// 父类
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayHi = function() {
    console.log('hello world');
}
​
function Student(gender) {
    this.gender = gender;
}
​
// 寄生原型
Student.prototype = Person.prototype;
​
Student.prototype.study = function() {
    console.log('study');
}
​
const s = new Student('男');
console.log(s);

寄生式组合继承

// 合并了 寄生继承 + 原型继承 + 独立第三方构造函数/**
  * 构造函数Person
  * @constructor
*/
function Person(name, pets) {
    this.name = name;
    this.pets = pets;
}
Person.prototype.run = function () {
    console.log('跑');
};
​
/**
  * 构造函数Student
  * @constructor
*/
function Student(num, name, pets) {
    // 注意: 一定要放在最前面
    Person.call(this, name, pets);   // ★借助构造函数继承call/apply★
    this.num = num;
}
​
// ★寄生式继承★
function Temp() {}  // 定义一个空类,用于寄生Person原型属性方法Temp.prototype = Person.prototype;  // 使Temp原型指向Person原型
var stuPrototype = new Temp();   // 实例化一个对象  原型指向Person原型Student.prototype = stuPrototype;  // 改变子类student的原型指向
stuPrototype.constructor = Student;  // 确保原型中的constructor指向Student构造函数var stu = new Student('001', '张三', ['小花']);
console.log(stu);

ES6类继承

// 使用 extends 关键字来实现继承
// class 子类类名 extends 父类 {}
// 在constructor里书写super();super(name,age) 等价于 Person.call(this,name,age)
// 如果要写自己的属性,必须写在 super 之后// 父类
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayHi = function() {
    console.log('hello world');
}
​
class Student extends Person {
    constructor(gender, name, age) {
        super(name, age);
        this.gender = gender;
    }
    
    study() {
        console.log('study');
    }
}
​
const s = new Student('男', 'Jack', 18);
console.log(s);