先上图,来了解一下继承关系。
原型链继承
面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。
大部分面向对象的编程语言,都是通过“类”(class)实现对象的继承。传统上,JavaScript 语言的继承不通过 class,而是通过“原型对象”(prototype)实现。
通过将自己的原型继承于另外一个构造函数创建出来的对象,将被继承的对象中的所有属性都存在于__proto__属性中.
//父类
function Person(name, age) {
this.name = name;
this.age = age;
}
//子类
function Star(like) {
this.like = like;
}
//实例化一个Person对象,将对象的地址赋值给Star的prototype属性。
Star.prototype.name = "郭德纲";
Star.prototype.age = 48;
// 【注意】Star.prototype原来指向的对象就没有了,指向新的Person实例对象。
Star.prototype = new Person("于谦", 52)
var likes = new Star("抽烟喝酒烫头")
console.log(likes);
运行结果
优点:
父类方法可以复用
缺点:
1.原型链继承多个实例的引用属性指向相同,改变一个会影响另一个实例的属性.如果是引用类型的,继承的是地址,那么子原型中的引用地址和父对象中的引用地址相同
2.不能传递参数, 因为是原型上继承所以不能传递参数
3.继承单一
借用构造函数
在子类构造函数中调用父类构造函数,可以在子类构造函数中使用call()和apply()方法
function Animals(color, age) {
this.color = color;
this.age = age;
this.eat = "啥都吃";
}
function Dog(color, age, name) {
Animals.call(this, color, age)
this.name = name;
}
var D1 = new Dog("yellow", 1, "小黄");
var D2 = new Dog("white", 2, "小白");
console.log(D1);
console.log(D2);
运行结果
优点: 可以在子类构造函数中向父类传参数
父类的引用属性不会被共享
缺点: 只能继承父类的实例属性和方法,不能继承到原型中的内容
无法实现构造函数的复用,每个子类都有父类实例函数的副本,影响性能,代码会臃肿
组合继承
组合继承综合了原型链继承和盗用构造函数继承(构造函数继承),将两者的优点结合了起来, 基本的思路就是使用原型链继承原型上的属性和方法,而通过构造函数继承实例属性,这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。
function Animals(color, age,) {
this.color = color;
this.age = age;
this.bodyType = ["Fat"];
}
// 将父类的方法定义在父类的原型对象上。
Animals.prototype.eat = function () {
console.log("啥都吃");
}
function Dog(color, age, name) {
//在子类的构造函数中借用父类的构造函数
Animals.call(this, color, age)
this.name = name;
}
//将子类的原型对象指向 父类的实例对象
Dog.prototype = new Animals();
var D1 = new Dog("yellow", 1, "小黄");
//修正 Dog的原型对象上 constructor属性 的指向。
Dog.prototype.constructor = Animals;
D1.eat();
D1.bodyType.push("tall");
var D2 = new Dog("white", 2, "小白");
console.log();
console.log(D1);
console.log(D2);
运行结果:
优点: 父类的方法可以复用
可以在子类构造函数中向父类构造函数中传参
父类构造函数中的引用属性不会被共享