JS面向对象
类的声明
使用构造函数或Class
关键字
继承的几种方式
// 第1种:通过执行父类构造函数把父类的属性挂载到子类上
// 缺点:Child无法继承Parent原型链上面的属性和方法
function Parent1() {
this.name = 'parent1'
}
function Child1() {
Parent1.call(this) // 这一步是关键原理
this.subName = 'child1'
}
// 第2种:借助原型链实现继承
// 缺点:如果改变父类实例的对象属性,则所有子类实例上的该属性都会被改变
function Parent2() {
this.name = 'parent2'
this.arr = [1,2]
}
function Child2() {
this.subName = 'child2'
}
Child2.prototype = new Parent2() // 把子类的原型对象指向父类的实力对象
// 缺点
var s1 = new Child2()
var s2 = new Child2()
// 改变父类上的对象属性,s
// 这样也会改变s2的name属性,因为是直接改变了Parent2的实例对象,因为s1,s2共用了同一个原型对象
s1.name = 'new name'
console.log(s1.name) // 'new name'
console.log(s2.name) // 'new name'
// 第3种:方案1+方案2结合,解决了前两种方案的缺点
// 缺点:Parent3实例化执行了两次,第一次Parent3.call(this),第2次new Parent3()
function Parent3() {
this.name = 'parent3'
}
function Child3() {
Parent3.call(this)
this.subName = 'child3'
}
Child3.prototype = new Parent3()
// 第4种:优化第3种
// 缺点:修改了Child4的constructor,不能判断s4是Child4直接构造出来的
function Parent4() {
this.name = 'parent4'
}
function Child4() {
Parent4.call(this)
this.subName = 'child4'
}
Child4.prototype = Parent4.prototype
var s4 = new Child4()
console.log(s4.constructor === Parent4) // true
console.log(s4.constructor === Child4) // false
// 第5种:最优方案
function Parent5() {
this.name = 'parent5'
}
function Child5() {
Parent5.call(this)
this.subName = 'child5'
}
Child5.prototype = Object.create(Parent5.prototype)
// Object.create等价于下面的代码
// var obj = {}
// obj.__proto__ = Parent5.prototype
// Child5.prototype = obj
Child5.prototype.constructor = Child5