JavaScript面向对象之组合-寄生式继承实现
前言
组合-寄生式继承是javascript对象继承中比较好的方案,它是在一系列继承方案改进的结果
原型链继承
// 父类: 公共属性和方法
function Person() {
this.name = "kun"
this.friends = []
}
Person.prototype.eating = function() {
console.log(this.name + " eating")
}
// 子类: 特有属性和方法
function Student() {
this.sno = 111
}
//继承Student原型
Student.prototype = new Person()
//在student原型上,也就是Person构造函数new出的对象上添加studying方法
Student.prototype.studying = function() {
console.log(this.name + " studying")
}
var stu = new Student()
obj.keys(stu) //无法遍历name
stu.name //kun
stu.studying() //kun studying
stu.eating() // kun eating
该方案实现了原型继承,但也有以下弊端
stu
对象无法遍历原型上的属性name
- 多个由
Student
创建的对象将共享name
Student
构造函数无法传递参数给name
借用构造函数继承(组合继承)
// 父类: 公共属性和方法
function Person(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
Person.prototype.eating = function() {
console.log(this.name + " eating")
}
// 子类: 特有属性和方法
function Student(name, age, friends, sno) {
//call将this传入Person构造函数中并调用,在Student构造函数存入父类属性
Person.call(this, name, age, friends)
//Student特有的属性
this.sno = 111
}
//继承Student原型,但此时会将无用属性name\age\friends赋给Student原型
Student.prototype = new Person()
//在Student原型上,也就是Person构造函数new出的对象上添加studying方法
Student.prototype.studying = function() {
console.log(this.name + " studying")
}
借用构造函数继承的方案解决了原型链继承中的诸多缺陷,比如借用构造函数继承中继承的属性可以遍历,创建多个子类 属性不共享,可以传递参数等。
该方案解决了原型继承的一些弊端,但也有以下缺陷
- 此时会将父类中无用属性(name\age\friends)赋给子类原型
- 父类至少要调用两次
寄生式继承
var personObj = {
running: function() {
console.log("running")
}
}
//工厂函数
function createStudent(name) {
//stu将其原型寄生在了personObj上 以此来实现继承
var stu = Object.create(personObj)
stu.name = name
stu.studying = function() {
console.log("studying~")
}
return stu
}
组合-寄生式继承
//Object.create()的 polyfill
function createObject(o) {
function Fn() {}
Fn.prototype = o
return new Fn()
}
//原型继承方法
function inheritPrototype(SubType, SuperType) {
//创建一个proto指向父类原型的对象赋给子类原型
SubType.prototype = Object.create(SuperType.prototype)
//将子类原型的原本指向父类的constructor指向自己
Object.defineProperty(SubType.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: SubType
})
}
//父类构造函数
function Person(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
Person.prototype.eating = function() {
console.log(this.name + " eating")
}
//子类构造函数
function Student(name, age, friends, sno, score) {
Person.call(this, name, age, friends)
this.sno = sno
this.score = score
}
//原型继承
inheritPrototype(Student, Person)
Student.prototype.studying = function() {
console.log("studying")
}
var stu = new Student("kunkun", 18, ["kobe"], 111, 55)
stu.studying()
stu.eating()
console.log(stu.constructor.name)
该方案关键使用了Object.create()
方法,传入父类原型后返回一个proto指向该原型的对象作为子类的原型,在创建子类对象时,执行Person.call()
,将父类构造函数的属性也传入了子类。