# 继承
# 1️⃣ 原型链继承
function Parent () {
this.name = 'Daren'
this.skills = ['Driving', 'Cooking']
}
Parent.prototype.getName = function () {
return this.name
}
function Child () {}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var ming = new Child()
ming.name = 'Ming'
ming.skills.push('Shopping')
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]
var tian = new Child()
tian.name = 'Tian'
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking", "Shopping"]
存在的问题:
- 父类引用类型属性被所有子类实例共享
- 在创建子类实例时,不能像父类传参
# 2️⃣ 借用构造函数(经典继承)
function Parent (name) {
this.name = name
this.skills = ['Driving', 'Cooking']
this.getName = function () {
return this.name
}
}
function Child (name) {
Parent.call(this, name)
}
var ming = new Child('Ming')
ming.skills.push('Shopping')
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]
var tian = new Child('Tian')
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking"]
优点:
- 避免父类应用类型被子类实例共享
- 在创建子实例时,可以向父类传参
缺点:方法都在父类构造函数中定义,每次创建子类实例都会创建一遍方法。
# 3️⃣ 组合继承(原型链继承+经典继承)
function Parent (name) {
this.name = name
this.skills = ['Driving', 'Cooking']
}
Parent.prototype.getName = function () {
return this.name
}
function Child (name, age) {
Parent.call(this, name)
this.age = age
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
var ming = new Child('Ming', 24)
ming.skills.push('Shopping')
console.log(ming.name, ming.age) // "Ming" 24
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]
var tian = new Child('Tian', 28)
console.log(tian.name, tian.age) // "Tian" 24
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking"]
# 4️⃣ 原型式继承
function createInstance(o) {
function F(){}
F.prototype = o;
return new F();
}
var person = {
name: 'Ming',
skills: ['Driving', 'Cooking']
}
var ming = createInstance(person)
ming.skills.push('Shopping')
console.log(ming.name) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]
var tian = createInstance(person)
tian.name = 'Tian'
console.log(tian.name) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking", "Shopping"]
缺点:如果父类包含引用类型的属性值则会被子类共享,这点与原型链继承一样的。
# 5️⃣ 寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createInstance (o) {
var clone = Object.create(o);
clone.getName = function () {
return this.name
}
return clone;
}
var person = {
name: 'Ming',
skills: ['Driving', 'Cooking']
}
var ming = createInstance(person)
ming.skills.push('Shopping')
console.log(ming.name) // "Ming"
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]
var tian = createInstance(person)
tian.name = 'Tian'
console.log(tian.name) // "Tian"
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking", "Shopping"]
# 6️⃣ 寄生组合式继承
function Parent (name) {
this.name = name;
this.skills = ['Driving', 'Cooking'];
}
Parent.prototype.getName = function () {
return this.name
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child
var ming = new Child('Ming', 24)
ming.skills.push('Shopping')
console.log(ming.name, ming.age) // "Ming" 24
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]
var tian = new Child('Tian', 25)
console.log(tian.name, tian.age) // "Tian" 25
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking"]
封装一下:
function createInstance(o) {
function F() {}
F.prototype = o;
return new F();
}
function extend(child, parent) {
var prototype = createInstance(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
// 当我们使用的时候:
extend(Child, Parent);
function Parent (name) {
this.name = name;
this.skills = ['Driving', 'Cooking'];
}
Parent.prototype.getName = function () {
return this.name
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
这种方式的高效率体现它只调用了一次父类构造函数,并且因此避免了在 父类原型 Parent.prototype
上面创建不必要的、多余的属性。与此同时,原型链还能保持不变,因此,能够正常使用 instanceof
和 isPrototypeOf
。所以寄生组合式继承是引用类型最理想的继承范式。