原型链继承
function Animal() {
this.color = ['red', 'yellow']
}
Animal.prototype.getColor = function() {
this.color.push('22')
}
function Dog() {}
Dog.prototype = new Animal()
let dog1 = new Dog()
dog1.color.push('blue')
dog2= new Dog()
dog3 = new Animal()
dog2.getColor()
dog3.getColor()
console.log(dog2.getColor()); // ["red", "yellow", "blue"]
console.log(dog3.getColor()); // ["red", "yellow"]
console.log(dog2.color); // ["red", "yellow", "blue", "22", "22"]
console.log(dog3.color); // ["red", "yellow", "22", "22"]
原型链继承存在的问题:
问题1:原型中包含的引用类型属性将被所有实例共享;
问题2:子类在实例化的时候不能给父类构造函数传参;
借助构造函数实现继承
function Animal(name) {
this.name = name
this.list = ['1']
this.getName = function() {
return this.name
}
this.setList = function() {
this.list.push('2')
}
}
Animal.prototype.setList3 = function() {
this.list.push('555')
}
function Dog(name) {
Animal.call(this, name)
}
// Dog.prototype = new Animal() // 如果加上这句话那么引用类型又出现了共享的问题
Dog.prototype.setList2 = function() {
this.list.push('3')
}
let dog = new Dog('小黄')
console.log(dog.name); // 小黄
console.log(dog.getName()); // 小黄
dog.setList()
console.log(dog.list); // ['1', '2']
let animal = new Animal()
animal.setList()
console.log(animal.list); // ['1', '2']
dog.setList2()
console.log(dog.list); // ['1', '2', ‘3]
console.log(animal.list) // ['1', '2']
dog.setList3() // 此方法不能被继承
console.log(dog.list)
animal.setList3()
console.log(dog.list);
console.log(animal.list);
借用构造函数实现继承解决了原型链继承的 2 个问题:
引用类型共享问题以及传参问题。但是由于方法必须定义在构造函数中,所以会导致每次创建子类实例都会创建一遍方法。
组合继承
function Animal(name) {
this.name = name
this.color = ['black', 'white']
}
Animal.prototype.getName = function() {
return this.name
}
Animal.prototype.setColor = function() {
this.color.push('red')
}
function Dog(name, age) {
Animal.call(this, name)
this.age = age
}
Dog.prototype = new Animal()
Dog.prototype.constructor = Dog
let dog = new Dog('小黄', 14)
console.log(dog.name, dog.age); // 小黄 14
let animal = new Animal('小黑')
console.log(animal.name, dog.name); // 小黑 小黄
// animal.setColor()
dog.setColor()
console.log(animal.color, dog.color); // ['black', 'white'] ['black', 'white', 'red']
// 使用这种方法引用数据类型互相独立,互不影响
组合继承结合了原型链和盗用构造函数,将两者的优点集中了起来。
基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。
这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。
寄生式组合继承
function Animal(name) {
this.name = name
this.color = ['black', 'white']
}
Animal.prototype.getName = function() {
return this.name
}
Animal.prototype.setColor = function() {
this.color.push('red')
}
function Dog(name, age) {
Animal.call(this, name)
this.age = age
}
Dog.prototype = new Animal()
Dog.prototype.constructor = Dog
// 组合继承已经相对完善了,但还是存在问题,它的问题就是调用了 2 次父类构造函数,
// 第一次是在 new Animal(),第二次是在 Animal.call() 这里。
// 所以解决方案就是不直接调用父类构造函数给子类原型赋值,而是通过创建空函数 F 获取父类原型的副本。
// 寄生式组合继承写法上和组合继承基本类似,区别是如下这里:
// 这两行代码省略掉
// Dog.prototype = new Animal()
// Dog.prototype.constructor = Dog
function F() {}
F.prototype = new Animal()
let f = new F()
Dog.prototype = f
f.constructor = Dog
// 可以进行函数的封装,封装后代码如下:
function object(o) {
function F(){}
F.prototype = o
return new F()
}
function inheritPrototype(child, parent) {
let prototype = object(parent.prototype)
child.prototype = prototype
prototype.constructor = child
}
inheritPrototype(Dog,Animal)