继承在JavaScript中也是比较重要的概念。如果仔细讲解会需要比较多的篇幅,所以这里我就列举两种使用比较多,比较被推荐的继承写法。在JavaScript中,实现继承的最根本的思想肯定是原型链
,不管怎样都离不开它,所以如果你对原型链理解非常好,那么继承对于你来说只是代码层面的事。
ES5中的两种非常经典的继承
第一种:组合继承
指的是将原型链和构造函数的技术组合到一起来实现继承。其背后的思路就是使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实例属性的继承。
举个例子:
//构造函数继承实例属性(当然也可以有方法,不过不建议这么做)
function Phone(name){
this.name = name;
this.label=["全面屏","5G通信"];
}
//原型链继承原型属性和方法
Phone.prototype.colors=["red","blue","green"];
Phone.prototype.takePhoto = function(){
console.log("我是父类的原型上的方法--------拍照ing: "+this.name);
}
function Huawei(name,inch){
//继承构造函数中的属性
Phone.call(this,name);
//子类自己构造函数中的属性
this.inch = inch;//英寸
}
//继承原型上的属性和方法
Huawei.prototype = new Phone();
Huawei.prototype.constructor = Huawei;
//在原型上添加子类自己的方法或属性
Huawei.prototype.listenMusic = function(){
console.log("我是子类在原型上添加的方法------听歌ing+ "+this.inch);
}
//以上我们就实现了Huawei继承Phone,并且Huawei也拥有了自己独有的属性inch和方法listenMusic。
//下面进行测试:
var h1 = new Huawei("华为P20",6.0);//实例化
h1.label.push("人脸识别")//操作父类构造函数的属性
console.log(h1.label);
h1.takePhoto();//调用父类原型上的方法
h1.listenMusic();//调用子类添加在原型上的方法
以上这种继承方式避免了单独使用原型链和构造函数的缺陷,融合了它们的优点,成为JavaScript中最常用的继承方式。而且,instanceof
和isPrototypeof()
也能够用于识别基于组合继承的对象。
第二种:寄生组合继承
这种继承方式是目前认为ES5中最有效的方式,当然组合继承用的也是非常的多的。
实现寄生组合继承的关键方法:inhert()
function inherit(son,father){
let prototypeObj = Object.create(father.prototype);
prototypeObj.constructor = son;
son.prototype = prototypeObj;
}
举个例子:
//构造函数继承实例属性
function Phone(name){
this.name = name;
this.label=["全面屏","5G通信"];
}
//原型链继承原型属性和方法
Phone.prototype.colors=["red","blue","green"];
Phone.prototype.takePhoto = function(){
console.log("我是父类的原型上的方法--------拍照ing: "+this.name);
}
function Huawei(name,inch){
//继承构造函数中的属性
Phone.call(this,name);
//子类自己构造函数中的属性
this.inch = inch;//英寸
}
//继承原型上的属性和方法:(非常重要)
inherit(Huawei,Phone);
//在原型上添加子类自己的方法或属性
Huawei.prototype.listenMusic = function(){
console.log("我是子类在原型上添加的方法------听歌ing+ "+this.inch);
}
//以上我们就实现了Huawei继承Phone,并且Huawei也拥有了自己独有的属性inch和方法listenMusic。
//下面进行测试:
var h1 = new Huawei("华为P20",6.0);//实例化
h1.label.push("人脸识别")//操作父类构造函数的属性
console.log(h1.label);
h1.takePhoto();//调用父类原型上的方法
h1.listenMusic();//调用子类添加在原型上的方法
两种方式的比较
对于组合继承:就是将子类的原型指向父类的实例
,然后子类和父类组合一下构造函数中的实例属性或方法
。这样做就会导致子类原型(此时是父类)上的属性和子类与父类组合的属性重复,即,原本父类的属性此刻在子类本身和子类原型上都有一份。也就是下面图片中两次调用所指的内容:
对于寄生组合继承:关键点就是理解上文提到的inhert()方法:将父类的原型赋给一个临时对象,子类的原型指向该临时对象
,如此得到父类原型上的属性和方法。再通过构造函数组合父类和子类本身上的属性或方法
。
如图:
ES6中通过Class
‘类’这个语法糖实现继承和Java等面向对象的语言在实现继承上已经非常相似,当然只是语法层面相似,本质当然依旧是通过原型实现的。
ES6实现继承是通过关键字extends
、super
来实现继承,和面向对象语言Java一样。
直接上一个例子:
//父类:两个属性,一个普通方法,一个静态方法
class Person{
constructor(name,age){
this.name= name;
this.age = age;
}
sayName(){
console.log("我的名字是: "+this.name);
}
static sayHello(){
console.log("hello!!!!!!");
}
}
//子类继承父类
class Chinese extends Person{
constructor(name,age,addr){
super(name,age);//必须现在最前面
this.addr = addr;
}
sayMyaddr(){
console.log("我是中国人,我住在:"+this.addr);
}
}
//测试
var wlk = new Chinese("wk",20,"中国-重庆");
wlk.sayName();
wlk.sayMyaddr();
Person.sayHello();
Chinese.sayHello();//静态方法也可以被继承
在ES5中的组合继承和寄生组合继承都不能继承静态方法,ES6的继承可以继承静态方法。
比如在Phone上加一个静态方法
:
Phone.sayHello = function(){ console.log("hello!!!!"); }
在Huawei上是访问不到的:
Huawei.sayHello();//报错
在ES6中,父类的静态方法sayHello()可以通过子类中调用。
再提醒一点,
ES6的static关键字只能用于静态方法,不能用于属性
。