借助构造函数实现继承
核心代码是Animal的构造函数在Dog的构造函数中执行(call)。
function Animal() {
this.name = 'cat';
this.msg = {
age: 9
}
}
Animal.prototype.walk = function () {
console.log(this.name +" walk");
}
function Dog() {
Animal.apply(this); //核心代码
this.type = 'hashiqi';
}
let dog1 = new Dog();
let dog2 = new Dog();
dog1.msg.age = 12;
console.log(dog1, dog2);
- dog1 和 dog2 已经有了 name 和 msg 属性;并且 msg 属性并不会相互影响;
- 缺点是:Dog这个类并没有继承Animal原型对象prototype上定义的方法!
借助原型继承
核心代码是Dog的原型直接指向new Animal对象,这样Dog的类会借助原型对象继承Animal的属性和方法。
function Animal() {
this.name = 'cat';
this.msg = {
age: 9
}
}
Animal.prototype.greet = function () {
console.log('hello')
};
function Dog() {
this.name = 'dog';
}
Dog.prototype = new Animal(); //核心代码
const dog1 = new Dog();
dog1.msg.age = '99';
const dog2 = new Dog();
dog2.msg.age = '199';
console.log(dog1, dog2);
- dog1和dog2借助原型对象已经继承了Animal的属性和方法;
- 缺点也比较明显了,因为dog1和dog2的原型对象指向的是同一个原型对象,这就导致了它们继承的属性如果是引用类型(masg)的话,会相互影响。
借助构造函数和原型链结合
核心思想是结合前面两种方法:Animal的构造函数在Dog中执行(call); Dog的原型对象执行一个Animal对象,借助原型链继承Animal的方法。
function Animal() {
this.name = 'cat';
this.msg = {
age: 9
}
}
Animal.prototype.greet = function () {
console.log('hello')
};
function Dog() {
Animal.call(this); //核心代码
this.name = 'dog';
}
Dog.prototype = new Animal(); //核心代码
const dog1 = new Dog();
dog1.msg.age = '99';
const dog2 = new Dog();
dog2.msg.age = '199';
console.log(dog1, dog2);
- dog1和dog2借助构造函数继承了Animal的属性;借助原型对象继承了Animal的属性和方法;同时dog1和dog2属性也保持了独立性,不会相互影响;
- 缺点是Animal的构造方法执行了2遍:在Dog构造函数中执行了一遍,在原型对象上执行了一遍。
组合优化方式
借助Object.create()方法,将Dog的原型对象的__proto__指向Animal的原型对象。即将Dog的原型对象也看成一个对象,将这个对象的原型指向 Animal;同时修改Dog的原型对象的constructor属性指向Dog函数。
function Animal() {
this.name = 'cat';
this.msg = {
age: 23
}
}
Animal.prototype.greet = function () {
console.log('hello')
};
function Dog() {
Animal.call(this);
this.name = 'dog';
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
let dog = new Dog();
console.log(dog);
对于Object.create()方法的理解,创建对象的如下两种方法的区别:
{
// new Object() 方式创建
let a = { rep : 'apple' }
let b = new Object(a)
console.log(b) // {rep: "apple"}
}
{
// Object.create() 方式创建
let a = { rep: 'apple' }
let b = Object.create(a)
console.log(b) // {}
}
- new Object(a)得到的b是普通对象,跟对象a一样
- Object.create(a)得到的b本身是空对象{}, 它的原型对象会指向a
实现多继承
function SuperClass() {
this.type = 'super';
}
SuperClass.prototype.sayType=function () {
console.log('type');
}
function OtherSuperClass() {
this.props = 'other';
}
OtherSuperClass.prototype.sayProps=function () {
console.log('other');
}
function MyClass() {
SuperClass.call(this);
OtherSuperClass.call(this);
}
// 继承一个类
MyClass.prototype = Object.create(OtherSuperClass.prototype);
// 混合其它
Object.assign(MyClass.prototype, SuperClass.prototype);
// 重新指定constructor
MyClass.prototype.constructor = MyClass;
let m = new MyClass();
m.sayProps();
m.sayType();
let o = new OtherSuperClass();
console.log(o);
Object.assign 会把 OtherSuperClass原型上的函数拷贝到 MyClass原型上,使 MyClass 的所有实例都可用 OtherSuperClass 的方法。Object.assign 是在 ES2015 引入的,且可用 polyfilled。要支持旧浏览器的话,可用使用 jQuery.extend() 或者 _.assign()。