js中的继承从简单到完善
原型链继承
// 原型链继承
function Animal(name) {
this.colors = ['red', 'blue']
this.name = name
}
Animal.prototype.getColors = function () {
return this.colors
}
function Dog() {}
/**
* new Animal()创建出来的新对象的隐式原型指向Animal.prototype,
* 同时new Animal()中具有colors数组;new Animal()).__proto__上有getColors方法
* 即(new Animal()).__proto__ === Animal.prototype
*/
/**
* Dog类构造函数的原型指向new Animal()创建出来的对象,
* 同时Dog.prototype中具有colors数组,所以new Dog()中也会有colors数组
* 即 Dog.prototype === new Animal()
* 即Dog.prototype.__proto__ === new Animal().__proto__
* 所以Dog.prototype.__proto__上有getColors方法
*/
Dog.prototype = new Animal()
/**
* new Dog().__proto__ === Dog.prototype
* 即new Dog().__proto__.__proto__ === Dog.prototype.__proto__
* 所以new Dog().__proto__.__proto__上有getColors方法
*/
let dog1 = new Dog()
// dog1中没有colors,就会去dog1.__proto__上查找,并改变dog1.__proto__上的colors
dog1.colors.push('green')
// new Dog('lll')(子类实例化时)传入的参数无法被Anmial类(父类构造函数)接收到
// dog2.__proto__ === Dog.prototype === new Animal(),同样dog2中没有colors,就会去dog2.__proto__上去寻找
let dog2 = new Dog('lll')
// 引用类型属性将被所有实例共享 实例1中修改,实例2中也会被修改,因为修改的是原型上的数据
console.log(dog2.colors); // ['red', 'blue', 'green']
console.log(dog2.name); // undefined
/**
* 原型链继承存在的问题
* 1.原型中包含的引用类型属性将被所有实例共享
* 2.子类在实例化的时候不能给父类构造函数传参
*/
借用构造函数实现继承
// 借用构造函数实现继承
function Animal(name) {
this.name = name
this.getName = function () {
return this.name
}
}
function Dog(name) {
Animal.call(this,name)
}
Dog.prototype = new Animal()
let dog1 = new Dog('旺财')
let dog2 = new Dog('小宝')
console.log(dog1);
console.log(dog2);
/**
* 借用构造函数实现继承可以解决引用类型共享问题,也可以将参数传递给父类构造函数
* 产生了新的问题:
* 子类每次创建子类实例的时候,都会将父类构造函数中的方法创建出来(将父类构造函数的this都指向了子类创建出来的对象)
* 所以将父类构造函数中的方法定义在父类构造函数的原型上就可以解决这个问题,称为组合继承
*/
组合继承
// 组合继承
/**
* 1.将父类构造函数的方法定义到父类构造函数的原型上
* 2.子类构造函数通过调用父类进行传参
*/
function Animal(name) {
this.name = name
this.colors = ['red', 'blue']
}
Animal.prototype.getName = function() {
return this.name
}
function Dog(name, feet) {
Animal.call(this,name)
this.feet = feet
}
Dog.prototype = new Animal()
Dog.prototype.constructor = Dog
let dog1 = new Dog('旺财', 4)
dog1.colors.push('red')
let dog2 = new Dog('小宝', 3)
console.log(dog1); // {name: '旺财', colors: Array(3), feet: 4}
console.log(dog2); // {name: '小宝', colors: Array(2), feet: 3}
/**
* 组合继承可以解决 子类每次创建子类实例的时候,都会将父类构造函数中的方法创建出来(将父类构造函数的this都指向了子类创建出来的对象)的问题
* 产生了新的问题:
* 子类每次创建子类实例的时候,都会调用两次父类父类构造函数
* 1. new Animal()
* 2. Animal.call()
* 为了不调用父类构造函数给子类原型赋值,可以通过创建空函数获取父类原型的副本
*/
寄生式组合继承
// 寄生式组合继承
// 为了不调用父类构造函数给子类原型赋值(避免调用2次),可以通过创建空函数获取父类原型的副本
function Animal(name) {
this.name = name
this.colors = ['red', 'blue']
}
Animal.prototype.getName = function() {
return this.name
}
function Dog(name, feet) {
Animal.call(this,name)
this.feet = feet
}
//#region 创建空函数获取父类原型的副本 方法一
// 返回一个指向o的空对象
function object(o) {
function φ() {}
// o的prototype赋值给函数的prototype 返回该函数的实例对象,this也为o的this
φ.prototype = o;
return new φ()
}
// 实现子类与父类构造函数的原型副本的相互引用
function inheritPrototype(child, parent) {
// 创建一个父类构造函数的原型对象副本
let prototype = object(parent.prototype)
// 与子类构造函数相互引用
prototype.constructor = child
child.prototype = prototype
}
inheritPrototype(Dog, Animal)
//#endregion
//#region 创建空函数获取父类原型的副本 方法二
// 创建一个父类构造函数原型的空对象,并将子类构造函数的原型指向该对象,该对象的constructor指向子类构造函数
// Dog.prototype = Object.create(Animal.prototype)
// Dog.prototype.constructor = Dog
//#endregion
let dog1 = new Dog('小宝', 3)
dog1.colors.push('green')
let dog2 = new Dog('小猪', 4)
console.log(dog1); // {name: '小宝', colors: Array(3), feet: 3}
console.log(dog2); // {name: '小猪', colors: Array(2), feet: 4}
class实现继承(ES6)
// class实现继承 es6
class Animal {
constructor(name) {
this.name = name
this._height = 1.88
}
// 实例方法 通过创建出来的对象进行访问
getName() {
return this.name
}
// 访问器方法 通过创建出来的对象进行访问,可以拦截访问操作
get height() {
console.log('拦截', this._height)
}
// 静态方法(类方法)通过类名访问
static all() {
console.log('all静态方法');
}
}
console.log((new Animal('zzy')).getName()) // zzy
console.log((new Animal('zzy').height)); // 拦截 1.88
console.log(Animal.all()); // all静态方法
class Dog extends Animal {
constructor(name,age) {
super(name)
this.age = age
}
}
console.log(new Dog('旺财',22).getName());// 旺财