@15013890200
2019-07-05T11:28:36.000000Z
字数 5038
阅读 483
继承
原型
原生js
js实现继承的方式有多种,每种方式都有自己适合的场景,也都有各自的优缺点
本文主要介绍7种继承实现方式(原型链继承、借用构造函数、组合方式、原型式继承、寄生式继承、寄生组合式继承、class实现),并附上例子
;(() => {
/**
* 原型链继承
* 原型会变成另一个类型的实例
* 该原型的引用类型属性会被所有的实例共享
* 创建子类型实例的时候,没有办法在不影响所有对象实例的情况下给超类的构造函数中传递参数
*/
console.log('----------原型链继承----------')
function Person () {
this.name = 'person'
this.hobby= ['game', 'sport']
}
Person.prototype.getName = function () {
console.log(this.name)
}
Person.prototype.getHobby = function () {
console.log(this.hobby)
}
let p1 = new Person()
function Chinese () {
this.country = 'china'
}
Chinese.prototype = new Person()
Chinese.prototype.constructor = Chinese
Chinese.prototype.getCountry = function () {
console.log(this.country)
}
let p2 = new Chinese()
let p3 = new Chinese()
p3.getHobby()
p2.getHobby()
p2.hobby.push('swim')
p2.getHobby()
p3.getHobby() // ['game', 'sport', 'swim']
p1.getHobby() // ['game', 'sport']
console.log(p2 instanceof Person) // true
console.log(p2 instanceof Chinese) // true
console.log(p2 instanceof Object) // true
console.log('--------------------')
})();
;(() => {
/**
* 借用构造函数继承
* 可以向超类传递参数
* 解决了原型中包含引用类型值被共享的问题
* 超类型的原型对于子类型是不可见的
*/
console.log('----------构造函数继承----------')
function Person (name) {
this.name = name
this.hobby= ['game', 'sport']
}
Person.prototype.getName = function () {
console.log(this.name)
}
Person.prototype.getHobby = function () {
console.log(this.hobby)
}
let p1 = new Person('p1')
function Chinese (name) {
Person.call(this, name)
this.country = 'china'
}
Chinese.prototype.getCountry = function () {
console.log(this.country)
}
let p2 = new Chinese('p2')
let p3 = new Chinese('p3')
// p2.getHobby() 不可用
p2.getCountry()
console.log(p1.name) // p1
console.log(p2.name) // p2
console.log(p2.hobby)
console.log(p3.hobby)
p2.hobby.push('swim')
console.log(p2.hobby)
console.log(p3.hobby) // ['game', 'sport']
console.log('--------------------')
})();
;(() => {
/**
* 构造函数 + 原型链(组合继承)
* 任何情况下,都会调用两次超类型构造函数
* 可以向超类传递参数
* 每个实例都有自己的属性
* 实现了函数复用
*/
console.log('----------组合式继承----------')
function Person (name) {
this.name = name
this.hobby= ['game', 'sport']
}
Person.prototype.getName = function () {
console.log(this.name)
}
Person.prototype.getHobby = function () {
console.log(this.hobby)
}
let p1 = new Person()
function Chinese (name, country) {
Person.call(this, name) // 1次
this.country = country
}
Chinese.prototype = new Person() // 2次
Chinese.prototype.constructor = Chinese
Chinese.prototype.getCountry = function () {
console.log(this.country)
}
let p2 = new Chinese('p2', 'anhui')
let p3 = new Chinese('p3', 'guangdong')
// p2.getHobby() 不可用
p2.getCountry() // anhui
p2.getName() // p2
p3.getCountry() // guangdong
console.log(p2.hobby)
console.log(p3.hobby)
p2.hobby.push('swim')
console.log(p2.hobby)
console.log(p3.hobby) // ['game', 'sport']
console.log('--------------------')
})();
;(() => {
/**
* 原型式继承
* 适用于没有必要创建构造函数,仅让一个对象与另一个对象保持相似
* 同原型链实现继承一样,引用类型值会被所有实例共享
*/
console.log('----------原型式继承----------')
let person = {
name: 'person',
hobby: ['game', 'sport']
}
let p1 = Object.create(person)
// es5之后通过Object.create(o),之前如下
// function create (o) {
// function F () {}
// F.prototype = o
// return new F()
// }
p1.name = 'p1'
p1.hobby.push('swim')
console.log(p1.hobby)
let p2 = Object.create(person)
p2.name = 'p2'
console.log(p2.hobby)
p2.hobby.push('sing')
console.log(p1.name)
console.log(p2.name)
console.log(p1.hobby)
console.log(p2.hobby)
console.log('--------------------')
})();
;(() => {
/**
* 寄生式继承
* 创建一个仅用于封装过程的函数,在内部来增强对象
* 适用于对象不是自定义类型和构造函数的情况下
* 不能做到函数复用而效率低下
* 与原型链原型式继承一样,引用类型值会被共享
*/
// function create (o) {
// function F () {}
// F.prototype = o
// return new F()
// }
console.log('----------寄生式继承----------')
function Person (p) {
let clone = Object.create(p)
clone.getName = function () {
console.log('hi')
}
return clone
}
var p1 = {
name: 'p1',
hobby: ['game', 'sport']
}
let p2 = Person(p1)
p2.getName() // hi
console.log(p1)
console.log(p2)
console.log(p2.name) // p1
console.log('--------------------')
})();
;(() => {
/**
* 寄生组合式继承
* 不必为了指定子类型的原型而调用超类型的构造函数,只调用了一次超类构造函数,效率更高
* 原型链保持一致
* 最理想的继承范式
*/
console.log('----------寄生组合式继承----------')
function inherit (subType, superType) {
let prototype = Object.create(superType.prototype) // 拷贝超类对象原型
prototype.constructor = subType // 指定构造对象
subType.prototype = prototype // 挂载原型
}
function SuperType (name) {
this.name = name
this.hobby = ['game', 'sport']
}
SuperType.prototype.getName = function () {
console.log(this.name)
}
SuperType.prototype.getHobby = function () {
console.log(this.hobby)
}
function SubType (name, country) {
SuperType.call(this, name)
this.country = country
}
SubType.prototype.getCountry = function () {
console.log(this.country)
}
inherit(SubType, SuperType)
let s1 = new SuperType('s1')
let s2 = new SubType('s2', 'anhui')
let s3 = new SubType('s3', 'guangdong')
console.log(s1)
console.log(s2)
console.log(s3)
s2.hobby.push('swim')
s2.getHobby()
s3.getHobby()
s2.getName()
console.log('--------------------')
})();
;(() => {
/**
* class(es6)实现继承
* 语法糖,本质上还是通过原型实现继承
*/
console.log('----------class继承----------')
class SuperType {
constructor (name) {
this.name = name
this.hobby = ['game', 'sport']
}
getName () {
console.log(this.name)
}
getHobby () {
console.log(this.hobby)
}
}
class SubType extends SuperType {
constructor(name, country) {
super(name)
this.country = country
}
getCountry () {
console.log(this.country)
}
}
let s1 = new SuperType('s1')
let s2 = new SubType('s2', 'anhui')
let s3 = new SubType('s3', 'guangdong')
console.log(s1)
console.log(s2)
console.log(s3)
s2.hobby.push('swim')
s2.getHobby()
s3.getHobby()
s3.getName()
s2.getCountry()
})();