原型继承
优点:可以继承原型中的方法
缺点:1.实例属性虽然有,但是无法传参(跟没有一样);
2. 子类实例对象共享了父类构造函数的引用属性
function Animal(name, age) {
this.name = name
this.age = age
this.other = {
age: 1
}
this.eat = function() {
console.log('品味美食')
}
}
Animal.prototype.say = function () {
console.log('hello');
}
function Cat(color) {
this.color = color
this.shangShu = function() {
console.log('猫咪可以爬树')
}
}
// 继承的核心代码:
Cat.prototype = new Animal('jack', 18)
// 设置被覆盖的constructor属性
Cat.prototype.constructor = Cat
const cat1 = new Cat('red', '111')
const cat2 = new Cat('white', '222')
console.log(cat1) // {color: "red", shangShu: ƒ}
console.log(cat1.name) // jack 原型上的name
console.log(cat1.age) // 18 原型链上的age
console.log(cat1.color) // red 该对象的属性color
cat1.shangShu() // 猫咪可以爬树
cat1.eat() // 品味美食
cat1.other.age = 2
console.log('cat1.other.age', cat1.other.age); // cat1.other.age 2
console.log('cat2.other.age', cat2.other.age); // cat2.other.age 2
console.log(cat1)
借用继承
优点:1. 可以向父类构造函数传参;
2. 不共享父类构造函数的引用属性
缺点:1. 方法不能复用,每个实例对象中继承来的方法是独立的,不是复用的
2. 不能继承父类原型上的方法
// - 设置需要被继承的构造函数Animal
function Animal(name, age) {
this.name = name
this.age = age
this.other = {
age: 1
}
this.eat = function() {
console.log('品味美食')
}
}
Animal.prototype.say = function () {
console.log('hello');
}
// - 再设置一个猫咪构造函数
// 已经存在的构造函数Animal中具有了一些属性和方法,这些属性与方法是Cat构造函数需要的没有必要再书写一遍了,可以使用继承的方式获取
function Cat(name, age) {
// 通过call的方式,修改Animal中的this指向为当前Cat的this,并设置参数
Animal.call(this, name, age)
}
const cat1 = new Cat('jack', 18)
const cat2 = new Cat('张三', 22)
console.log(cat1) // {name: "jack", age: 18, eat: ƒ}
cat1.eat() // 品味美食
try {
cat1.say()
} catch (e) {
console.log(e.message); // cat1.say is not a function
}
cat1.other.age = 2
console.log('cat1.other.age', cat1.other.age); // cat1.other.age 2
console.log('cat2.other.age', cat2.other.age); // cat2.other.age 1
组合继承
优点:
1. 可以向父类构造函数传参
2. 可以复用父类原型上的方法
3. 不共享父类的引用属性
使用前面两种继承方式时发现各有优缺点,所以将两种方式结合即可 这种结合使用的方式称为组合继承,组合继承是最常用的一种继承方式。
function Animal(name, age) {
this.name = name
this.age = age
this.eat = function() {
console.log('品味美食')
}
}
function Cat(name, age, color) {
// 使用借用构造函数的方式继承:
Animal.call(this, name, age)
this.color = color
this.shangShu = function() {
console.log('猫咪可以爬树')
}
}
// 原型继承的核心代码:
// 使用组合继承方式后,在构造函数体中就设置了实例属性,此处不需要传参
// Cat.prototype = new Animal() // 不使用这种方式,因为会重复调用父类构造函数导致会存在多余的父类属性,使用下面的方式
// Cat.prototype = Animal.prototype // 此方式当修复子类构造函数的执行后,父类实例的构造函数指向也会跟着变化
// 通过创建中间对象,子类原型和父类原型就会隔离开,不是同一个。可以有效避免上面的的问题
Cat.prototype = Object.create(Animal.prototype)
// 设置被覆盖的constructor属性
Cat.prototype.constructor = Cat
const cat = new Cat('rose', 20, 'red')
console.log(cat) // {name: "rose", age: 20, color: "red", eat: ƒ, shangShu: ƒ}
console.log(cat.name) // rose
console.log(cat.age) // 20
console.log(cat.color) // red
cat.shangShu() // 猫咪可以爬树
cat.eat() // 品味美食
console.log(cat.constructor)
/**
ƒ Cat(name, age, color) {
// 使用借用构造函数的方式继承:
Animal.call(this, name, age)
this.color = color
this.shangShu = function() {
console.log('猫咪可以爬树')
}
}
*/
拷贝继承
使用继承的原型中的方法时,查找的次数会降低
function Animal(name, age) {
this.name = name
this.age = age
this.eat = function() {
console.log('品味美食')
}
}
function Cat(name, age, color) {
// 使用借用构造函数的方式继承:
Animal.call(this, name, age)
this.color = color
this.shangShu = function() {
console.log('猫咪可以爬树')
}
}
// 拷贝
for (const k in Animal.prototype) {
if (Animal.prototype.hasOwnProperty(k)) {
Cat.prototype[k] = Animal.prototype[k]
}
}
Cat.prototype.constructor = Cat
const cat = new Cat('rose', 20, 'red')
console.log(cat) // {name: "rose", age: 20, color: "red", eat: ƒ, shangShu: ƒ}