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);
原型继承
子类原型赋值为父类实例,通过这种办法继承父类的原型方法。
Amy实例的结构如上图。
Amy实例
grade实例属性。
隐式原型。
new Student()
Amy实例产生grade实例属性
Amy实例的隐式原型被赋值为构造函数Student的显式原型(Student.prototype属性)
Student.prototype = new Person()
存在的问题:
原型属性是所有实例共享的,但实际上属性应该是每个实例独立的才合理,除非是常量。因此通常会在构造函数中定义而不会定义在原型上。
子类型在实例化时不能给父类型的构造函数传参。
因此,原型链的继承方式更适合继承方法。方法大家一起共享是很合理的。
盗用构造函数(经典继承)
在子类构造函数中调用父类构造函数。
name和age都成为了instance实例属性,因此就不存在不同对象共享相同属性的问题。
存在的问题:
组合继承(经典伪继承)
综合原型链和盗用构造函数,将两者的优点集中了起来。
使用原型链继承原型上的属性和方法 (目的:访问父类原型)
通过盗用构造函数继承实例属性 (目的:每个实例属性独立)
存在问题:
存在效率问题。最主要的效率问题就是父类构造函数始终会被调用两次
原型式继承(Object.create)
经典原型式继承
Object.create()方法封装了上述操作,使用现有对象作为新创建对象的原型。
这个方法接收两个参数:
现有对象。新对象 隐式原型上的属性和方法。
给新对象定义额外属性的对象(第二个可选)。新对象实例属性和方法。
Object.create()的第二个参数与 Object.defineProperties()的第二个参数一样:每个新增属性都通过各自的描述符来描述。
寄生式继承
和原型式继承特别像,只是在原型式继承的基础上,给新对象添加了其他属性或者方法(和带第二个参数的Object.creat()函数很像)。
寄生式组合继承
组合继承中,父类构造函数始终会被调用两次:一次是在子类构造函数中调用,一次在是创建子类原型时调用。
在子类构造函数中调用已经把每个实例所需的父类实例的属性和方法创建成当前实例的属性和方法了。
如果在创建子类原型时再调用父类构造函数,就会造成子类原型上还有父类实例的属性和方法。
而我们只是需要父类原型上的方法和属性而已,所以在创建子类原型时不需要new一个父类,直接继承原型更高效。
⚠️注意:
想过为啥不能直接把父类的原型赋值给子类,后面试了一下:
如果像这样的话就没有子类和父类的原型链层级了,就相当于原型链是:实例隐式原型(Person) ——》Person隐式原型 (Object)——》Object隐式原型(null)
而实际原型链应该是:实例隐式原型(Student) ——》Student隐式原型 (Person)——》Person隐式原型(Object)——》Object隐式原型(null)
所以,必须创建一个中间对象o,使得o的prototype等于父类的prototype。然后让创建o的实例,让子类的prototype等于o的实例。就相当于把父类的prototype提取出来,创建一个空对象来包裹它。也就是相当于提炼吧,本身的父类有很多杂质不需要。保留需要的prototype,再用空对象去包裹它。