js实现对象的继承是开发人员迈向高级的重要基础,本文就es5和es6实现对象继承的最佳实践方式做一总结。
es5最佳继承模式“寄生组合”
关于es5多种继承方式实现的比较不太清除的,请移步“ES5面向对象js实现继承的最优方式”如果对es5与es6定义对象不清楚的可以移步“es5和es6定义对象比较”。
我们用es5继承的最佳实践“寄生组合方式”实现Person 和Gcc的继承关系:
let Person = function(name,age){
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
console.log(`大家好我是${this.name},我今年${this.age}岁了。`)
}
//子类
function Gcc(name, age, sex) {
Person.apply(this, [name,age]);
//Gcc的属性
this.sex = sex;
}
Gcc.prototype = Object.create(Person.prototype, {
constructor: {
value: Gcc,
writable: true,
configurable: true
}
});
//Gcc的公共方法
Gcc.prototype.saySex = function() {
console.log(`大家好我是${this.sex}`)
}
let gcc = new Gcc('cc',30,'boy');
gcc.say();
gcc.saySex();
我们看下结果:
Gcc的实例对象gcc私有属性:name,age,sex;公共方法saySex()同时还继承了父类的公共方法say();还有自己的构造函数Gcc,其构造函数的property就是Person类。
最最重要的是这种方式可以进行继承关系的判断,比如我们要判断Gcc类的实例对象到底是不是继承自Person类:
let cc = new Person("g",30);
let gcc = new Gcc('cc',30,'boy');
console.log(gcc instanceof Gcc);
console.log(gcc instanceof Person);
console.log(cc instanceof Person);
console.log(cc instanceof Gcc);
console.log(Person.prototype.isPrototypeOf(gcc)) ;
console.log(Gcc.prototype.isPrototypeOf(gcc)) ;
console.log(Person.prototype.isPrototypeOf(cc)) ;
console.log(Gcc.prototype.isPrototypeOf(cc)) ;
要判断两个类是否具有继承关系步骤:
- 分别用两个类A和B new出实例对象a和b;
- 判断
(1) A.prototype.isPrototypeOf(b) ;
(2) B.prototype.isPrototypeOf(a)) ;
以上判断有且仅有一个为真,我们就能确定A和B之间肯定有继承关系。
如果全为假说明没有继承关系,都为真则说明两个类相同或者继承自同一个类。
- 再次判断:
如果上一步(1)为真且(2)为假,则 B继承自A;
如果(1)为假且(2)为真,则A继承自B;
综上,我们就实现了继承关系的判断了。
es6 实现类的继承
class Person{
constructor(name,age) {
this.name = name;
this.age = age;
this.say = function() {
console.log(`我是${this.name}`);
}
}
do(str) {
console.log(`我是${this.name},我正在${str}`);
}
}
class Gcc extends Person{
constructor(name,age,job){
super(name,age); //必须先调用调用父类的constructor(name,age)获取this
this.job = job;
this.sayJob = function(){
console.log(`我是${this.name},我的工作是${this.job}`)
}
}
sayAll = function (){
console.log(`我是${this.name},我年龄${this.age},我的工作是${this.job}`);
}
}
let cc = new Person("g",30);
cc.say();
let gcc = new Gcc('gcc',30,'程序员');
gcc.say();
gcc.do("吃饭");
gcc.sayJob();
gcc.sayAll();
通过截图我们看到,es6实现的继承属性和方法除了父对象原型链上的方法属性外都是对象的私有的。
同样我们可以判断用prototype.isPrototypeOf判断出继承关系,方法同上面es5。
比较类的prototype属性和__proto__属性
- ES5中每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。
- ES6中没有构造函数,Class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。
(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
这样的结果是因为,类的继承是按照下面的模式实现的。
class A {
}
class B {
}
// B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);
const b = new B();
// B的实例继承A的静态属性
Object.setPrototypeOf(B, A);
const b = new B();
所以:
作为一个对象,子类(B)的原型(__proto__属性)是父类(A);
作为一个构造函数,子类(B)的原型(prototype属性)是父类的实例。