ES6之前:继承主要分为 原型链继承、构造函数继承、组合继承、寄生组合继承(最终继承方式)
ES6之后:class继承
目录
1.原型链继承
原型链继承通过修改子类的原型为父类的实例,从而实现子类可以访问到父类构造函数以及原型上的属性或者方法
优点:好理解,逻辑清晰
缺点:不好传参数,无法定制化对象,若父类有引用类型,如数组,数据可能公用
function Person() {
this.name = 'coder'
}
Person.prototype.eat = function () {
console.log(this.name + ' eating')
}
function Student(name) {
this.name = name
}
//因为 new Peoson(),这个对象的__proto__指向Person.prototype
Student.prototype = new Person()
// 错误写法
// 这样写的话,Student类中的私有方法也会加到Person.prototype中,对于面向对象而言,是错误的
Student.prototype = Person.prototype
let s = new Student('str')
s.age = 17
console.log(s) // { name: 'str', age: 17 }
2.构造函数继承(借用构造函数)
通过call,this来实现继承,在子类中调用父类的构造函数
优点:可以进行传参,定制对象了
缺点:属性和方法都写在一起,浪费空间资源,因为方法都是一样的,不应该每次重新创建
function Person(name,age) {
this.name = name
this.age = age
this.eat = function () {
console.log(this.name + ' eating')
}
}
function Student(name,age,sno) {
// 用call调用父类构造函数,实现代码复用
Person.call(this,name,age)
this.sno = sno
}
let s = new Student('str', 17)
console.log(s) // { name: 'str', age: 17, eat: [Function (anonymous)] }
3.组合继承
原型链继承、构造函数继承结合在一起,就是组合继承
优点:解决构造函数继承中,每次创建对象方法也跟着创建的问题
缺点:1.父类至少被调用了两次,一次是call调用,一次是改变子类prototype指向时调用
2.子类的prototype上也会存在属性,值都为undefined,这其实时不必要的
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.eat = function () {
console.log(this.name + ' eating')
}
function Student(name,age,sno) {
// 用call调用父类构造函数,实现代码复用
Person.call(this,name,age)
this.sno = sno
}
Student.prototype = new Person()
// 因为修改了Student.prototype指向,导致没有constructor属性,会通过原型链找到父类,所以打印出来的类型是父类的
// 手动增加constructor属性指回自己
Student.prototype.constructor = Student
4.寄生组合继承(最终方案)
优点:最终方案,完美
缺点:应该没有
// 让子类继承父类
function inter(childObj, superObj) {
childObj.prototype = createObj(superObj.prototype)
childObj.prototype.constructor = childObj
}
// 拐一个弯实现
function createObj(o) {
function foo() {}
foo.prototype = o
return new foo()
}
//------------------------------------
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.eat = function () {
console.log(this.name + ' eating')
}
function Student(name, age, sno) {
Person.call(this, name, age)
this.sno = sno
}
// 1.可以这么继承 可能有兼容问题
// Student.prototype = Object.create(Person.prototype)
//Student.prototype.constructor = Student
// 2.手动实现继承
inter(Student, Person)
Student.prototype.stuny = function () {
console.log(this.name + ' study')
}
let s = new Student('小明', 15, 11223)
console.log(s)
s.eat()
s.stuny()
ES6之后:使用class、extends实现继承
class Person {
// 赋值写在构造器中
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this.name + ' running');
}
}
class Student extends Person {
constructor(name, age, sno) {
// 若使用了构造器接受参数,super必须位于第一行
super(name, age)
this.sno = sno
}
studing() {
console.log(this.name + ' studying');
}
}
let stu = new Student('小明', 14, 00001)
stu.running() //小明 running
stu.studing() //小明 studying
console.log(stu); //Student { name: '小明', age: 14, sno: 1 }