Twlig / issuesBlog

MIT License
3 stars 0 forks source link

继承 #27

Open Twlig opened 2 years ago

Twlig commented 2 years ago

原型继承

子类原型赋值为父类实例,通过这种办法继承父类的原型方法。

function Person() {
    this.name = "Amy";  //实例属性name
}
Person.prototype.age = 22;
Person.prototype.getName = function() {
    return this.name;
}
function Student() {
    this.grade = 1;
}
Student.prototype = new Person();  //关键代码!,此处已经new了,所以name是Person实例属性,不是Amy的
Student.prototype.getGrade = function() {
    return this.grade;
}
var Amy = new Student();
Amy.getName();
Amy.getGrade();

image

Amy实例的结构如上图。

存在的问题:

盗用构造函数(经典继承)

在子类构造函数中调用父类构造函数

function SuperType(name){ 
 this.name = name; 
} 
function SubType(name) { 
 // 继承 SuperType 并传参
 SuperType.call(this, name);   //调用call,把this对象指向SubType实例。
 // 实例属性
 this.age = 29; 
} 
let instance = new SubType("Nicholas"); 
console.log(instance.name); // "Nicholas"; 
console.log(instance.age); // 29

name和age都成为了instance实例属性,因此就不存在不同对象共享相同属性的问题。

存在的问题:

组合继承(经典伪继承)

综合原型链和盗用构造函数,将两者的优点集中了起来。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.getMsg = function() {
    console.log("My name is " + name + ", I am " + age + "years old.");
}
function Student(grade, name, age) {
    Person.call(this, name, age);
    this.grade = grade;
}
Student.prototype = new Person();
Student.prototype.sayGrade = function() {
    console.log("My grade is " + this.grade);
}
var Amy = new Student(1, "Amy", 8);  //Student {name: 'Amy', age: 8, grade: 1}
var LiLei = new Student(3, "LiLei", 10);  //Student {name: 'LiLei', age: 10, grade: 3}

存在问题:

原型式继承(Object.create)

经典原型式继承

function createNew(o) {
    function F(){};
    F.prototype = o;
    return new F();
}
let person = { 
     name: "Nicholas", 
     friends: ["Shelby", "Court", "Van"] 
}; 
let newObj = createNew(person);

Object.create()方法封装了上述操作,使用现有对象作为新创建对象的原型

这个方法接收两个参数:

let person = { 
     name: "Nicholas", 
     friends: ["Shelby", "Court", "Van"] 
}; 
let anotherPerson = Object.create(person);

//等价于 
function F(){};
F.prototype = person;
anotherPerson = new F();

//第二个参数
let Greg = Object.create(person, { 
     name: { 
         value: "Greg" 
     },
    age: {
        value: 22
    }
});

寄生式继承

和原型式继承特别像,只是在原型式继承的基础上,给新对象添加了其他属性或者方法(和带第二个参数的Object.creat()函数很像)。

function createAnother(original){ 
 let clone = object(original); // 通过调用函数创建一个新对象
 clone.sayHi = function() { // 以某种方式增强这个对象
 console.log("hi"); 
 }; 
 return clone; // 返回这个对象
}

寄生式组合继承

组合继承中,父类构造函数始终会被调用两次:一次是在子类构造函数中调用,一次在是创建子类原型时调用。

function object(o) {  //原型式继承
    function F(){};
    F.prototype = o;
    return new F();
}

function inheritPrototype(subType, superType) {  //寄生式继承
 let prototype = object(superType.prototype); // 创建对象
 prototype.constructor = subType; // 增强对象 
 subType.prototype = prototype; // 赋值对象
}

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.getMsg = function() {
    console.log("My name is " + name + ", I am " + age + "years old.");
}
function Student(grade, name, age) {
    Person.call(this, name, age);
    this.grade = grade;
}
inheritPrototype(Student, Person);  //原本是 Student.prototype = new Person();
Student.prototype.sayGrade = function() {
    console.log("My grade is " + this.grade);
}
var Amy = new Student(1, "Amy", 8);  //Student {name: 'Amy', age: 8, grade: 1}
var LiLei = new Student(3, "LiLei", 10);  //Student {name: 'LiLei', age: 10, grade: 3}

⚠️注意

想过为啥不能直接把父类的原型赋值给子类,后面试了一下:

function inheritPrototype(subType, superType) { 
 subType.prototype = superType.prototype; // 赋值对象
}

如果像这样的话就没有子类和父类的原型链层级了,就相当于原型链是:实例隐式原型(Person) ——》Person隐式原型 (Object)——》Object隐式原型(null)

而实际原型链应该是:实例隐式原型(Student) ——》Student隐式原型 (Person)——》Person隐式原型(Object)——》Object隐式原型(null)

所以,必须创建一个中间对象o,使得o的prototype等于父类的prototype。然后让创建o的实例,让子类的prototype等于o的实例。就相当于把父类的prototype提取出来,创建一个空对象来包裹它。也就是相当于提炼吧,本身的父类有很多杂质不需要。保留需要的prototype,再用空对象去包裹它。