面向对象(三)
2020-09-08 本文已影响0人
懂会悟
1、构造函数、原型和实例的关系
- 每个构造函数都有一个原型对象, 通过prototype指针指向该原型对象.
- 原型对象都包含一个指向构造函数的指针, 通过constructor指针, 指向构造函数
- 而实例都包含一个指向原型对象的内部指针, 该内部指针我们通常使用proto来描述.
2、原型链
- 我们可以通过Person.prototype = {}的方式来重写原型对象.
- 假如, 我们后面赋值的不是一个{}, 而是另外一个类型的实例, 结果会是怎么样呢?
- 显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型对象中也包含着一个指向另一个构造函数的指针。
- 假如另一个原型对象又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型对象的链条。这就是所谓原型链的基本概念。
// 1.创建Animal的构造函数
function Animal() {
this.animalProperty = "Animal"
}
// 2.给Animal的原型中添加一个方法
Animal.prototype.animalFunction = function () {
console.log(this.animalProperty)
}
// 3.创建Person的构造函数
function Person() {
this.personProperty = "Person"
}
// 4.给Person的原型对象重新赋值
Person.prototype = new Animal()
// 5.给Person添加属于自己的方法
Person.prototype.personFunction = function () {
console.log(this.personProperty)
}
// 6.创建Person的实例
var person = new Person()
person.animalFunction()
person.personFunction()
3、实例属性搜索
当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。
在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。
参照上面的例子
- 第一步, 在person实例中搜索, 搜索到直接返回或者调用函数. 如果没有执行第二步.
- 第二步, 在Person的原型中搜索, Person的原型是谁? Animal的实例. 所以会在Animal的实例中搜索, 无论是属性还是方法, 如果搜索到则直接返回或者执行. 如果没有, 执行第三步.
- 第三步, 在Animal的原型中搜索, 搜索到返回或者执行, 如果没有, 搜索结束. (当然其实还有Object, 但是先不考虑)
4、判断实例原型
- instanceof:只要是实例与原型链中出现过的构造函数,结果就会返回true。
- isPrototypeOf:只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型,因此isPrototypeOf()方法也会返回true
// instanceof
console.log(person instanceof Object)
// true
console.log(person instanceof Animal)
// true
console.log(person instanceof Person)
// true
// isPrototypeOf
console.log(Object.prototype.isPrototypeOf(person))
// true
console.log(Animal.prototype.isPrototypeOf(person))
// true
console.log(Person.prototype.isPrototypeOf(person))
// true
5、原型链模式的问题
- 原型链存在最大的问题是关于引用类型的属性.
- 在创建子类型的实例时,不能向父类型的构造函数中传递参数,这就导致实例继承的属性都一致
function Person() {
this.personProperty = "Person"
}
Person.prototype = new Animal()
Person.prototype.personFunction = function () {
console.log(this.personProperty)
}
var person1 = new Person()
var person2 = new Person()
console.log(person1.colors)
// red,green
console.log(person2.colors)
// red,green
person1.colors.push("blue")
console.log(person1.colors)
// red,green,blue
console.log(person2.colors)
// red,green,blue
6、组合继承
基于原型链的问题我们可以使用组合继承来解决
- 组合继承就是发挥原型链和经典继承各自的优点来完成继承的实现.
- 使用原型链实现对原型属性和方法的继承.
- 通过经典继承实现对实例属性的继承, 以及可以在构造函数中传递参数.
// 1.创建构造函数的阶段
// 1.1.创建Animal的构造函数
function Animal(age) {
this.age = age
this.colors = ["red", "green"]
}
// 1.2.给Animal添加方法
Animal.prototype.animalFunction = function () {
console.log("Hello Animal")
}
// 1.3.创建Person的构造函数
function Person(name, age) {
Animal.call(this, age)
this.name = name
}
// 1.4.给Person的原型对象重新赋值
Person.prototype = new Animal(0)
// 1.5.给Person添加方法
Person.prototype.personFunction = function () {
console.log("Hello Person")
}
// 2.验证和使用的代码
// 2.1.创建Person对象
var person1 = new Person("Tom", 18)
var person2 = new Person("Jury", 30)
// 2.2.验证属性
console.log(person1.name + "-" + person1.age)
console.log(person2.name + "-" + person2.age)
// 2.3.验证方法的调用
person1.animalFunction()
// Hello Animal
person1.personFunction()
// Hello Person
// 2.4.验证引用属性的问题
person1.colors.push("blue")
console.log(person1.colors)
// red,green,blue
console.log(person2.colors)
// red,green