ES5的继承
首先我们创建一个简单的父类
1 2 3 4 5 6
| function Person(name){ this.name = name } Person.prototype.say = function(){ return 'hi' }
|
继承父类
首先有一点需要说明,constructor属于不可枚举的属性
1 2 3 4 5
| constructor: configurable: true enumerable: false // 不可枚举 value: ƒ Student(name, age) writable: true
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Student(name, age){ //拓展一个实例属性 Person.call(this, name); //其实很简单直接,就是调用一遍父类的this.x = x this.age = age } //将子类的原型对象指向Person的实例 Student.prototype = new Person(); //将子类的原型对象的构造函数正确指回 Student.prototype.constructor = Student; //新增一个实例方法 Student.prototype.sayAge = function(){ return this.age };
|
JavaScript高程里的写法,需要借助两个辅助函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function inheritPrototype(subType, superType) { let prototype = object(superType.prototype); // 创建对象 prototype.constructor = subType; // 增强对象 subType.prototype = prototype; // 赋值对象 } function object(o) { function F() {} F.prototype = o; return new F(); } function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function () { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function () { console.log(this.age); };
|
下边是网上的变种(倒是能用)
第一种方式,感觉不太好
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function Student(name, age){ Person.call(this, name); this.age = age } // Object.assign 无法复制继承属性及不可枚举的数据 以及get set函数 Student.prototype = Object.assign(Object.create(Person.prototype), Student.prototype) // 所以需要重新赋值constructor,但是用这种方法constructor属性就变为可枚举属性了 Student.prototype.constructor = Student; Student.prototype.sayAge = function(){ return this.age };
const student = new Student('lucy',30) console.log(student);
|
第二种方式,可以解决枚举属性的问题
1 2 3 4 5 6 7 8
| Student.prototype = Object.create(Person.prototype, { constructor: { value: Student, enumerable: false, writable: true, configurable: true } })
|
第三种方式是我自己想的,结合Object.getOwnPropertyDescriptors()
该函数的参数是Student.prototype,第一次写的时候忘了写.prototype
1 2 3 4
| Student.prototype = Object.create( Person.prototype, Object.getOwnPropertyDescriptors(Student.prototype) )
|
下图是控制台输出,左边是组合继承,右边是寄生组合继承
我们为什么要把子类的prototype指向父类的实例而不是父类的prototype呢
如果我们把子类的原型对象直接指向父类的原型对象,扩展子类会影响到父类
将子类的原型对象直接指向父类的原型对象,给子类添加额外的原型方法
1 2 3 4 5 6 7 8 9 10 11 12
| function Student(name, age){ Person.call(this, name); this.age = age } Student.prototype = Person.prototype; Student.prototype.constructor = Student;
Student.prototype.sayAge = function(){ return this.age }; console.log(Person.prototype); console.log(Student.prototype);
|
我们可以看到父类的prototype已经被子类改变了,这明显不符合我们的目的
ES5继承的帮助记忆
子类的prototype必须是某个对象(这个对象的__proto__必须指向父类的prototype)
满足这个条件的某个对象:
- 父类的实例 组合继承
- Object.create(父类.prototype) 寄生组合继承
满足条件后子类的实例可以通过两个__proto__查找,访问到父类原型对象,即
ES6的继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Person { constructor(name){ this.name = name //实例属性 } say(){ //原型方法 return 'hi' } } class Student extends Person{ constructor(name, age){ super(name) this.age = age } } const student = new Student('lucy',30) console.log(student);
|
控制台输出与ES5寄生组合继承一致