JS实现继承的几种方式总结小记

基本概要:继承是面向对象语言中最重要的特点之一,“继承”主要为了实现代码的封装、继承、多态这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();

解决的问题

  1. 构造函数继承导致无法的继承原型对象;
  2. 原型链继承导致改变父对象属性影响所有的子对象;(针对“引用类型”的属性值)

产生的问题

  1. 父构造函数实例化了两次;
  2. 造成内存额外占用;

四、原型式继承

“原型式继承”是通过封装个功能函数,在该函数中实现新的构造函数,同时传入对象字面量(功能对象),然后将该对象拷贝至构造函数的原型属性上,并通过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();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值