一、构造函数
创建构造函数
// 创建一个构造函数
function Animal (name, age) {
// 内部属性
this.name = name
this.age = age
// 内部的方法
this.action = () => {
console.log('叫')
}
}
实例化对象
/*
使用new关键字后,发生的隐式操作
1. 创建一个实例对象 Dog
2. 将Dog的__proto__属性赋值成 Animal.prototype属性 (相当于继承原型链上的属性以及方法)
3. 执行Animal.call(Dog, '旺财', 12)
如果不使用new关键字,Dog就为Animal返回的东西,没有为undefined
*/
const Dog = new Animal('旺财', 12)
验证刚才的观点
console.log(Dog) // Animal {name: '旺财', age: 12, action: ƒ}就是 Animal的实例
console.log(Dog.__proto__) // __proto__指向对应构造函数的"原型对象"
console.log(Animal.prototype) // 构造函数的原型对象
console.log(Dog.__proto__ === Animal.prototype) // true
console.log(Animal.prototype.isPrototypeOf(Dog)) // true 存在在Animal的原型链上
此为Animal.prototype打印出的结果,也就是原型对象,原型对象也有自己的__proto__指针,指向的是对应的原型对象,以此类推一直到顶层的null,即为原型链 ,此时的原型链上除了内置的方法外,没有其他的自定义的一些属性和方法,因为Animal中定义的属性以及方法都是私有的,对应每个创建好的实例对象
添加公共属性
// 直接像原型链上创建一个属性方法
Animal.prototype.perform = () => {
console.log('我会表演', this)
}
那此时原型对象上的公共属性就会多一个perform,每一个实例化对象都是可以使用的
一、构造函数的继承
// 还是动物的构造函数
function Animal (name, age) {
console.log(name, age)
console.log(this)
this.name = name
this.age = age
this.action = () => {
console.log(`我是动物, 我叫${name},年纪${age}`)
}
}
// 在原型上添加方法
Animal.prototype.perform = () => {
console.log('我会表演', this)
}
// 创建另一个构造函数,使用call将当前this传进去
function Cat (name, age) {
Animal.call(this, name, age)
this.name = name
this.age = age
}
// 创建cat1
const cat1 = new Cat('小猫', 16)
上面我们说过,new时隐式操作了三个步骤,创建,赋值原型,call改变this,那么这里我们用到了第一步以及第三步,看一下是什么效果
console.log(cat1.action) // 存在
console.log(cat1.__proto__ === Cat.prototype) // true
console.log(Cat.prototype.__proto__ === Animal.prototype) // false
console.log(cat1.perform) // undefined
// 第一句: 可以看出,此类继承,继承了公共Animal的action内部的方法,
// 第二句: cat1的__proto__指向的一定是Cat的prototype属性,因为是使用new创建的cat1
// 第三句: 证明Cat这个构造函数并没有继承实际Animal原型对象
// 第四句: 证明cat1并没有在Animal以下的这条原型链上,因为无法读取原型链上的方法,也就证明,Cat使用call这种继承方法,只能继承对应内部的属性,无法继承原型链上的属性
二、原型链继承
// 还是动物的构造函数
function Animal (name, age) {
console.log(name, age)
console.log(this)
this.name = name
this.age = age
this.action = () => {
console.log(`我是动物, 我叫${name},年纪${age}`)
}
}
// 在原型上添加方法
Animal.prototype.perform = () => {
console.log('我会表演', this)
}
// 创建另一个构造函数
function Cat (name, age) {
this.name = name
this.age = age
}
Cat.prototype = new Animal(); // 此时Cat.prototype.constructor==Animal
Cat.prototype.constructor = Cat; // 修正constructor指向
const cat1 = new Cat('小猫', 16)
console.log(cat1.action) // 存在
console.log(cat1.__proto__ === Cat.prototype) // true
console.log(Cat.prototype.__proto__ === Animal.prototype) // true
console.log(cat1.perform) // 存在
// 第一句: action存在说明,继承了Animal身上的属性,(但此处属性是共享的互相影响的)
// 第二句: 一样使用new 创建的 cat1的__proto__一定是Cat的prototype
// 第三句: 说明原型继承了,所以第四句也是可以访问到,原型链上的方法的
// 第四句: 可以访问,因为存在同一个原型链上
// 缺点: 属性共享,改一处,其他地方也跟着改变
// 缺点: 无法传参数到Animal中
三、组合继承
// 还是动物的构造函数
function Animal (name, age) {
console.log(name, age)
console.log(this)
this.name = name
this.age = age
this.action = () => {
console.log(`我是动物, 我叫${name},年纪${age}`)
}
}
// 在原型上添加方法
Animal.prototype.perform = () => {
console.log('我会表演', this)
}
// 创建另一个构造函数
function Cat (name, age) {
this.name = name
this.age = age
// 加了这么一句
Animal.call(this, name, age)
}
Cat.prototype = new Animal(); // 此时Cat.prototype.constructor==Animal
Cat.prototype.constructor = Cat; // 修正constructor指向
const cat1 = new Cat('小猫', 16)
// 缺点: 由于调⽤了2次⽗类的构造⽅法,会存在⼀份多余的⽗类实例属性