原型链继承
第一步:定义父类型构造函数,给父类型的原型添加方法
第二步:定义子类型的构造函数,创建父类型的对象赋值给子类型的原型
第三步: 将子类型原型的构造属性设置为子类型,给子类型原型添加方法
第四步: 创建子类型的对象: 可以调用父类型的方法
关键点:子类型的原型为父类型的一个实例对象,并且修正子类的构造函数式本身
// 父类型
function Super() {
this.superProp = 'The super prop'
}
// 原型的数据所有的实例对象都可见
Super.prototype.showSuperProp = function () {
console.log(this.superProp)
}
// 子类型
function Sub() {
this.subProp = 'The sub prop'
}
// 子类的原型为父类的实例
Sub.prototype = new Super()
// 修正Sub.prototype.constructor为Sub本身
Sub.prototype.constructor = Sub
Sub.prototype.showSubProp = function () {
console.log(this.subProp)
}
// 创建子类型的实例
var sub = new Sub()
// 调用子类型的方法
sub.showSubProp()
// 调用父类型的方法
sub.showSuperProp()
这种方式的继承,当我创建多个子类对象的时候,修改父类中的继承过来的属性(复合类型的属性),当子类实例对象修改属性值的时候,所有的子类实例对象的这个属性值都会跟着改变
因为这个属性是子类实例对象的原型上的
function Supper() { //父类型
this.superProp = 'The super prop'
this.play = [1, 2, 3]
}
// 原型的数据所有的实例对象都可见
Supper.prototype.showSupperProp = function () {
console.log(this.superProp)
}
function Sub() { //子类型
this.subProp = 'The sub prop'
}
// 子类的原型为父类的实例
Sub.prototype = new Supper()
// 修正Sub.prototype.constructor为Sub本身
Sub.prototype.constructor = Sub
Sub.prototype.showSubProp = function () {
console.log(this.subProp)
}
// 创建子类型的实例
var sub1 = new Sub()
var sub2 = new Sub()
console.log(sub1.play)
console.log(sub2.play)
console.log(sub1.superProp)
console.log(sub2.superProp)
sub1.play.push(4)
sub1.superProp = 'superProp...........' // 其实是在自己实例对象上新增了一个属性:superProp,原型上的这个属性还是原来那个
console.log(sub1.play)
console.log(sub2.play)
console.log(sub1.superProp)
console.log(sub2.superProp)
console.log('-------------------------------')
console.log(sub1)
console.log(sub1.__proto__)
console.log(sub2)
console.log(sub2.__proto__)
[ 1, 2, 3 ]
[ 1, 2, 3 ]
The super prop
The super prop
[ 1, 2, 3, 4 ]
[ 1, 2, 3, 4 ]
superProp...........
The super prop
-------------------------------
Sub { subProp: 'The sub prop', superProp: 'superProp...........' }
Sub {
superProp: 'The super prop',
play: [ 1, 2, 3, 4 ],
constructor: [Function: Sub],
showSubProp: [Function] }
Sub { subProp: 'The sub prop' }
Sub {
superProp: 'The super prop',
play: [ 1, 2, 3, 4 ],
constructor: [Function: Sub],
showSubProp: [Function] }
借用构造函数继承(假的)
第一步:定义父类型构造函数
第二步:定义子类型构造函数
第三步:在子类型构造函数中调用父类型构造
关键: 在子类型构造函数中通过call()调用父类型构造函数
function Person(name, age) {
this.name = name
this.age = age
}
function Student(name, age, price) {
// 这种方式其实就是改变了父级的this指向,也就是将this指向了Student这个类 父级执行的时候属性都会挂载到子类实例中
// 将父类构造函数的this指向 子类构造函数的实例上去
Person.call(this, name, age) // 使用call或者apply,相当于是this.Person(name, age)
this.price = price
}
var s = new Student('Tom', 20, 12000)
console.log(s.name, s.age, s.price)
这种方式的缺点是父类中的原型上的属性和方法不能被继承过来
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sn = '12345'
Person.prototype.say = function () {
console.log(this.name)
}
function Student(name, age, price) {
// 这种方式其实就是改变了父级的this指向,也就是将this指向了Student这个类 父级执行的时候属性都会挂载到子类实例中
// 将父类构造函数的this指向 子类构造函数的实例上去
Person.call(this, name, age) // 使用call或者apply,相当于是this.Person(name, age)
this.price = price
}
var s = new Student('Tom', 20, 12000)
console.log(s.name, s.age, s.price)
console.log(s)
结果可以看到,在Student的原型对象上,并没有将say方法和sn属性继承过来,所以说这种方式只是实现了部分继承,如果属性和方法都在父类的构造函数中,那么都可以继承过来
但继承不了父类原型上的属性和方法
原型链+借用构造函数的组合继承
利用原型链实现对父类型对象的方法继承
利用call()借用父类型构建函数初始化相同属性
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.setName = function (name) {
this.name = name
}
function Student(name, age, price) {
Person.call(this, name, age) // 得到父类型的属性
this.price = price
}
Student.prototype = new Person() // 得到父类型的方法
Student.prototype.constructor = Student
Student.prototype.setPrice = function (price) {
this.price = price
}
var s = new Student('Tom', 12, 10000)
s.setPrice(11000)
s.setName('Bob')
console.log(s)
console.log(s.__proto__) // Student的原型对象
console.log(s.__proto__.__proto__) // Person的实例对象
console.log(s.__proto__.__proto__.__proto__) // Object的实例对象
console.log(s.__proto__.__proto__.__proto__.__proto__) // Object的原型对象是null
console.log(Student.prototype)
console.log(s.constructor)
Student { name: 'Bob', age: 12, price: 11000 }
Student {
name: undefined,
age: undefined,
constructor: [Function: Student],
setPrice: [Function] }
Person { setName: [Function] }
{}
null
Student {
name: undefined,
age: undefined,
constructor: [Function: Student],
setPrice: [Function] }
[Function: Student]
这种继承方式的缺点是每创建一个对象,父类的构造函数都会执行两次
function Person(name, age) {
this.name = name
this.age = age
this.paly = [1, 2, 3]
console.log('Person constructor......')
}
Person.prototype.setName = function (name) {
this.name = name
}
function Student(name, age, price) {
Person.call(this, name, age) // 得到父类型的属性
this.price = price
}
Student.prototype = new Person() // 得到父类型的方法
Student.prototype.constructor = Student
Student.prototype.setPrice = function (price) {
this.price = price
}
var s1 = new Student('Tom', 12, 10000)
var s2 = new Student('Tom', 12, 10000)
s1.paly.push(4)
console.log(s1)
console.log(s2)
Person constructor......
Person constructor......
Person constructor......
Student { name: 'Tom', age: 12, paly: [ 1, 2, 3, 4 ], price: 10000 }
Student { name: 'Tom', age: 12, paly: [ 1, 2, 3 ], price: 10000 }
优化组合继承方式
我们使用call这一步就已经继承了父类构造函数中的所有属性和方法,那么如果要继承父类中的原型方法和属性,那让子类原型指向父类原型即可,这样父类的构造函数就不会执行两次了
需要注意的是一定要让子类的原型上的构造函数重新指向本身构造函数
Student.prototype.constructor = Student
function Person(name, age) {
this.name = name
this.age = age
this.paly = [1, 2, 3]
}
Person.prototype.setName = function (name) {
this.name = name
}
function Student(name, age, price) {
Person.call(this, name, age) // 得到父类型的属性
this.price = price
}
Student.prototype = Person.prototype
Student.prototype.constructor = Student
Student.prototype.setPrice = function (price) {
this.price = price
}
var s1 = new Student('Tom', 12, 10000)
var s2 = new Student('Tom', 12, 10000)
s1.paly.push(4)
console.log(s1)
console.log(s2)
function Person(name, age) {
this.name = name
this.age = age
this.paly = [1, 2, 3]
}
Person.prototype.setName = function (name) {
this.name = name
}
function Student(name, age, price) {
Person.call(this, name, age) //得到父类型的属性
this.price = price
}
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
Student.prototype.setPrice = function (price) {
this.price = price
}
var s1 = new Student('Tom', 12, 10000)
// 因为s1对象的原型对象是同一个
console.log(s1 instanceof Student) // true
console.log(s1 instanceof Person) // true
console.log(s1.constructor)