原型链继承
- 利用原型让一个引用类型继承另一个引用类型的属性和方法
- 核心:原型链对象变成父类实例,子类就可以调用父类方法和属性(子类.prototype = new 父类())
function Parent() {
this.likeFood = ['水果', '鸡', '烤肉']
}
Parent.prototype.age = 18
Parent.prototype.getName = function () {
return this.name
}
function Child(name) {
this.name = name
}
Child.prototype = new Parent()
var child = new Child('leo')
// 这样子类就可以调用父类的属性和方法
console.log(child.getName()) // leo
console.log(child.age) // 18
Child.prototype.constructor = Child//修改constructor的指向
var chongqiChild = new Child('重庆孩子')
var guangdongChild = new Child('广东孩子')
// 重庆孩子还喜欢吃花椒,但是原型链继承会变成所有孩子喜欢的食物都增加了花椒
chongqiChild.likeFood.push('花椒')
console.log(chongqiChild.likeFood) // ["水果", "鸡", "烤肉", "花椒"]
console.log(guangdongChild.likeFood) // ["水果", "鸡", "烤肉", "花椒"]
- 缺点:在包含有引用类型的数据时,会被所有的实例对象所共享,容易造成修改的混乱;在创建子类型的时候不能向超类型传递参数
借用构造函数实现继承
- 子类构造函数内部调用父类构造函数,并传入this指针
// 2. 借用构造函数
function Parent(name) {
this.name = name
this.likeFood = ["水果", "鸡", "烤肉"]
}
function Child(name) {
Parent.call(this, name)
}
Parent.prototype.getName = function() {
return this.name
}
var chongqingChild = new Child('重庆孩子')
var guangdongChild = new Child('广东孩子')
chongqingChild.likeFood.push('花椒')
console.log(chongqingChild.likeFood) // ["水果", "鸡", "烤肉", "花椒"]
console.log(guangdongChild.likeFood) // ["水果", "鸡", "烤肉"]
console.log(chongqingChild.name) // "重庆孩子"
console.log(chongqingChild.getName()) // Uncaught TypeError: chongqingChild.getName is
- 优点:解决了不能向超类型传递参数的缺点
- 缺点:无法实现函数方法的复用,并且超类型原型定义的方法子类型也没有办法访问到
组合继承(JS经典继承方式)
- 结合原型链继承和借助构造函数方式,通过借用构造函数的方式来实现类型的属性的继承,通过将子类型的原型设置为超类型的实例来实现方法的继承
function Parent(name){
this.name = name
this.likeFood = ["水果", "鸡", "烤肉"]
}
function Child(name,age){
Parent.call(this,name)//借助构造函数实现继承
this.age = age
}
Parent.prototype.getName = function() {
return this.name
}
Child.prototype = new Parent() //原型链继承
Child.prototype.constructor = Child
Child.prototype.getAge = function(){
return this.age
}
var chongqingChild = new Child('重庆孩子', 18)
var guangdongChild = new Child('广东孩子', 19)
chongqingChild.likeFood.push('花椒')
console.log(chongqingChild.likeFood) // ["水果", "鸡", "烤肉", "花椒"]
console.log(guangdongChild.likeFood) // ["水果", "鸡", "烤肉"]
console.log(chongqingChild.name) // "重庆孩子"
console.log(chongqingChild.getName()) // "重庆孩子"
console.log(chongqingChild.getAge()) // 18
- 缺点:由于我们是以超类型的实例来作为子类型的原型,所以调用了两次超类的构造函数,造成了子类型的原型中多了很多不必要的属性
原型式继承
- 借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型
- 向函数中传入一个对象,然后返回一个以这个对象为原型的对象
- 多用于对象与对象之间
/*
@function 实现继承 函数
@param parent 充当父类的对象
*/
function realizeInheritance(parent) {
// 临时函数
function tempFunc() {}
tempFunc.prototype = parent
return new tempFunc()
}
// 这个就是已有的对象
var baba = {
name: "爸爸",
likeFoods: ["水果", "鸡", "烤肉"]
}
/*
var newChild = {} <==> baba 这两个对象建立关系就是这种继承的核心了。
*/
var child1 = realizeInheritance(baba)
var child2 = realizeInheritance(baba)
child1.likeFoods.push('花椒')
console.log(child1.likeFoods) // ["水果", "鸡", "烤肉", "花椒"]
console.log(child2.likeFoods) // ["水果", "鸡", "烤肉", "花椒"]
- ES5 新增了个 Object.create(parentObject) 函数来更加便捷的实现上述继承
var baba = {
name: "爸爸",
likeFoods: ["水果", "鸡", "烤肉"]
}
var child1 = Object.create(baba)
var child2 = Object.create(baba)
child1.likeFoods.push('花椒')
console.log(child1.likeFoods) // ["水果", "鸡", "烤肉", "花椒"]
console.log(child2.likeFoods) // ["水果", "鸡", "烤肉", "花椒"]
寄生式继承
- 创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对象进行扩展,最后返回这个对象
function realizeInheritance(parent) {
// 临时函数
function tempFunc() {}
tempFunc.prototype = parent
return new tempFunc()
}
// Parasitic: 寄生的 inheritance: 继承 一个最简单的工厂函数。
function parasiticInheritance(object) {
var clone = realizeInheritance(object)
// 这是用了原型式继承,但是只要是任何可以返回对象的方法都可以
clone.sayName = function() {
console.log('我是'+this.name)
}
return clone
}
var baba = {
name: "爸爸",
likeFoods: ["水果", "鸡", "烤肉"]
}
var child = parasiticInheritance(baba)
child.name = '儿子'
child.sayName() // 我是儿子
- 优点就是对一个简单对象实现继承,如果这个对象不是自定义类型时
- 缺点是没有办法实现函数的复用
寄生组合式继承
function Parent(name) {
this.name = name
this.likeFood = ["水果", "鸡", "烤肉"]
}
function Child(name, age) {
Parent.call(this, name)
this.age = age
}
Parent.prototype.getName = function() {
return this.name
}
// Child.prototype = new Parent() 使用新方法解决
// Child.prototype.constructor = Child
inheritPrototype(Child, Parent)
function inheritPrototype(childFunc, parentFunc) {
var prototype = realizeInheritance(parentFunc.prototype) //创建对象,我们继续使用原型式继承的创建
prototype.constructor = childFunc //增强对象
childFunc.prototype = prototype //指定对象
}
function realizeInheritance(parent) {
// 临时函数
function tempFunc() {}
tempFunc.prototype = parent
return new tempFunc()
}
Child.prototype.getAge = function() {
return this.age
}
var chongqingChild = new Child('重庆孩子', 18)
var guangdongChild = new Child('广东孩子', 19)
chongqingChild.likeFood.push('花椒')
console.log(chongqingChild.likeFood) // ["水果", "鸡", "烤肉", "花椒"]
console.log(guangdongChild.likeFood) // ["水果", "鸡", "烤肉"]
console.log(chongqingChild.name) // "重庆孩子"
console.log(chongqingChild.getName()) // "重庆孩子"
console.log(chongqingChild.getAge()) // 18
- 优点:组合继承+寄生式继承
- 缺点在于调用两次父类构造函数,子类原型有冗余属性,寄生式继承的特性规避了这类情况,集寄生式继承和组合继承的优点与一身,是实现基于类型继承的最有效方式
---------------------------------------------------------------------------2024.5.18每日一题