基本概要:继承是面向对象语言中最重要的特点之一,“继承”主要为了实现代码的封装、继承、多态这3种特性,以达到复用的目的,而JavaScript语言严格来说不属于面向对象语言,所以无法很好的实现继承的特性。只能一些JS特性原型和原型链来实现继承,几乎所有的继承都是通过原型和原型链展开的;
一、原型链继承
“原型链继承”通过JS的原型和原型原理来实现继承关系。将父Class中的原型对象拷贝到子Class中的原型对象中;代码如下:
function Parent(){
this.name = "test";
this.show = function(){
console.log("Parent对象打印name的值:"+this.name);
}
this.setName = function(value){
this.name = value;
}
}
function Child1(){
}
//Child1开始实现继承关系
Child1.prototype = new Parent();
Child1.prototype.showInfo = function(){
console.log("Child对象打印name的值:"+this.name);
}
var child1 = new Child1();
child1.show();
child1.setName("张三");
child1.showInfo();
原理:通过子对象原型对象指向父对象原型对象来实现继承;核心代码如下:
Child.prototype = new Parent();
问题:当父原型对象的某个属性值是“多级对象”引用类型值时,同时子Class原型对象继承父原型对象指针时,会出现改变父原型对象属性值时,所有的子对象都会一起发生改变;(编码注意些即可,不要让子对象直接拷贝父对象指针)
//错误编码
var parent = new Parent();
Child1.prototype = parent;
Child2.prototype = parent;
//正确编码
Child1.prototype = new Parent();
Child2.prototype = new Parent();
//演示代码
function Parent(){
this.data = {name:"test",info:{age:27}};
this.show = function(){
console.log("Parent对象打印name的值:",this.data);
}
this.setName = function(value){
this.data.info.age = value;
}
}
var parent = new Parent();
//声明子类Child1
function Child1(){}
//Child1开始实现继承关系
Child1.prototype = parent;
Child1.prototype.showInfo = function(){
console.log("Child对象打印name的值:"+this.data.info.age);
}
var child1 = new Child1();
child1.show();
child1.setName(29);
child1.showInfo(); //29
//声明子类Child2
function Child2(){}
//Child1开始实现继承关系
Child2.prototype = parent;
Child2.prototype.showInfo = function(){
console.log("Child对象打印name的值:"+this.data.info.age);
}
var child2 = new Child2();
child2.show();
child2.showInfo(); //29
二、构造函数继承
“构造函数继承”通过在子对象的构造函数中改变父构造函数的this指针来实现继承的方案;代码如下:
function Parent(){
this.name = "songxy";
}
function Child(){
Parent.call(this);
}
Child.prototype = {
showName:function(){
console.log("name value:",this.name);
}
}
var child = new Child();
child.showName();
原理:在子构造函数中使用apply,call等方法去改变父构造函数指针来实现继承;
function Parent(){
this.name = "songxy";
}
function Child(){
//通过这个来实现继承
Parent.call(this);
}
问题:该方式无法实现父对象中的原型对象继承。代码如下:
function Parent(){
this.name = "songxy";
}
Parent.prototype = {
setName: function(value){
this.name = value;
}
}
function Child(){
Parent.call(this);
}
Child.prototype = {
showName:function(){
console.log("name value:",this.name);
}
}
var child = new Child();
child.setName("李四"); //报错:无法访问到父原型对象中的setName方法
child.showName();
三、组合继承
组合继承其实是“原型链继承”和“构造函数继承”的组合方案,主要为了解决上述出现的问题;代码如下:
function Parent(){
this.data = {name:"test",info:{age:27}};
this.show = function(){
console.log("Parent对象打印name的值:",this.data);
}
this.setName = function(value){
this.data.info.age = value;
}
}
var parent = new Parent();
function Child1(){
Parent.call(this);
}
//Child1开始实现继承关系
Child1.prototype = parent;
Child1.prototype.showInfo = function(){
console.log("Child对象打印name的值:"+this.data.info.age);
}
var child1 = new Child1();
child1.show();
child1.setName(29);
child1.showInfo();
function Child2(){
Parent.call(this);
}
//Child1开始实现继承关系
Child2.prototype = parent;
Child2.prototype.showInfo = function(){
console.log("Child对象打印name的值:"+this.data.info.age);
}
var child2 = new Child2();
child2.show();
child2.showInfo();
解决的问题:
- 构造函数继承导致无法的继承原型对象;
- 原型链继承导致改变父对象属性影响所有的子对象;(针对“引用类型”的属性值)
产生的问题:
- 父构造函数实例化了两次;
- 造成内存额外占用;
四、原型式继承
“原型式继承”是通过封装个功能函数,在该函数中实现新的构造函数,同时传入对象字面量(功能对象),然后将该对象拷贝至构造函数的原型属性上,并通过new实例化然后返回实例化对象。代码实现如下:
//主要核心代码思想
function funs(obj){
function classFuns(){
this.name = "test";
};
classFuns.prototype = obj;
return new classFuns();
}
var funObj = {
showName: function(){
console.log(this.name);
}
};
var obj = funs(funObj);
obj.showName();
五、寄生式继承
“寄生式继承”在原型式继承上做了代理实现,通过代理函数实现功能函数,然后可以在代理函数中进行功能扩展,同时每次实例化都是新的功能函数;
function funs(obj){
function classFuns(){
this.name = "test";
};
classFuns.prototype = obj;
return new classFuns();
}
function extends(obj){
var n_obj = funs(obj);
n_obj.setName=function(value){
this.name = value;
}
return n_obj;
}
var funObj = {
showName: function(){
console.log(this.name);
}
};
var obj = extends(funObj);
obj.setName("张三");
obj.showName();
六、ES6版实现Class实现继承
通过ES6关键字中的class关键字来实现继承,目前在低版本浏览器不支持ES6语法,需要通过babel编译可支持的ES5语法;
class Parent{
constructor(){
this.name = "test";
}
setName(value){
this.name=value;
}
}
class Child extends Parent {
showName(){
console.log(this.name);
}
}
var child = new Child();
child.setName("李四");
child.showName();
七、Object.create()方法实现继承
通过Object.create()方法实现继承并返回一个新的空对象,原型链上的对象是继承的对象;
function Parent(){
this.name = "test";
}
Parent.prototype = {
showName:function(){
console.log(this.name);
}
}
var parent = new Parent();
var obj = Object.create(parent);
obj.showName();