前段时间写了一篇关于原型的博客,好似打通了任督二脉,以前斩不断理还乱的各种继承方式突然看得明白了,赶紧记下来✍~~~
—— 以下是正文 ——
原型链继承
核心
将父类的实例作为子类的原型
// 创建SuperType的实例,并将该实例赋值给SubType.prototype
SubType.prototype = new SuperType()
// 所有涉及到原型链继承的继承方式都要修改子类构造函数的指向,否则子类实例的构造函数会指向SuperType。
SubType.prototype.constructor = SubType
举个例子
// 父类
function Person (name, age) {
this.name = name || 'Person'
this.age = age || 0
this.hobbies = ['music', 'reading']
}
// 为父类新增一个方法
Person.prototype.say = function () {
console.log('I am a person')
}
// 子类
function Student (name) {
this.name = name
this.score = 90
}
// 继承(注意,继承必须要写在子类方法定义的前面)
Student.prototype = new Person()
Student.prototype.constructor = Student
// 为子类新增一个方法(在继承之后,否则会被覆盖)
Student.prototype.study = function () {
console.log('I am studing')
}
var student = new Student('April')
console.log(student.name) // April --子类覆盖父类的属性
console.log(student.age) // 0 --父类的属性
console.log(student.score) // 90 --子类自己的属性
student.say() // I am a person --继承自父类的方法
student.study() // I am studing --子类自己的方法
存在的缺点
多个实例对引用类型的操作会被篡改
var stu1 = new Student()
var stu2 = new Student()
stu1.hobbies.push('basketball')
console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading,basketball
构造函数继承
核心
将父类构造函数的内容复制给了子类的构造函数
SuperType.call(SubType)
举个例子
// 父类
function Person (name, age) {
this.hobbies = ['music', 'reading']
}
Person.prototype.say = function() {console.log('I am a person')}
// 子类
function Student(){
Person.call(this)
}
var stu1 = new Student()
var stu2 = new Student()
stu1.hobbies.push('basketball')
console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading
stu1.say() // 报错
存在的缺点
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现复用,每个子类都有父类实例函数的副本,影响性能
组合继承
核心
用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承
function SubType(name, age){
SuperType.call(this)
}
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType
举个例子
// 父类
function Person() {
this.hobbies = ['music','reading']
}
// 父类函数
Person.prototype.say = function() {console.log('I am a person')}
// 子类
function Student(){
Person.call(this) // 构造函数继承(继承属性)
}
// 原型链继承(继承方法)
Student.prototype = new Person()
Student.prototype.constructor = Student
// 实例化
var stu1 = new Student()
var stu2 = new Student()
stu1.hobbies.push('basketball')
console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading
stu1.say() // I am a person
存在的缺点
在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法
原型式继承
核心
利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型
function object(obj){
function F(){}
F.prototype = obj
return new F()
}
或者用 ES 5
的 Object.create()
代替上面的 object 方法
举个例子
function object(obj){
function F(){}
F.prototype = obj
return new F()
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
}
var anotherPerson = object(person)
anotherPerson.name = "Greg"
anotherPerson.friends.push("Rob")
var yetAnotherPerson = object(person)
yetAnotherPerson.name = "Linda"
yetAnotherPerson.friends.push("Barbie")
console.log(person.friends) // Shelby,Court,Van,Rob,Barbie
存在的缺点
原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
无法传递参数
寄生式继承
核心
寄生式继承其实就是在原型式继承的基础上,做了一些增强
function createAnother(original){
// 通过调用 object() 函数创建一个新对象
var clone = object(original)
// 以某种方式来增强对象
clone.sayHi = function(){
console.log("hi")
}
// 返回这个对象
return clone // 返回这个对象
}
举个例子
function createAnother(original){
var clone = object(original)
clone.sayHi = function(){
console.log("hi")
}
return clone
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
}
var anotherPerson = createAnother(person)
anotherPerson.sayHi() // hi
存在的缺点
原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
无法传递参数
寄生组合式继承
核心
结合借用构造函数传递参数和寄生模式实现继承
function inheritPrototype(subType, superType){
var prototype = Object.create(superType.prototype) // 创建对象,创建父类原型的一个副本
prototype.constructor = subType // 增强对象,弥补因重写原型而失去的默认的constructor 属性
subType.prototype = prototype // 指定对象,将新创建的对象赋值给子类的原型
}
举个例子
function inheritPrototype(subType, superType){
var prototype = Object.create(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}
// 父类初始化实例属性和原型属性
function Person (name, age) {
this.name = name || 'Person'
this.age = age || 0
this.hobbies = ['music', 'reading']
}
Person.prototype.say = function () {
console.log('I am a person')
}
// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function Student(name){
Person.call(this, name)
}
// 将父类原型指向子类
inheritPrototype(Student, Person)
// 新增子类原型属性
Student.prototype.study = function () {
console.log('I am studing')
}
var stu1 = new Student("stu1")
var stu2 = new Student("stu2")
stu1.hobbies.push('basketball')
stu2.hobbies.push('swimming')
console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading,swimming
这个继承方案是目前最成熟的方案
ES6类继承extends
核心
class SubType extends SuperType {
constructor() {
super()
}
}
举个例子
class Person {
constructor(name, age) {
this.name = name || 'Person'
this.age = age || 0
this.hobbies = ['music', 'reading']
}
say() {
console.log('I am a person')
}
}
class Student extends Person {
constructor(name) {
super(name)
this.name = name
this.score = 90
}
study() {
console.log('I am studing')
}
}
var stu1 = new Student("stu1")
var stu2 = new Student("stu2")
stu1.hobbies.push('basketball')
stu2.hobbies.push('swimming')
console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading,swimming
参考文章: